Each element type is taken from how the container is used rather than
guessed: kept_items is returned from trim_to_fit, whose signature is
already list[Any]; traces collects the dicts built at the append site;
expert_results and tool_outputs are keyed by tool_name (str) and hold
ToolReturnPart.content.
tracing_router.py needed `from typing import Any` added — it had no
import for it, so annotating without that would have traded a
var-annotated error for a name-defined one. Function-body variable
annotations are not evaluated at runtime, so this would not have raised;
mypy was the only thing that would have caught it.
90 errors -> 86.
Co-Authored-By: Claude <noreply@anthropic.com>
mypy's warn_unused_ignores is on, so these were reported as errors in
their own right — a suppression that no longer suppresses is a claim
that something is broken when it is not, and it silently widens to
cover a real error if one later appears on that line.
Two carried a "Forward reference" note that is still accurate; the note
is kept and only the ignore removed.
95 errors -> 90. Comment-only, so no runtime behaviour can have changed
and the suite was not re-run for this commit. The `# type: ignore` count
across the tree drops from 6 to 1, which is the anchor for the rest of
this work: clearing a type error by suppressing it would push that
number the other way.
Co-Authored-By: Claude <noreply@anthropic.com>
The 21 the automatic pass could not make on its own. `ruff check` and
`ruff format --check` are both clean now; typecheck is still red and is next.
`in_reasoning` in chat/service.py was a complete state machine that nothing read:
initialised False, set True when a reasoning delta arrived, set False when the
summary ended — three assignments, zero reads. Ruff reported one at a time, and
removing each revealed the next, so what looked like a single stray variable took
three passes to bottom out. The branches themselves do real work and are
untouched; only the flag is gone.
Four `raise HTTPException` inside `except` blocks now chain with `from e`. Until
now a failure while handling an error was indistinguishable from the error, which
matters most in exactly the situation where the traceback is all you have.
In biographer/tools.py the binding was unused but the call is not: MemoryType()
is called for the ValueError it raises on an invalid name. The binding is gone
and the call and its comment stay, because dropping the line would have removed
the validation.
The rest are unused bindings in tests where the assertions are on something else
(call_args, mostly), plus three unused loop variables and an isinstance tuple.
One correction to my own work: removing a dead comprehension in
test_error_handling.py left an `if` block with nothing but comments in it, which
is a SyntaxError. Ruff caught it immediately. The block now says what the test
actually pins — that the stream parses without crashing, which reaching that line
demonstrates — rather than computing a list nobody asserts on.
`make test` is intermittent here, and it is not this change.
test_tatlock_tool_call_logging_calculator failed in two of five full runs across
both HEAD and this branch, and passes in the other three; it also fails in
isolation at HEAD while passing in isolation here. Order- or timing-dependent.
Recorded rather than chased, since tests are not gated in this repo yet.
Co-Authored-By: Claude <noreply@anthropic.com>
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>
Phase A review minors:
- Coverage note: source_status (when present) is now used exclusively;
the source_counts-absence fallback only considers the optional legs
the request explicitly enabled (web/documents/volatile). library-desk
computes source_counts from the final top-N fused results only, so
absence of the always-on vector/graph legs is normal ranking behavior
- the old heuristic warned on virtually every healthy search
- update_wiki_page: the empty-list tags sentinel (leave unchanged) made
clearing all tags impossible; pass exactly ["__CLEAR__"] to send an
empty tag list, documented in the docstring for the local model
- Text-delegation parallel fallback: zip(..., strict=True) with an
explicit count-mismatch guard so results can never be silently
attributed to the wrong agent
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QbFZyDvYksazX6nYQYZ67L
- delegate_to_* now receives a trimmed conversation history (last ~6
turns, 500 chars/turn) as context on both live direct-delegation
paths (streaming and steward non-streaming), via new
build_delegation_context helper
- _stream_direct_delegation restructured as an async generator: the
butler 'start' think message streams BEFORE the expert runs and the
success/error message right after it finishes, instead of all
messages arriving after the research completed
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- run_librarian / run_librarian_stream raise AgentError instead of
returning/yielding error text as normal output; detail stays in logs
- delegate_to_* wrappers now put a curated butler-toned sentence in
DelegationResult.output on failure and never expose str(e), so
streaming's error branch is reachable and honest
- _execute_single_delegation propagates success; direct delegation only
records delegate_to_* as called when the expert actually succeeded
- librarian tools return user-safe messages instead of
'Error searching: {e}' strings that leaked internal URLs into
synthesis; coordination stream errors are curated as well
- ruff cleanups (TYPE_CHECKING forward refs, B904, unused locals) in
the touched files to keep them lint-clean
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Remove references to unimplemented get_benchmark_store from steward and
tool tracking tests
- Fix steward test fixture calling async initialize_application synchronously
by using sync register_household_members instead
- Rewrite tool tracking tests to assert actual logging behavior
- Change unit test fixture model from Tatlock to lorem-tester so unit tests
don't require external services
- Add session-scoped _initialize_app fixture to run Claude health check,
ensuring integration tests use Claude instead of falling back to Ollama
- Increase integration test timeouts from 30s to 120s to match OLLAMA_TIMEOUT
- Add Steward reasoning as ReasoningOutputItem in create_response_with_steward
so <think> tags appear in chat completion responses
- Add test_tatlock_ollama_fallback to verify Ollama fallback path works
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Steward's internal routing analysis (DELEGATE, COMPLEXITY, etc.)
was being exposed in <think> blocks. This is implementation detail,
not useful reasoning for the user.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Instrument the full request flow with trace spans for debugging:
- Wrap expert delegations (librarian/biographer/housekeeper) in spans
- Add orchestrate and synthesize spans to TatlockAgent
- Trace Steward analysis in preprocessing
- Start/end traces in response service with context management
- Simplify router by moving context handling to service layer
- Include tracing router in debug mode
- Remove benchmark recording from tool_tracking and steward service
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix `invalid message content type: <nil>` error from Ollama
- Create TatlockOllamaProvider that sanitizes messages (null → "")
- Update all agents to use sanitized provider
- Fix repeating think messages by adding ReasoningSummaryDone signal
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fixes several issues with the web search migration to Librarian:
- Update Steward routing guidelines for web search/weather → Librarian
- Register search_web, read_url, read_urls_batch tools with Librarian agent
- Update Librarian system prompt with web search documentation
- Fix query enrichment not being passed to delegations (location context)
- Add URL reading keywords to RESEARCH action type detection
Weather queries now automatically include user's stored location from
the Biographer, enabling location-aware search results.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Two-Phase Tatlock Execution:
- orchestrate_tool_calls() for Phase 1 coordination
- synthesize_from_results() for Phase 2 butler-toned synthesis
- Guarantees butler personality in all responses
Automatic Think Slugs:
- Deterministic butler-perspective messages during expert delegation
- ActionType enum: RETRIEVE, RESEARCH, CREATE, CONTROL, RECORD
- HOUSEHOLD_THINK_MESSAGES mapping for all experts
- Streaming delegation wrappers with automatic think messages
Steward Query Enrichment:
- Auto-fill user context (location, timezone) when not specified
- _build_enriched_query() with regex word boundary matching
- enriched_query field in StewardRecommendation schema
Documentation:
- ORCHESTRATION_SCENARIOS.md rewritten with Mermaid diagrams
- New Housekeeper and Biographer scenarios
- TESTING_IMPROVEMENTS.md for future LLM testing patterns
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
### Added
- Environment-aware configuration:
- Auto-selected logging (DEBUG for dev, WARNING for prod)
- Auto-selected default user (llm_tester for dev isolation)
- User context logging at request entry
- Direct delegation bypass:
- Pure memory/librarian requests skip Tatlock LLM
- Reduces latency for memory-only requests
- Text-based delegation fallback:
- Parse [DELEGATE:agent] patterns from LLM output
- Sequential and parallel execution support
- Comprehensive E2E test suite:
- 22 orchestration tests with QdrantVerifier
- assert_llm_behavior() for flexible pattern matching
- Tests for memory, delegation, isolation, scenarios
### Fixed
- Unit test mocks for streaming (async generator)
- Temporal context handling in tests
- LLM non-determinism with pytest.xfail()
- Streaming test timeouts increased
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fix streaming issues that caused text repetition and broken tool execution
in Open WebUI. Implements real LLM streaming using PydanticAI's run_stream()
with delta=True instead of artificial word-by-word chunking.
**Fixed:**
- Text repetition in streaming output (was accumulating instead of deltas)
- Broken tool execution (tools now execute properly in streaming mode)
- Invalid 'thinking' parameter in ReasoningOutputItem schema
**Changes:**
- Add run_with_scoped_tools_stream() method to TatlockAgent
- Uses PydanticAI's run_stream() with delta=True for real deltas
- Properly streams LLM output with tool execution
- Update StreamingCoordinator.stream_response_with_steward()
- Uses new streaming method instead of fake word-by-word streaming
- Removes invalid thinking parameter from ReasoningOutputItem
- All streaming now uses actual LLM deltas, not accumulated text
Resolves streaming issues reported in Open WebUI where responses showed
repetitive text and tool calls appeared as raw JSON instead of executed results.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implements Phases 2, 3, and 6: Complete Responses API implementation
Core API (Phase 2):
- OpenAI Responses API format with structured output items
- Streaming and non-streaming support via SSE-Starlette
- Reasoning items (thinking summaries)
- Function call items (tool execution)
- Message items (assistant responses)
- Router, schemas, service, and streaming coordinator
Conversation History (Phase 3):
- Hybrid client/server approach
- Auto-generated deterministic conversation IDs
- Configurable max turns with automatic trimming
- Context window management with token counting
- Token usage statistics
- Placeholder for future vector memory integration
Advanced Features (Phase 6):
- Parameter validation with Pydantic field validators:
- Temperature: 0.0-2.0 range enforcement
- Reasoning effort: 6 levels (none to xhigh)
- Max output tokens: positive integer enforcement
- Stop sequences: up to 4, non-empty strings
- Real-time stop sequence detection during streaming
- Real-time max tokens enforcement with token counting
- Graceful error handling and OpenAI-compatible error format
Testing:
- 9 unit tests for API endpoints and streaming
- 11 unit tests for error handling
- 13 unit tests for conversation history and context
- 12 unit tests for advanced features and validation
- Total: 45 tests with comprehensive coverage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>