Skip to content

fix: 6 infrastructure fixes (LLM retry, cache, token counting, tests) - #166

Merged
ULookup merged 12 commits into
mainfrom
infra-fixes-2026-06-20
Jun 21, 2026
Merged

fix: 6 infrastructure fixes (LLM retry, cache, token counting, tests)#166
ULookup merged 12 commits into
mainfrom
infra-fixes-2026-06-20

Conversation

@ULookup

@ULookup ULookup commented Jun 20, 2026

Copy link
Copy Markdown
Owner

Summary

Fix 6 infrastructure issues across LLM Provider, Agent Loop, Token Counter, and SubAgentRunner subsystems.

Closes #1 — LLM HTTP Retry (exponential backoff)
Closes #2 — Apply CacheAwareContext::split() Result
Closes #6 — Token Counting: Hybrid Running Count
Closes #9 — Separate System Prompt from session_history_
Closes #10 — fan_out() Parallelism Hard Cap
Closes #4 — Test Coverage

Changes

#1 — LLM HTTP Retry (exponential backoff)

  • Add RetryConfig struct to llm_provider.hpp
  • Wrap curl sections in both openai_provider.cpp and anthropic_provider.cpp with retry loops
  • Retry on 5xx/429/curl errors, never retry 4xx/auth/cancellation
  • Backoff: 1s → 2s → 4s, max 3 retries

#2 — Apply CacheAwareContext::split() Result

  • Previously split() result was only debug-logged, never applied
  • Now reorders messages so stable content (system prompt, tools) precedes volatile content (conversation), enabling prompt cache optimization

#6 — Token Counting: Hybrid Running Count

  • Add update_authoritative() to TokenCounter, tracking API usage.prompt_tokens
  • Hybrid counting: authoritative baseline + heuristic for new messages
  • Shared TokenCounter in AgentLoop, reset on history restore

#9 — Separate System Prompt from session_history_

  • Add system_prompt_ and compaction_summaries_ members to AgentLoop
  • Prevent compaction summary from being overwritten by set_system_prompt()
  • build_context() uses effective system prompt with fallback, prepends summaries

#10 — fan_out() Parallelism Hard Cap

  • Replace unlimited std::async spawning with batched processing
  • Cap: min(4, hardware_concurrency), preventing thread exhaustion

#4 — Test Coverage

  • test_openai_provider.cpp: 4 tests (build_messages, build_tools)
  • test_anthropic_provider.cpp: 5 tests (cache_control, tool_use, thinking config)
  • test_agent_loop.cpp: 14 tests (config defaults, state management, system prompt isolation)
  • test_sub_agent_runner.cpp: 5 tests (register, delegate, sequential, fan_out)
  • Total: 28 new tests, 100% pass rate

Files Changed

  • 8 source files modified
  • 4 new test files
  • 2 header files exposed for testing
  • 1 CMakeLists.txt updated

Test Plan

  • Build passes (no regressions)
  • 4 new test executables: 100% pass
  • Pre-existing non-DB tests still pass (prompts)

ULookup added 12 commits June 20, 2026 05:09
Includes 14 tests covering Config defaults, state management, history
restore, agent registration, delegation error handling, and sequential
pipeline ordering. Uses stub provider classes to avoid real LLM calls.
Provider layer (openai_provider.cpp, anthropic_provider.cpp) already handles
all transient failures with 3 attempts and exponential backoff. The AgentLoop
retry was redundant and introduced two bugs:

1. Duplicate stream chunks: the failed attempt already emitted partial text
   via the on_chunk callback (flushed to UI). The retry re-emitted the same
   content from the start.
2. Response.text corruption: on final failure, response.text contained debris
   from the last failed attempt.
…vider

Provider layer handles all transient errors (5xx, 429, curl errors)
with 3 attempts and exponential backoff. AgentLoop now only catches
for logging before propagating the exception upstream.

Context-window retry was also removed: all callers (fork_skill_tool,
worldbuilding_tools) already catch exceptions from sub_loop.run()
and return errors as tool results. The heuristic-based compact+retry
was unreliable — if token counting underestimates, compaction won't
help either.
@ULookup
ULookup merged commit df99f5e into main Jun 21, 2026
0 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment