Skip to content

Fix agentic-traffic reliability and enable provider prompt caching - #91

Merged
veerareddyvishal144 merged 4 commits into
mainfrom
fix/agentic-reliability-prompt-cache
Aug 18, 2026
Merged

Fix agentic-traffic reliability and enable provider prompt caching#91
veerareddyvishal144 merged 4 commits into
mainfrom
fix/agentic-reliability-prompt-cache

Conversation

@veerareddyvishal144

Copy link
Copy Markdown
Contributor

Problem

Agent loops through Lynkr were silently degraded in four ways, all observed and measured on ITSMBench / terminal-bench harnesses (pi agent, Azure gpt-5.6-sol):

  1. Episodes killed mid-task: upstream 429-retry exhaustion surfaced as a contentless 200 with finish_reason: stop; agent clients read that as "done" and stopped. Killed 6/7 benchmark episodes in one run.
  2. System prompt discarded after turn 1: continuations replaced the client agent's instructions with a generic one-liner.
  3. Reasoning never forwarded: reasoning_effort was dropped on the Azure Responses path; thinking requests silently ran at minimum effort. max_output_tokens was also unset for gpt-5.x (read from the wrong key).
  4. Provider prompt caching broken (0–3% hit rate): six code paths rewrote request bytes every turn — random tool-call fallback IDs (Date.now()+random), the system-prompt swap, conditional reminder-stripping, timestamped tee IDs in tool-result compression, tier-flapping compression thresholds — so prefixes never matched and every token billed at full price.

Fix

  • openai-router: guard that aborts the SSE stream (client retries) instead of serving a clean empty completion.
  • databricks: forward reasoning.effort; fix max_output_tokens; surface cache_read_input_tokens through both response conversions into telemetry; add prompt_cache_key (session id or first-user-message hash) on gpt-5.x requests.
  • Prefix stability: deterministic content-hash tool-call/tee IDs, one constant system prompt per conversation (full prompt, reminders stripped uniformly on every turn), fixed COMPLEX compression threshold.
  • config: new TOOL_RESULT_COMPRESSION_ENABLED env knob (was hardcoded).
  • nodemon.json: ignore self-written data/db files (dev restart storms).

Measured results

  • Provider cache hit rate: 0–3% → 92–95% on agent loops (~5× real cost reduction; ~$5.00/task → ~$0.87/task at reasoning=high on ITSMBench).
  • Through-proxy quality on ITSMBench now statistically equal to the same model direct (35.0% vs 35.51% Pass@1).
  • No Azure content-filter rejections across ~2,000 continuation requests with the full system prompt.

Behavior changes for review

  • Tool-result compression threshold is now fixed at COMPLEX (>2000 chars) instead of following the routed tier — determinism is required for caching; net effect is lighter compression for most traffic.
  • Continuations carry the client's full (reminder-stripped) system prompt instead of the generic replacement.

Known issue (follow-up, not addressed here)

Terminal-bench A/B shows Lynkr still costs significant quality on long compute-heavy coding tasks (direct 17/22 vs through-Lynkr 8/21), dominated by agent timeouts from per-turn latency (buffered non-streaming upstream calls). Fix is true streaming passthrough — proposed as a follow-up issue.

🤖 Generated with Claude Code

Measured on ITSMBench + terminal-bench via a pi-agent harness; each fix
addresses a defect observed in production-like agent loops:

- openai-router: never serve a clean empty completion. Upstream 429-retry
  exhaustion surfaced as a contentless 200 with finish_reason=stop, which
  agent clients read as "task complete" and terminated mid-task (observed
  killing 6/7 benchmark episodes). Abort the SSE stream instead so clients
  retry.

- databricks (Azure Responses): forward reasoning effort (was silently
  dropped — thinking requests never reached the model), fix
  max_output_tokens (read from max_completion_tokens for gpt-5.x), and
  surface cache_read_input_tokens through both response conversions so
  telemetry records provider cache hits (was always null).

- Prefix stability for provider prompt caching (measured 0-3% -> 92-95%
  cache hit rate, ~5x real cost cut on agent loops):
  * deterministic tool-call fallback IDs (were Date.now()+random — history
    re-randomized every turn)
  * one constant system prompt per conversation (continuations previously
    swapped in a generic prompt, which also silently discarded the client
    agent's instructions after turn 1)
  * uniform system-reminder stripping on every turn
  * content-hash tee IDs in the tool-result compressor (were timestamped —
    new bytes in old messages each turn)
  * fixed compression threshold (routed tier flaps between turns; COMPLEX
    keeps compression deterministic and lightly lossy)
  * prompt_cache_key on Azure gpt-5.x requests (session id or first-user-
    message hash)

- config: TOOL_RESULT_COMPRESSION_ENABLED env knob (was hardcoded)
- nodemon.json: ignore self-written data/db files (restart-storm fix)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ions

- zai (anthropic-format endpoint): force buffered mode. With stream:true the
  endpoint returns Anthropic SSE, but the downstream transformer parses
  OpenAI SSE — every chunk was unreadable and clients received empty
  completions on all streamed turns (terminal-bench: 0/13 episodes survived;
  32% pass rate after this fix, verifying the mechanism).
- openai-router: mirror the streaming empty-completion guard on the
  non-streaming path — return a retryable 502 instead of a clean empty 200
  that agent clients read as "task complete".
- docs: add ITSMBench results writeup (methodology, full-89 + subset scores,
  per-family and assertion-level analysis, reproduction).

Known issue (not fixed here): the provider fallback ladder keeps the original
tier's model name when switching providers (observed: fallback to azure-openai
requesting model "kimi-k3" → 404).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@veerareddyvishal144
veerareddyvishal144 force-pushed the fix/agentic-reliability-prompt-cache branch from 74d38d4 to 6b40190 Compare August 18, 2026 20:53
vishal veerareddy and others added 2 commits August 18, 2026 13:53
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The classifier leg is live and can time out under load, sending identical
text down different fallback paths — the same turn scored differently
between the router and the pin-drift checker (observed 63 vs 77 under
suite-wide Ollama contention; session-fingerprint drift-consistency test
failed intermittently in the full suite).

First resolution wins for the process lifetime. Lexical fallback is not
memoized so a degraded score isn't locked in past service recovery. Calls
with scoring-altering opts (custom centroids/embedFn, risk, prior turns)
bypass the memo.

Fixes the flaky drift-consistency failure; 1257/1257 passing across
back-to-back full-suite runs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@veerareddyvishal144
veerareddyvishal144 merged commit 8277bf9 into main Aug 18, 2026
3 of 4 checks passed
@veerareddyvishal144
veerareddyvishal144 deleted the fix/agentic-reliability-prompt-cache branch August 18, 2026 21:07
veerareddyvishal144 pushed a commit that referenced this pull request Aug 18, 2026
The "Web Tools Test Suite" check has been failing on every PR/push that
touches its trigger paths (including src/config/index.js, which this
branch's Baidu changes touch) since commit b32e988 deleted
src/tools/web.js, src/tools/web-client.js, and test/web-tools.test.js
without removing the standalone workflow that ran that test file — this
predates and is unrelated to the Baidu provider work, confirmed failing
on main itself and on PR #90/#91 too (`gh run list --workflow
web-tools-tests.yml`).

- Remove .github/workflows/web-tools-tests.yml (nothing left to test)
- Remove the same two stale file refs (test/web-tools.test.js,
  test/code-mode.test.js — deleted in the same commit) from ci.yml's
  "Tests" job. These didn't fail there since node --test silently skips
  a missing file when given alongside other real ones in one invocation,
  but a passing check that silently means "0 tests ran for this file"
  is a gap worth closing, not a reference worth keeping.
- Update workflows/README.md: drop the Web Tools Tests section,
  renumber remaining sections, drop the dead badge/example command.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
veerareddyvishal144 added a commit that referenced this pull request Aug 18, 2026
* Add Baidu Qianfan (ERNIE) as a supported model provider

Wires up "baidu" end-to-end alongside openai/openrouter/moonshot/zai,
modeled on invokeMoonshot since Qianfan's v2 endpoint is OpenAI-compatible:

- src/config/index.js: BAIDU_API_KEY/BAIDU_ENDPOINT/BAIDU_MODEL parsing,
  config.baidu block, SUPPORTED_MODEL_PROVIDERS entry, hot-reload sync
- src/clients/databricks.js: new invokeBaidu (request conversion via
  openrouter-utils, response converted to Anthropic shape locally),
  registered in PROVIDER_INVOKERS
- src/orchestrator/index.js: destination-url, tool-schema, and
  response-conversion branches for "baidu"
- src/routing/model-tiers.js: baidu case in _getProviderModel
- src/api/providers-handler.js, src/api/openai-router.js: discovery listing
- bin/lynkr-init.js: onboarding wizard entry + PROVIDER_ORDER + baseline env
- .env.example, documentation/providers.md: new provider docs/section
- test/baidu-model-mapping.test.js: model mapping, tool conversion, auth,
  response conversion (mirrors moonshot-model-mapping.test.js)
- test/dispatch-registry.test.js, test/init.test.js: anti-drift guard lists
- package.json: wire new test file into test:unit

Not yet done, flagged in code/docs: sampling-param quirks and the exact
ERNIE model roster are best-effort from public docs, not probed against a
live key; "baidu" is not yet in DEFAULT_OPENAI_SSE_PROVIDERS (streaming is
buffered-only until SSE shape is confirmed); not added to
REASONING_CONTENT_PROVIDERS (unconfirmed whether ERNIE reasoning models
emit reasoning_content). All 1267 existing unit tests plus the new baidu
suite pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs(README): add Z.ai, Moonshot, and Baidu Qianfan to provider table

Follow-up to 57959c0. Only these three rows + the cloud-provider count are
touched — README already had unrelated pending edits (OAuth/wrap-mode
policy note) in the working tree that are intentionally left unstaged,
they're not part of this branch's scope.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* ci: remove dead web-tools-tests workflow and stale test refs

The "Web Tools Test Suite" check has been failing on every PR/push that
touches its trigger paths (including src/config/index.js, which this
branch's Baidu changes touch) since commit b32e988 deleted
src/tools/web.js, src/tools/web-client.js, and test/web-tools.test.js
without removing the standalone workflow that ran that test file — this
predates and is unrelated to the Baidu provider work, confirmed failing
on main itself and on PR #90/#91 too (`gh run list --workflow
web-tools-tests.yml`).

- Remove .github/workflows/web-tools-tests.yml (nothing left to test)
- Remove the same two stale file refs (test/web-tools.test.js,
  test/code-mode.test.js — deleted in the same commit) from ci.yml's
  "Tests" job. These didn't fail there since node --test silently skips
  a missing file when given alongside other real ones in one invocation,
  but a passing check that silently means "0 tests ran for this file"
  is a gap worth closing, not a reference worth keeping.
- Update workflows/README.md: drop the Web Tools Tests section,
  renumber remaining sections, drop the dead badge/example command.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: vishal veerareddy <vishalveera.reddy@servicenow.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant