This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
make all- Setup venv and runmain.pymake test- Run all tests with pytestmake fmt- Format code with Black and JSON files with jqmake ci- Run all CI checks (ruff, vulture, ty)uv run python -m tests.path_to.test_module- Run a specific test in isolation
This project uses uv (not pip) for dependency management. Dependencies are in pyproject.toml.
uv sync- Sync dependenciesuv run python <file>.py- Run Python filesuv pip install <package>- Add new dependencies
The project uses pydantic-settings for type-safe configuration with YAML + environment variables:
common/global_config.yaml- Base configuration (hyperparameters, LLM settings, logging)common/config_models.py- Pydantic models for config validationcommon/global_config.py- Config class loading from YAML and.env.env- Secrets (API keys) - git-ignored.global_config.yaml- Local overrides (git-ignored, highest priority)
Access config:
from common import global_config
print(global_config.example_parent.example_child) # YAML values
print(global_config.OPENAI_API_KEY) # Environment variablesAdd new hyperparameters to common/global_config.yaml. For nested configs, define the Pydantic model in config_models.py first.
Use the DSPYInference wrapper in utils/llm/dspy_inference.py for LLM calls with built-in LangFuse observability:
from utils.llm.dspy_inference import DSPYInference
import dspy
class MySignature(dspy.Signature):
text: str = dspy.InputField()
result: str = dspy.OutputField()
module = DSPYInference(pred_signature=MySignature)
result = await module.run(text="input")Pass tools=[...] for agentic tool-use with dspy.ReAct.
Use centralized logging from src/utils/logging_config.py:
from loguru import logger as log
from src.utils.logging_config import setup_logging
setup_logging()
log.info("message")Log levels are configured in common/global_config.yaml under logging.levels.
Tests use pytest with a custom TestTemplate base class:
from tests.test_template import TestTemplate, slow_test, nondeterministic_test
class TestFeature(TestTemplate):
@pytest.fixture(autouse=True)
def setup_shared_variables(self, setup):
pass
def test_something(self):
assert True- Inherit from
TestTemplatefor proper setup - Use
@slow_testor@nondeterministic_testdecorators as appropriate - Add
__init__.pyto new test directories
- Snake case for functions, files, directories
- CamelCase for classes
- UPPERCASE for constants
- Double quotes for strings
- 4 spaces for indentation
- Use native
list,tupletype hints (Python 3.12+), nottyping.List
- Whenever a new notebook is created or significantly updated, the root
README.mdshould be updated to reflect the changes or additions.
Use @observe decorator for LLM tracing:
from langfuse.decorators import observe, langfuse_context
@observe
def my_function():
langfuse_context.update_current_observation(name="custom_name")For long-running operations, structure code as:
init()- Initializecontinue(task_id)- Resume from checkpointcleanup(task_id)- Cleanup
Always checkpoint with IDs. Keep data serializable. Handle rate limits, timeouts, and retries at system boundaries.