Skip to content

Commit 76dc571

Browse files
committed
🐛 resolve merge conflict in CLAUDE.md
2 parents 53cfbc6 + 237334b commit 76dc571

8 files changed

Lines changed: 1741 additions & 1252 deletions

File tree

CLAUDE.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ make test_nondeterministic # Run nondeterministic tests only
2424
make fmt # Run ruff formatter + JSON formatting
2525
make ruff # Run ruff linter
2626
make vulture # Find dead code
27-
make ty # Run typer type checker
27+
make ty # Run type checker
2828
make lint_links # Check for broken links in markdown files (README, etc.)
29-
make ci # Run all CI checks (ruff, vulture, ty, lint_links)
29+
make ci # Run all CI checks (ruff, vulture, ty, import_lint, docs_lint, check_deps, lint_links)
3030

3131
# Dependencies
3232
uv sync # Install dependencies (not pip install)
@@ -47,11 +47,10 @@ uv run pytest path/to/test.py # Run specific test
4747
- `<name>.yaml` - Optional split configs (loaded as root key `<name>`)
4848
- `global_config.py` - Config class (access via `from common import global_config`)
4949
- `.env` - Secrets/API keys (git-ignored)
50-
- **src/** - Source code (api/, db/, utils/, stripe/)
50+
- **src/** - Source code (utils/)
5151
- **utils/llm/** - LLM inference with DSPY (`dspy_inference.py`) and LangFuse observability
5252
- **tests/** - pytest tests inheriting from `TestTemplate` in `test_template.py`
5353
- **init/** - Initialization scripts (banner generation)
54-
- **alembic/** - Database migrations
5554

5655
## Code Style
5756

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ Opinionated Python stack for fast development. The `saas` branch extends `main`
5858
- `make all` - runs `main.py`
5959
- `make fmt` - runs `ruff format` + JSON formatting
6060
- `make banner` - create a new banner that makes the README nice 😊
61-
- `make test` - runs all tests defined by `TEST_TARGETS = tests/folder1 tests/folder2`
61+
- `make test` - runs all tests in `tests/`
62+
- `make ci` - runs all CI checks (ruff, vulture, ty, etc.)
6263

6364

6465

pyproject.toml

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,32 +6,32 @@ authors = [
66
{ name = "Miyamura80", email = "eitomiyamura@gmail.com" }
77
]
88
dependencies = [
9-
"pyyaml>=6.0.2",
10-
"python-dotenv>=1.0.1",
9+
"pyyaml>=6.0.3",
10+
"python-dotenv>=1.2.1",
1111
"human-id>=0.2.0",
12-
"import-linter>=2.0.0",
13-
"pytest>=8.3.3",
12+
"import-linter>=2.1.1",
13+
"pytest>=8.3.4",
1414
"pytest-xdist>=3.6.1",
15-
"termcolor>=2.4.0",
15+
"termcolor>=2.5.0",
1616
"loguru>=0.7.3",
1717
"vulture>=2.14",
18-
"dspy>=2.6.24",
19-
"langfuse>=2.60.5",
20-
"litellm>=1.70.0",
21-
"tenacity>=9.1.2",
22-
"pillow>=11.2.1",
23-
"google-genai>=1.15.0",
24-
"ty>=0.0.1a9",
25-
"pytest-env>=1.1.5",
26-
"pydantic-settings>=2.12.0",
18+
"dspy>=3.1.2",
19+
"langfuse>=3.12.1",
20+
"litellm>=1.59.8",
21+
"tenacity>=9.0.0",
22+
"pillow>=12.1.0",
23+
"google-genai>=1.60.0",
24+
"ty>=0.0.14",
25+
"pytest-env>=1.2.0",
26+
"pydantic-settings>=2.7.1",
2727
"pytest-cov>=7.0.0",
28-
"pytest-repeat>=0.9.4",
29-
"pylint>=3.3.0",
30-
"deptry>=0.24.0",
31-
"openfeature-sdk>=0.8.4",
28+
"pytest-repeat>=0.9.3",
29+
"pylint>=3.3.3",
30+
"deptry>=0.23.0",
31+
"openfeature-sdk>=0.7.1",
3232
"scrubadub>=2.0.1",
33-
"pydantic>=2.0.0",
34-
"numpy>=1.26.0",
33+
"pydantic>=2.10.6",
34+
"numpy>=2.2.2",
3535
]
3636
readme = "README.md"
3737
requires-python = ">= 3.12"
@@ -44,7 +44,7 @@ build-backend = "hatchling.build"
4444
allow-direct-references = true
4545

4646
[tool.hatch.build.targets.wheel]
47-
packages = ["src/python_template"]
47+
packages = ["src", "common", "utils"]
4848

4949
[tool.ruff]
5050
line-length = 88

tests/healthcheck/test_env_var_loading.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def test_env_var_loading_precedence(monkeypatch):
3737

3838
# 3. Reload the common module to pick up the new .env file
3939
importlib.reload(common_module)
40-
reloaded_config = common_module.global_config # type: ignore
40+
reloaded_config = common_module.global_config
4141

4242
# 4. Assert that the variables are loaded with the correct precedence
4343
assert reloaded_config.DEV_ENV == "dotenv", "Should load from .env first"

tests/healthcheck/test_pydantic_type_coercion.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def test_pydantic_type_coercion(monkeypatch):
3636

3737
# Reload the config module to pick up the new environment variables
3838
importlib.reload(common_module)
39-
config = common_module.global_config # type: ignore[attr-defined]
39+
config = common_module.global_config
4040

4141
# Verify integer coercion
4242
assert isinstance(config.default_llm.default_max_tokens, int), (

tests/test_logging_security.py

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,29 @@ def test_email_redaction(self):
1414
"""Test that email addresses are redacted from log messages."""
1515
record = {"message": "User email is test@example.com", "exception": None}
1616
scrub_sensitive_data(record)
17-
assert "test@example.com" not in record["message"]
18-
assert "{{EMAIL}}" in record["message"]
17+
message = record["message"]
18+
assert isinstance(message, str)
19+
assert "test@example.com" not in message
20+
assert "{{EMAIL}}" in message
1921

2022
def test_phone_redaction(self):
2123
"""Test that phone numbers are redacted (new capability via scrubadub)."""
2224
record = {"message": "Call me at 1-800-555-0199", "exception": None}
2325
scrub_sensitive_data(record)
24-
assert "1-800-555-0199" not in record["message"]
25-
assert "{{PHONE}}" in record["message"]
26+
message = record["message"]
27+
assert isinstance(message, str)
28+
assert "1-800-555-0199" not in message
29+
assert "{{PHONE}}" in message
2630

2731
def test_api_key_redaction(self):
2832
"""Test that OpenAI API keys are redacted from log messages."""
2933
api_key = "sk-abc123def456ghi789jkl012mno345pqr678stu901"
3034
record = {"message": f"Using key: {api_key}", "exception": None}
3135
scrub_sensitive_data(record)
32-
assert api_key not in record["message"]
33-
assert "[REDACTED_API_KEY]" in record["message"]
36+
message = record["message"]
37+
assert isinstance(message, str)
38+
assert api_key not in message
39+
assert "[REDACTED_API_KEY]" in message
3440

3541
def test_multiple_redactions(self):
3642
"""Test redacting multiple sensitive items in a single message."""
@@ -39,10 +45,12 @@ def test_multiple_redactions(self):
3945
"exception": None,
4046
}
4147
scrub_sensitive_data(record)
42-
assert "{{EMAIL}}" in record["message"]
43-
assert "[REDACTED_API_KEY]" in record["message"]
44-
assert "test@example.com" not in record["message"]
45-
assert "sk-123456789012345678901234" not in record["message"]
48+
message = record["message"]
49+
assert isinstance(message, str)
50+
assert "{{EMAIL}}" in message
51+
assert "[REDACTED_API_KEY]" in message
52+
assert "test@example.com" not in message
53+
assert "sk-123456789012345678901234" not in message
4654

4755
def test_exception_message_redaction(self):
4856
"""Test that PII is redacted from exception messages."""
@@ -87,8 +95,10 @@ def test_anthropic_api_key_redaction(self):
8795
api_key = "sk-ant-api03-abc123def456ghi789jkl012mno345pqr678"
8896
record = {"message": f"Using Anthropic key: {api_key}", "exception": None}
8997
scrub_sensitive_data(record)
90-
assert api_key not in record["message"]
91-
assert "[REDACTED_API_KEY]" in record["message"]
98+
message = record["message"]
99+
assert isinstance(message, str)
100+
assert api_key not in message
101+
assert "[REDACTED_API_KEY]" in message
92102

93103
def test_stripe_api_key_redaction(self):
94104
"""Test that Stripe API keys are redacted."""
@@ -100,16 +110,20 @@ def test_stripe_api_key_redaction(self):
100110
key = prefix + suffix
101111
record = {"message": f"Stripe key: {key}", "exception": None}
102112
scrub_sensitive_data(record)
103-
assert key not in record["message"]
104-
assert "[REDACTED_API_KEY]" in record["message"]
113+
message = record["message"]
114+
assert isinstance(message, str)
115+
assert key not in message
116+
assert "[REDACTED_API_KEY]" in message
105117

106118
def test_bearer_token_redaction(self):
107119
"""Test that Authorization Bearer tokens are redacted."""
108120
token = "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkw"
109121
record = {"message": f"Authorization: {token}", "exception": None}
110122
scrub_sensitive_data(record)
111-
assert "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" not in record["message"]
112-
assert "[REDACTED_BEARER_TOKEN]" in record["message"]
123+
message = record["message"]
124+
assert isinstance(message, str)
125+
assert "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" not in message
126+
assert "[REDACTED_BEARER_TOKEN]" in message
113127

114128
def test_generic_api_key_redaction(self):
115129
"""Test that generic api_key patterns are redacted."""
@@ -123,5 +137,7 @@ def test_generic_api_key_redaction(self):
123137
for pattern in patterns:
124138
record = {"message": f"Config: {pattern}", "exception": None}
125139
scrub_sensitive_data(record)
126-
assert "abc123def456ghi789jkl012" not in record["message"]
127-
assert "[REDACTED_KEY]" in record["message"]
140+
message = record["message"]
141+
assert isinstance(message, str)
142+
assert "abc123def456ghi789jkl012" not in message
143+
assert "[REDACTED_KEY]" in message

utils/llm/dspy_langfuse.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from dspy.utils.callback import BaseCallback
77
from langfuse.client import Langfuse, StatefulGenerationClient # type: ignore
88
from langfuse.decorators import langfuse_context # type: ignore
9-
from litellm.cost_calculator import completion_cost # type: ignore
9+
from litellm.cost_calculator import completion_cost
1010
from loguru import logger as log
1111
from pydantic import BaseModel, Field, ValidationError
1212

@@ -138,7 +138,7 @@ def on_lm_start( # noqa
138138
parent_observation_id = langfuse_context.get_current_observation_id()
139139
span_obj: StatefulGenerationClient | None = None
140140
if trace_id:
141-
span_obj = self.langfuse.generation( # type: ignore (Langfuse fails the type check in this function, grr...)
141+
span_obj = self.langfuse.generation(
142142
input=user_input,
143143
name=model_name,
144144
trace_id=trace_id,
@@ -314,7 +314,7 @@ def on_lm_end( # noqa
314314
"total": total_cost,
315315
# "cache_read_input_tokens": 0.0, # Optional
316316
}
317-
span.update( # type: ignore[call-arg] # Langfuse typing for update can be tricky
317+
span.update(
318318
usage_details=usage_details_update,
319319
cost_details=cost_details_update,
320320
)
@@ -355,7 +355,7 @@ def on_lm_end( # noqa
355355
"status_message": status_message,
356356
}
357357
# Langfuse client's `end` method handles None for these specific optional parameters.
358-
span.end(**end_args) # type: ignore[call-arg] # Langfuse typing for end can be tricky
358+
span.end(**end_args)
359359
self.current_span.set(None)
360360

361361
if level == "DEFAULT" and completion_content is not None:

0 commit comments

Comments
 (0)