style: apply ruff's automatic fixes and formatter

Mechanical only, and separated from the judgment calls that follow so the
reviewable changes are not buried in a 98-file whitespace diff.

227 automatic fixes: 60 blank lines carrying whitespace, 60 unsorted import
blocks, 34 Optional[X] to X | None, 28 unused imports, 16 deprecated typing
imports, 12 datetime.timezone.utc to datetime.UTC, and assorted smaller
modernisations. Then `ruff format` over src and tests: 98 files reformatted,
35 already conforming.

No file among the unused-import findings defines __all__ or is an __init__.py,
so nothing here removes a re-export.

`make test`: 658 passed, unchanged from HEAD.

Two things observed while verifying, neither addressed here:

`pytest tests/` cannot collect — tests/e2e/test_orchestration_e2e.py uses an
`e2e` marker that is not registered, and the config is strict about markers.
This fails identically at HEAD, so it predates this change; `make test` passes
because it ignores tests/e2e, tests/integration and tests/contracts.

test_tatlock_tool_call_logging_calculator is flaky. It failed once in a full run
with these changes and passed on the next, passes in isolation with them, and
fails in isolation at HEAD. It is order- or timing-dependent, not a regression
from this commit — established by running the full suite both ways rather than
by reasoning about which change could have caused it.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-08-11 17:25:18 +02:00
co-authored by Claude
parent 57fa6c13fc
commit 78066fab1b
103 changed files with 1601 additions and 1749 deletions
+66 -86
View File
@@ -7,10 +7,12 @@ These tests hit the actual running server and test the full stack:
- Tool execution
- Response formatting
"""
from collections.abc import AsyncGenerator
import httpx
import pytest
import pytest_asyncio
import httpx
from typing import AsyncGenerator
# Test server base URL (assumes server is running on localhost:8777 via ./wakeup.sh)
BASE_URL = "http://localhost:8777"
@@ -34,10 +36,8 @@ class TestChatCompletionsE2E:
"/v1/chat/completions",
json={
"model": "Tatlock",
"messages": [
{"role": "user", "content": "What is 144 divided by 12?"}
],
}
"messages": [{"role": "user", "content": "What is 144 divided by 12?"}],
},
)
assert response.status_code == 200
@@ -68,7 +68,9 @@ class TestChatCompletionsE2E:
assert "usage" in data
assert data["usage"]["total_tokens"] > 0
print(f"✓ Calculator test passed. Found '12' in response. Tool indicator: {has_calculator_indicator}")
print(
f"✓ Calculator test passed. Found '12' in response. Tool indicator: {has_calculator_indicator}"
)
@pytest.mark.asyncio
async def test_web_search(self, client: httpx.AsyncClient):
@@ -80,7 +82,7 @@ class TestChatCompletionsE2E:
"messages": [
{"role": "user", "content": "Search for the current population of Tokyo"}
],
}
},
)
assert response.status_code == 200
@@ -114,10 +116,8 @@ class TestChatCompletionsE2E:
"/v1/chat/completions",
json={
"model": "Tatlock",
"messages": [
{"role": "user", "content": "What is 15 times 4?"}
],
}
"messages": [{"role": "user", "content": "What is 15 times 4?"}],
},
)
assert response1.status_code == 200
@@ -135,9 +135,9 @@ class TestChatCompletionsE2E:
"messages": [
{"role": "user", "content": "What is 15 times 4?"},
{"role": "assistant", "content": message1},
{"role": "user", "content": "Now add 20 to that result."}
{"role": "user", "content": "Now add 20 to that result."},
],
}
},
)
assert response2.status_code == 200
@@ -152,7 +152,9 @@ class TestChatCompletionsE2E:
has_calculation = "60" in message2 and "20" in message2
assert has_answer or has_calculation, f"Expected '80' or calculation in: {message2}"
print(f"✓ Multi-turn test passed. Answer found: {has_answer}, Calculation shown: {has_calculation}")
print(
f"✓ Multi-turn test passed. Answer found: {has_answer}, Calculation shown: {has_calculation}"
)
@pytest.mark.asyncio
async def test_calculation_and_search(self, client: httpx.AsyncClient):
@@ -161,13 +163,8 @@ class TestChatCompletionsE2E:
"/v1/chat/completions",
json={
"model": "Tatlock",
"messages": [
{
"role": "user",
"content": "Calculate the square root of 256"
}
],
}
"messages": [{"role": "user", "content": "Calculate the square root of 256"}],
},
)
assert response.status_code == 200
@@ -194,10 +191,8 @@ class TestChatCompletionsE2E:
"/v1/chat/completions",
json={
"model": "Tatlock",
"messages": [
{"role": "user", "content": "Hello, how are you?"}
],
}
"messages": [{"role": "user", "content": "Hello, how are you?"}],
},
)
assert response.status_code == 200
@@ -223,10 +218,8 @@ class TestChatCompletionsE2E:
"/v1/chat/completions",
json={
"model": "Tatlock",
"messages": [
{"role": "user", "content": "What is today's date?"}
],
}
"messages": [{"role": "user", "content": "What is today's date?"}],
},
)
assert response.status_code == 200
@@ -242,11 +235,16 @@ class TestChatCompletionsE2E:
# Should contain some date/time information (flexible - varies in format)
import re
has_date = (
re.search(r'\d{4}', message) or # Year
re.search(r'\d{1,2}', message) or # Day/month number
re.search(r'(January|February|March|April|May|June|July|August|September|October|November|December)', message, re.IGNORECASE) or
"today" in message.lower()
re.search(r"\d{4}", message) # Year
or re.search(r"\d{1,2}", message) # Day/month number
or re.search(
r"(January|February|March|April|May|June|July|August|September|October|November|December)",
message,
re.IGNORECASE,
)
or "today" in message.lower()
)
assert has_date, f"Expected date/time information in: {message}"
@@ -263,11 +261,9 @@ class TestResponsesAPIE2E:
"/v1/responses",
json={
"model": "Tatlock",
"input": [
{"role": "user", "content": "Calculate 25 times 16"}
],
"reasoning": {"effort": "medium", "summary": "auto"}
}
"input": [{"role": "user", "content": "Calculate 25 times 16"}],
"reasoning": {"effort": "medium", "summary": "auto"},
},
)
assert response.status_code == 200
@@ -300,7 +296,7 @@ class TestResponsesAPIE2E:
assert "usage" in data
assert data["usage"]["total_tokens"] > 0
print(f"✓ Responses API test passed. Found '400' with Steward reasoning.")
print("✓ Responses API test passed. Found '400' with Steward reasoning.")
@pytest.mark.asyncio
async def test_response_multi_turn(self, client: httpx.AsyncClient):
@@ -312,10 +308,10 @@ class TestResponsesAPIE2E:
"input": [
{"role": "user", "content": "What is 7 times 8?"},
{"role": "assistant", "content": "Certainly, sir. 7 times 8 equals 56."},
{"role": "user", "content": "Double that number."}
{"role": "user", "content": "Double that number."},
],
"reasoning": {"effort": "medium", "summary": "auto"}
}
"reasoning": {"effort": "medium", "summary": "auto"},
},
)
assert response.status_code == 200
@@ -346,11 +342,9 @@ class TestStreamingE2E:
"/v1/chat/completions",
json={
"model": "Tatlock",
"messages": [
{"role": "user", "content": "What is 9 times 7?"}
],
"stream": True
}
"messages": [{"role": "user", "content": "What is 9 times 7?"}],
"stream": True,
},
) as response:
assert response.status_code == 200
@@ -362,6 +356,7 @@ class TestStreamingE2E:
break
import json
chunk = json.loads(data_str)
chunks.append(chunk)
@@ -373,8 +368,7 @@ class TestStreamingE2E:
# Should have received Steward's reasoning (in <think> tags)
full_content = "".join(
chunk["choices"][0]["delta"].get("content", "") or ""
for chunk in chunks
chunk["choices"][0]["delta"].get("content", "") or "" for chunk in chunks
)
assert "<think>" in full_content
assert "</think>" in full_content
@@ -393,10 +387,8 @@ class TestErrorHandling:
"/v1/chat/completions",
json={
"model": "nonexistent-model",
"messages": [
{"role": "user", "content": "Hello"}
],
}
"messages": [{"role": "user", "content": "Hello"}],
},
)
assert response.status_code == 404
@@ -411,7 +403,7 @@ class TestErrorHandling:
json={
"model": "Tatlock",
# Missing "messages" field
}
},
)
assert response.status_code == 422
@@ -425,11 +417,9 @@ class TestErrorHandling:
"/v1/chat/completions",
json={
"model": "Tatlock",
"messages": [
{"role": "user", "content": "Hello"}
],
"temperature": 5.0 # Max is 2.0
}
"messages": [{"role": "user", "content": "Hello"}],
"temperature": 5.0, # Max is 2.0
},
)
assert response.status_code == 422
@@ -447,11 +437,9 @@ class TestChatResponsesWrapper:
"/v1/responses",
json={
"model": "Tatlock",
"input": [
{"role": "user", "content": "Calculate 13 times 9"}
],
"reasoning": {"effort": "medium", "summary": "auto"}
}
"input": [{"role": "user", "content": "Calculate 13 times 9"}],
"reasoning": {"effort": "medium", "summary": "auto"},
},
)
assert response.status_code == 200
@@ -500,10 +488,8 @@ class TestChatResponsesWrapper:
"/v1/chat/completions",
json={
"model": "Tatlock",
"messages": [
{"role": "user", "content": "What is 5 plus 3?"}
],
}
"messages": [{"role": "user", "content": "What is 5 plus 3?"}],
},
)
assert response.status_code == 200
@@ -539,11 +525,9 @@ class TestChatResponsesWrapper:
"/v1/chat/completions",
json={
"model": "Tatlock",
"messages": [
{"role": "user", "content": "Count to 3"}
],
"stream": True
}
"messages": [{"role": "user", "content": "Count to 3"}],
"stream": True,
},
) as response:
assert response.status_code == 200
@@ -555,6 +539,7 @@ class TestChatResponsesWrapper:
break
import json
chunk = json.loads(data_str)
chunks.append(chunk)
@@ -574,10 +559,7 @@ class TestChatResponsesWrapper:
assert chunks[0]["choices"][0]["delta"]["role"] == "assistant"
# Should have content chunks
has_content = any(
"content" in chunk["choices"][0]["delta"]
for chunk in chunks
)
has_content = any("content" in chunk["choices"][0]["delta"] for chunk in chunks)
assert has_content
print(f"✓ Streaming format matches OpenAI spec ({len(chunks)} chunks)")
@@ -593,11 +575,9 @@ class TestStewardIntegration:
"/v1/responses",
json={
"model": "Tatlock",
"input": [
{"role": "user", "content": "Calculate 123 times 456"}
],
"reasoning": {"effort": "medium", "summary": "auto"}
}
"input": [{"role": "user", "content": "Calculate 123 times 456"}],
"reasoning": {"effort": "medium", "summary": "auto"},
},
)
assert response.status_code == 200
@@ -620,10 +600,10 @@ class TestStewardIntegration:
"input": [
{"role": "user", "content": "My favorite number is 42"},
{"role": "assistant", "content": "Noted, sir. 42 is an excellent choice."},
{"role": "user", "content": "What was that number again?"}
{"role": "user", "content": "What was that number again?"},
],
"reasoning": {"effort": "medium", "summary": "auto"}
}
"reasoning": {"effort": "medium", "summary": "auto"},
},
)
assert response.status_code == 200
+88 -68
View File
@@ -18,13 +18,14 @@ Requirements:
Note: LLM outputs are non-deterministic. Tests use flexible assertions
that check for behavioral patterns rather than exact text matches.
"""
import pytest
import httpx
import asyncio
import re
from typing import AsyncGenerator
from collections.abc import AsyncGenerator
from dataclasses import dataclass
import httpx
import pytest
from src.core.config import PRODUCTION_TENANT, TEST_TENANT
@@ -43,6 +44,7 @@ assert TEST_USER != PRODUCTION_TENANT
@dataclass
class LLMAssertionResult:
"""Result of an LLM output assertion check."""
passed: bool
evidence: str
confidence: str # "high", "medium", "low"
@@ -183,6 +185,7 @@ def assert_llm_behavior(
# Fixtures
# ============================================================================
@pytest.fixture
async def client() -> AsyncGenerator[httpx.AsyncClient, None]:
"""HTTP client for API requests."""
@@ -223,6 +226,7 @@ async def clean_test_memories(qdrant: QdrantVerifier):
# Memory System Tests
# ============================================================================
@pytest.mark.e2e
@pytest.mark.asyncio
class TestMemoryStorage:
@@ -245,9 +249,7 @@ class TestMemoryStorage:
"/v1/responses",
json={
"model": "Tatlock",
"input": [
{"role": "user", "content": "Remember that my test color is purple"}
],
"input": [{"role": "user", "content": "Remember that my test color is purple"}],
},
)
@@ -281,7 +283,8 @@ class TestMemoryStorage:
# Verify data in Qdrant - look for any color-related or test-related memory
points = await qdrant.scroll_points(TEST_COLLECTION)
relevant_memories = [
p for p in points
p
for p in points
if "color" in p.get("payload", {}).get("key", "").lower()
or "purple" in str(p.get("payload", {}).get("value", "")).lower()
or "test" in p.get("payload", {}).get("key", "").lower()
@@ -341,9 +344,7 @@ class TestMemoryStorage:
"/v1/responses",
json={
"model": "Tatlock",
"input": [
{"role": "user", "content": "What is my favorite food?"}
],
"input": [{"role": "user", "content": "What is my favorite food?"}],
},
)
@@ -431,6 +432,7 @@ class TestMemoryRecall:
# Steward Delegation Tests
# ============================================================================
@pytest.mark.e2e
@pytest.mark.asyncio
class TestStewardDelegation:
@@ -445,9 +447,7 @@ class TestStewardDelegation:
"/v1/responses",
json={
"model": "Tatlock",
"input": [
{"role": "user", "content": "What do you know about me?"}
],
"input": [{"role": "user", "content": "What do you know about me?"}],
"reasoning": {"effort": "medium", "summary": "auto"},
},
)
@@ -479,9 +479,7 @@ class TestStewardDelegation:
"/v1/responses",
json={
"model": "Tatlock",
"input": [
{"role": "user", "content": "Calculate 127 times 83"}
],
"input": [{"role": "user", "content": "Calculate 127 times 83"}],
"reasoning": {"effort": "medium", "summary": "auto"},
},
)
@@ -511,15 +509,16 @@ class TestStewardDelegation:
# Remove commas for number comparison
message_normalized = message_text.replace(",", "")
assert "10541" in message_normalized, (
f"Expected calculation result 10541. Got: {message_text[:200]}"
)
assert (
"10541" in message_normalized
), f"Expected calculation result 10541. Got: {message_text[:200]}"
# ============================================================================
# Direct Delegation Tests
# ============================================================================
@pytest.mark.e2e
@pytest.mark.asyncio
class TestDirectDelegation:
@@ -569,6 +568,7 @@ class TestDirectDelegation:
# Data Verification Tests
# ============================================================================
@pytest.mark.e2e
@pytest.mark.asyncio
class TestDataVerification:
@@ -610,8 +610,7 @@ class TestDataVerification:
payload = point.get("payload", {})
for field in required_fields:
assert field in payload, (
f"Point {point['id']} missing required field '{field}'. "
f"Payload: {payload}"
f"Point {point['id']} missing required field '{field}'. " f"Payload: {payload}"
)
async def test_qdrant_point_types_are_valid(
@@ -643,6 +642,7 @@ class TestDataVerification:
# Integration Health Tests
# ============================================================================
@pytest.mark.e2e
@pytest.mark.asyncio
class TestIntegrationHealth:
@@ -678,6 +678,7 @@ class TestIntegrationHealth:
# Orchestration Scenario Tests (from ORCHESTRATION_SCENARIOS.md)
# ============================================================================
@pytest.mark.e2e
@pytest.mark.asyncio
class TestScenario1WeatherWithMemory:
@@ -697,9 +698,7 @@ class TestScenario1WeatherWithMemory:
"/v1/responses",
json={
"model": "Tatlock",
"input": [
{"role": "user", "content": "What's the weather like?"}
],
"input": [{"role": "user", "content": "What's the weather like?"}],
"reasoning": {"effort": "medium", "summary": "auto"},
},
)
@@ -717,13 +716,17 @@ class TestScenario1WeatherWithMemory:
print(f"Weather query - Steward: {reasoning_text[:200]}...")
# Should mention location/memory and search capabilities
has_memory_mention = "biographer" in reasoning_text.lower() or "memory" in reasoning_text.lower()
has_search_mention = "tatlock_core" in reasoning_text.lower() or "search" in reasoning_text.lower()
has_memory_mention = (
"biographer" in reasoning_text.lower() or "memory" in reasoning_text.lower()
)
has_search_mention = (
"tatlock_core" in reasoning_text.lower() or "search" in reasoning_text.lower()
)
# Weather query should trigger at least web search
assert has_search_mention, (
f"Weather query should recommend search capability. Got: {reasoning_text[:200]}"
)
assert (
has_search_mention
), f"Weather query should recommend search capability. Got: {reasoning_text[:200]}"
@pytest.mark.e2e
@@ -744,9 +747,7 @@ class TestScenario4SimpleExpertDelegation:
"/v1/responses",
json={
"model": "Tatlock",
"input": [
{"role": "user", "content": "What is 847 times 293?"}
],
"input": [{"role": "user", "content": "What is 847 times 293?"}],
},
)
@@ -761,9 +762,9 @@ class TestScenario4SimpleExpertDelegation:
# Expected result: 248171 (may be formatted as 248,171)
message_normalized = message_text.replace(",", "")
assert "248171" in message_normalized, (
f"Calculator should compute 847 * 293 = 248171. Got: {message_text[:200]}"
)
assert (
"248171" in message_normalized
), f"Calculator should compute 847 * 293 = 248171. Got: {message_text[:200]}"
async def test_datetime_query_uses_datetime_tool(
self,
@@ -774,9 +775,7 @@ class TestScenario4SimpleExpertDelegation:
"/v1/responses",
json={
"model": "Tatlock",
"input": [
{"role": "user", "content": "What day of the week is it?"}
],
"input": [{"role": "user", "content": "What day of the week is it?"}],
},
)
@@ -791,8 +790,20 @@ class TestScenario4SimpleExpertDelegation:
# Should mention a day of the week (full or abbreviated)
days = [
"monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday",
"mon", "tue", "wed", "thu", "fri", "sat", "sun"
"monday",
"tuesday",
"wednesday",
"thursday",
"friday",
"saturday",
"sunday",
"mon",
"tue",
"wed",
"thu",
"fri",
"sat",
"sun",
]
has_day = any(day in message_text.lower() for day in days)
@@ -838,9 +849,9 @@ class TestScenario6WikiCreation:
print(f"Wiki search - Steward: {reasoning_text[:200]}...")
# Should mention librarian
assert "librarian" in reasoning_text.lower(), (
f"Wiki search should recommend librarian. Got: {reasoning_text[:200]}"
)
assert (
"librarian" in reasoning_text.lower()
), f"Wiki search should recommend librarian. Got: {reasoning_text[:200]}"
@pytest.mark.e2e
@@ -862,7 +873,10 @@ class TestScenario8MultiExpertCoordination:
json={
"model": "Tatlock",
"input": [
{"role": "user", "content": "What do you know about me? And also search for Python tutorials."}
{
"role": "user",
"content": "What do you know about me? And also search for Python tutorials.",
}
],
"reasoning": {"effort": "medium", "summary": "auto"},
},
@@ -885,15 +899,16 @@ class TestScenario8MultiExpertCoordination:
has_librarian = "librarian" in reasoning_text.lower()
has_search = "tatlock_core" in reasoning_text.lower() or "search" in reasoning_text.lower()
assert has_biographer or has_librarian or has_search, (
f"Complex query should identify multiple capabilities. Got: {reasoning_text[:200]}"
)
assert (
has_biographer or has_librarian or has_search
), f"Complex query should identify multiple capabilities. Got: {reasoning_text[:200]}"
# ============================================================================
# New Scenarios from Today's Session
# ============================================================================
@pytest.mark.e2e
@pytest.mark.asyncio
class TestDirectDelegationBypass:
@@ -917,9 +932,7 @@ class TestDirectDelegationBypass:
"/v1/responses",
json={
"model": "Tatlock",
"input": [
{"role": "user", "content": "Remember that my test value is alpha123"}
],
"input": [{"role": "user", "content": "Remember that my test value is alpha123"}],
},
)
@@ -979,7 +992,10 @@ class TestUserContextIsolation:
json={
"model": "Tatlock",
"input": [
{"role": "user", "content": f"Remember that my isolation marker is {unique_value}"}
{
"role": "user",
"content": f"Remember that my isolation marker is {unique_value}",
}
],
},
)
@@ -1000,13 +1016,15 @@ class TestUserContextIsolation:
else:
in_prod_collection = False
print(f"Isolation test - In test collection: {in_test_collection}, In prod: {in_prod_collection}")
print(
f"Isolation test - In test collection: {in_test_collection}, In prod: {in_prod_collection}"
)
# Should be in test collection OR response acknowledged
# Should NOT be in production collection
assert not in_prod_collection, (
f"Test data leaked to production collection! Value: {unique_value}"
)
assert (
not in_prod_collection
), f"Test data leaked to production collection! Value: {unique_value}"
@pytest.mark.e2e
@@ -1025,16 +1043,16 @@ class TestErrorHandling:
"/v1/responses",
json={
"model": "nonexistent-model-xyz",
"input": [
{"role": "user", "content": "Hello"}
],
"input": [{"role": "user", "content": "Hello"}],
},
)
# Should return error status (404 or 400)
assert response.status_code in [400, 404, 422], (
f"Expected error status for invalid model. Got: {response.status_code}"
)
assert response.status_code in [
400,
404,
422,
], f"Expected error status for invalid model. Got: {response.status_code}"
data = response.json()
# Error could be in "error" or "detail" key
assert "error" in data or "detail" in data
@@ -1084,7 +1102,10 @@ class TestEvaluationReport:
json={
"model": "Tatlock",
"input": [
{"role": "user", "content": "Remember that my test pet is a hamster named Fluffy"}
{
"role": "user",
"content": "Remember that my test pet is a hamster named Fluffy",
}
],
},
)
@@ -1096,7 +1117,7 @@ class TestEvaluationReport:
break
report_lines.append("\n[STORE TEST]")
report_lines.append(f"Input: 'Remember that my test pet is a hamster named Fluffy'")
report_lines.append("Input: 'Remember that my test pet is a hamster named Fluffy'")
report_lines.append(f"Response: {store_text[:200]}...")
await asyncio.sleep(1.0)
@@ -1106,9 +1127,7 @@ class TestEvaluationReport:
"/v1/responses",
json={
"model": "Tatlock",
"input": [
{"role": "user", "content": "What pet do I have?"}
],
"input": [{"role": "user", "content": "What pet do I have?"}],
},
)
@@ -1119,7 +1138,7 @@ class TestEvaluationReport:
break
report_lines.append("\n[RECALL TEST]")
report_lines.append(f"Input: 'What pet do I have?'")
report_lines.append("Input: 'What pet do I have?'")
report_lines.append(f"Response: {recall_text[:200]}...")
# Check Qdrant state
@@ -1128,7 +1147,8 @@ class TestEvaluationReport:
report_lines.append(f"Total points in {TEST_COLLECTION}: {len(points)}")
pet_memories = [
p for p in points
p
for p in points
if "pet" in str(p.get("payload", {})).lower()
or "fluffy" in str(p.get("payload", {})).lower()
or "hamster" in str(p.get("payload", {})).lower()