Skip to content

fix: orchestrate structured provider features - #805

Merged
seonghobae merged 62 commits into
fix/auto-reasoning-effort-contract-rebasedfrom
fix/structured-output-orchestration
Aug 21, 2026
Merged

fix: orchestrate structured provider features#805
seonghobae merged 62 commits into
fix/auto-reasoning-effort-contract-rebasedfrom
fix/structured-output-orchestration

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Summary

  • keep non-null response_format, json_object, json_schema, and Responses requests inside the conducted multi-agent workflow
  • reserve single-provider passthrough for the explicit x-contextual-orchestrator-tool-loop: v1 client-owned tool loop
  • preserve typed multimodal evidence, tool-call adjacency, Responses metadata, and the latest user turn through final synthesis
  • isolate request sampling controls with thread-local settings and negotiate Azure models that reject non-default temperature
  • preserve Chat response_format and Responses text.format at remote and local final provider boundaries
  • persist provider, synthesis, and model-judge usage in runtime spend and the central cost ledger
  • attribute every metered workflow call to its serving model under one workflow run, without summing mixed currencies
  • bound raw in-memory workflow retention while preserving cumulative spend and durable run evidence
  • contain raw provider failures behind stable gateway-owned error boundaries

Root cause

Structured provider features previously bypassed orchestration or mutated process-wide sampling defaults. Concurrent requests could contaminate one another, Responses images could lose typed evidence, an implicit temperature=0.2 could reach an Azure deployment that supports only its default value, provider and model-judge usage could escape budget accounting, the central cost ledger could retain only final-synthesis usage, high-volume passthrough could retain every raw workflow in memory, and HTTP/local structured workflows could drop the provider-native output contract before final synthesis.

Contract

The final provider call occurs only after the Thinker/Worker/Verifier/Synthesizer workflow has assembled evidence. A Responses-capable remote provider receives the native Responses contract; a local provider translates text.format to the equivalent Chat response_format at its explicit transport boundary. The gateway independently validates structured JSON before returning it. Each provider-reported workflow call writes a model-specific cost record under the shared workflow run id; usage_record_ids exposes the complete set and unmetered_provider_call_count makes unavailable provider usage explicit. Raw workflows share the existing bounded recent-run capacity; evicted records feed compact cumulative spend while a configured durable store retains full run evidence. Image-bearing work requires an enabled vision capability, and unavailable tool execution fails closed unless the caller explicitly owns the tool loop. No provider-specific model-name table, raw provider shortcut, provider response leakage, string-coupled ledger ordering, or cross-currency monetary sum is introduced.

Stacked on fix/auto-reasoning-effort-contract-rebased at f1b0cd48271e870571b022463e1ec2c857ae4a8a.

Verification

  • exact head: 1d11e7d40dc52121d440991969be2967adf2136e
  • full suite: 1619 passed in 552.40s
  • focused retention, persistence, streaming, batch, governance, passthrough, cost, and budget regressions: 97 passed
  • incremental coverage-tracked production diff: 53 / 53 executable lines, 100%
  • exact-head statuses: CodeRabbit=success; Devin Review=success
  • all review threads: resolved
  • git diff --check: passed

No prompts, answers, images, tool arguments, credentials, real records, or unbounded traces are added to telemetry or repository artifacts.

@coderabbitai

coderabbitai Bot commented Aug 21, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e2a0e7d7-ead5-411d-b3c8-6be1449e6723

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

구조화된 Provider 요청이 멀티 에이전트 workflow를 거친 뒤 최종 Provider 호출로 전달됩니다. Responses 변환, 원본 메시지 보존, judge 제어, spend budget 검사, 로컬 MLX passthrough 및 회귀 검증이 추가되었습니다.

Changes

구조화 Provider 오케스트레이션

Layer / File(s) Summary
Provider 입력 변환과 로컬 passthrough
contextual_orchestrator/orchestrator.py, tests/test_local_mlx.py
Responses의 toolstext.format을 처리합니다. 로컬 요청에 max_tokenschat_template_kwargs를 보완합니다.
Workflow 메시지와 judge 제어
contextual_orchestrator/orchestrator.py
conductpreserve_messagesjudge 인자를 추가합니다. 원본 메시지를 workflow step에 보존하고, 활성화된 경우에만 model judge를 실행합니다.
오케스트레이션 라우팅과 최종 합성
contextual_orchestrator/orchestrator.py, contextual_orchestrator/server.py, tests/test_openai_passthrough.py, docs/architecture.md, docs/planning/adrs/0011-structured-provider-features-stay-orchestrated.md
구조화 기능과 Responses 요청을 workflow로 라우팅합니다. workflow evidence와 멀티모달 입력을 최종 합성에 전달합니다. Chat 및 Responses 응답 형식과 분석 이벤트를 갱신합니다.
공통 spend budget 검사
contextual_orchestrator/orchestrator.py, tests/test_openai_passthrough.py
Provider completion, run, batch_route 및 최종 Provider 호출에 공통 spend budget 검사를 적용합니다. 한도를 초과하면 BudgetExceededError를 발생시킵니다.
Workflow persistence와 회귀 검증
.github/workflows/repair-pr-805-runtime-accounting.yml, contextual_orchestrator/server.py
workflow 결과, trace, 정책 스냅샷과 이벤트를 저장합니다. 회귀 테스트와 전체 테스트를 실행하는 repair workflow를 추가합니다.

Estimated code review effort: 4 (Complex) | ~60 minutes

Merge Risk: 🔴 Critical · up to 7cf48

The PR routes structured provider requests through orchestration, but its added automation currently cannot parse reliably, may generate invalid source, bypasses provider budget enforcement, and can push test-generated changes with write credentials. These create merge-blocking correctness, spend-control, and security risks, so the PR is not ready to merge until they are fixed or explicitly redesigned.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant proxy_completion
  participant conduct
  participant synthesizer
  participant Provider
  Client->>proxy_completion: 구조화 Chat 또는 Responses 요청
  proxy_completion->>conduct: workflow 실행과 원본 메시지 전달
  conduct->>synthesizer: workflow evidence와 보존된 입력 전달
  synthesizer->>Provider: 최종 Provider 요청
  Provider-->>synthesizer: Provider 응답
  synthesizer-->>proxy_completion: Chat 또는 Responses 형식 응답
  proxy_completion-->>Client: orchestration metadata 포함 응답
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.63% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 32 functions across 4 files. (1 skipped: 1 unsupported.) Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 구조화된 provider 기능을 멀티 에이전트 workflow에서 처리하도록 변경한 PR의 핵심 내용을 명확하게 요약합니다.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/structured-output-orchestration

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

devin-ai-integration[bot]

This comment was marked as resolved.

@seonghobae

Copy link
Copy Markdown
Contributor Author

Addressed the current-head Devin review finding in commit 1f6b665a.

  • ModelClient.proxy_send now applies the same bounded local-provider controls to the final chat/completions synthesis path: preserves an explicit max_tokens, otherwise uses max_output_tokens, and forwards chat_template_args only for direct MLX.
  • Remote providers and explicit caller controls remain unchanged.
  • Regression coverage added for the final-synthesis-shaped local passthrough.

Local evidence on the PR head: 46 passed for tests/test_local_mlx.py; 59 passed including Chat Completions and Responses json_object/json_schema contracts; compileall and diff checks passed. Protected current-head Checks remain the merge gate.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

@seonghobae

Copy link
Copy Markdown
Contributor Author

Current-head evidence for 503e6397ee8cb1fe6005605c7732a58790d2f00d:

  • Preserved caller message order in the multi-agent final synthesis path; synthesis guidance/evidence remains appended, so structured/tool requests are not silently downgraded to passthrough.
  • Preserved Responses instructions, normalized metadata, text, and tools in the provider echo contract.
  • Treated tools: null as an omitted optional field during Responses-to-Chat translation.
  • Focused regression suite: 80 passed.
  • Full local suite: 1438 passed in 9m09s.
  • python3 -m compileall -q contextual_orchestrator and git diff --check passed before publication.

Protected remote Checks are still queued and the PR remains BLOCKED; no approval or merge bypass is claimed.

…ct-rebased' into fix/structured-output-local-controls

# Conflicts:
#	contextual_orchestrator/orchestrator.py
#	contextual_orchestrator/server.py
devin-ai-integration[bot]

This comment was marked as resolved.

…ion' into fix/structured-output-local-controls

# Conflicts:
#	contextual_orchestrator/orchestrator.py
#	contextual_orchestrator/server.py
@seonghobae

Copy link
Copy Markdown
Contributor Author

Current-head evidence for ee5cf348d85cebe7e4e95b99ff54ec561d3111f5:

  • Added one shared spend-budget guard used by run, batch conduct, provider-shaped passthrough, and the final structured/Responses synthesizer call. A structured request cannot start or perform its final provider call after budget_max_output_tokens or budget_max_cost_usd is exceeded.
  • Added the explicit local-provider max_tokens preservation regression test requested by review.
  • Updated the /v1/responses server comment to reflect conduct-based multi-agent orchestration and synthesizer shaping.
  • Focused suite: 62 passed; full local suite: 1441 passed in 8m49s; compileall and git diff --check passed.
  • Hosted Checks are still pending and the PR remains protected-gate blocked. No approval, force-push, or merge bypass was used.

@seonghobae

Copy link
Copy Markdown
Contributor Author

Exact-head validation for ee5cf348d85cebe7e4e95b99ff54ec561d3111f5:\n\n- Addressed the valid structured-provider spend-budget finding with the shared _raise_if_spend_budget_exceeded() guard before workflow entry and again before the final provider call; existing run()/batch_route() checks reuse the same helper.\n- Added the explicit local max_tokens preservation regression.\n- Focused structured/local suite: 62 passed.\n- Full exact-head suite: 1441 passed in 528.14s.\n- python3 -m compileall -q contextual_orchestrator tests and exact-base git diff --check: passed.\n\nProtected hosted Checks and independent approval remain the merge gate; no bypass or self-approval is claimed.

@seonghobae seonghobae added the bug Something isn't working label Aug 21, 2026 — with ChatGPT Codex Connector
devin-ai-integration[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

@seonghobae

Copy link
Copy Markdown
Contributor Author

Exact-head validation for a87af91 (base e226e11):

  • Structured provider completions now persist one retrievable workflow_run, including the final provider-facing synthesis step and reported usage for spend analytics.
  • The fast-mlsirm structured judge now makes one bounded direct provider call and cannot recursively enter another conducted workflow.
  • Structured Chat and Responses request sampling controls are scoped across intermediate workflow steps and restored afterward.
  • Final and intermediate preserved message sequences keep caller order and assistant tool-call/tool adjacency; the legacy analytics event names remain emitted as compatibility aliases.
  • Exact-head local focused suite: 50 passed.
  • Exact-head full suite: 1445 passed in 534.26s (0:08:54).
  • compileall and git diff --check passed.

The new hosted checks and an independent current-head approval remain required; no merge or bypass is claimed.

@seonghobae seonghobae added the priority: critical Immediate blocker, P0, urgent deadlock, or critical incident label Aug 21, 2026 — with ChatGPT Codex Connector
@seonghobae

Copy link
Copy Markdown
Contributor Author

Current exact-head audit for 5c536de:

  • Removed the concurrent self-modifying repair workflow; the reviewed implementation is now present directly in the PR tree and no workflow can rewrite or force-push source.
  • Exact current review-thread audit: 0 unresolved, non-outdated threads.
  • Local evidence remains 1445 passed in 534.26s, compileall passed, and git diff --check passed on the code parent; the latest commit only removes the unsafe workflow.
  • Hosted exact-head checks are queued. Independent approval is still absent, so the PR remains blocked and no merge or bypass is claimed.

@seonghobae

Copy link
Copy Markdown
Contributor Author

Preserved concurrent Agent commit ea37a1eb via normal merge; current exact head is a47a0c8b43cd745ac7a27018ed799261e3f391f8. Verified the remaining response-format precedence finding: _responses_to_chat_payload now has one translation path, so text.format is not overwritten by a later legacy response_format assignment. Added a regression test for json_schema precedence. Merged-head verification: uv run --with pytest pytest -q tests/test_local_gateway.py tests/test_model_judge.py tests/test_provider_reliability.py tests/test_model_discovery.py -> 108 passed; compileall and diff checks passed. The concurrent provider-error-boundary changes were retained.

@seonghobae

Copy link
Copy Markdown
Contributor Author

Resolved the remaining metadata-forwarding finding in 5764fb966461ef887e4e7b1f1ed6b1ed02337147: validated Responses metadata is now preserved as a bounded shallow-copied field on the translated Chat provider payload, so contextual metadata can accompany the provider request. Added a regression fixture with synthetic PU/corp/author keys. Verification on exact head: uv run --with pytest pytest -q tests/test_local_gateway.py tests/test_tip_reland_sdk_omit_persist_http_honesty.py tests/test_responses_text_format_http_honesty.py tests/test_responses_response_format_http_honesty.py tests/test_chat_response_format_http_honesty.py tests/test_model_judge.py -> 106 passed; compileall and diff checks passed.

@seonghobae

Copy link
Copy Markdown
Contributor Author

Revalidated the current remote Agent head 39c3c4daf64910de2fae43bdf792185021a9466e after preserving its native Responses usage commit. uv run --with pytest pytest -q tests/test_local_gateway.py tests/test_model_judge.py tests/test_tip_reland_sdk_omit_persist_http_honesty.py tests/test_responses*_http_honesty.py tests/test_chat_response_format_http_honesty.py -> 230 passed in 80.19s; compileall and diff checks passed. Our metadata preservation and single response-format translation path remain present.

@seonghobae

Copy link
Copy Markdown
Contributor Author

Current remote head is now 82bdbb41bb07e4e331985eac34337e50e9a72020 after preserving the concurrent structured-control changes. Exact-head verification: uv run --with pytest pytest -q tests/test_local_gateway.py tests/test_model_judge.py tests/test_openai_passthrough.py tests/test_tip_reland_sdk_omit_persist_http_honesty.py tests/test_responses*_http_honesty.py tests/test_chat_response_format_http_honesty.py -> 279 passed in 79.41s; compileall and diff checks passed. The metadata-forwarding and response-format precedence fixes remain present.

devin-ai-integration[bot]

This comment was marked as resolved.

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Submitted prior exact-head remediation replies; no approval asserted.

Comment thread contextual_orchestrator/orchestrator.py Outdated
Comment thread contextual_orchestrator/server.py
Comment thread contextual_orchestrator/orchestrator.py
Comment thread contextual_orchestrator/cost_router.py
devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

@seonghobae

Copy link
Copy Markdown
Contributor Author

Exact-head audit for current PR 805 head 9a3f32d (base f1b0cd4). Source work retained Responses text-format precedence and metadata forwarding, native Responses usage normalization, structured verification and in-flight budget accounting, and per-provider workflow/judge/synthesis ledger attribution. Current exact-head focused regression set: 172 passed; compileall and diff-check passed. Parent 68bdda6 full-suite 1617 passed and coverage 93.16 percent statement / 87.40 percent branch are explicitly stale after the 9a3f32d refactor and are not presented as current-head proof. Live state: open, non-Draft, mergeable clean, hosted check-runs count 0, approvals 0, labels bug and priority: critical. Current Devin informational findings on usage-record compatibility, passthrough retention, and partial provider usage were dispositioned in their threads. Decision: WAIT_AND_REMEDIATE. No emergency labels, attestation, merge, bypass, direct protected-branch push, or force push.

@seonghobae

Copy link
Copy Markdown
Contributor Author

Exact current head 1d11e7d40dc52121d440991969be2967adf2136e was revalidated in an isolated worktree. uv run --with pytest --with pytest-asyncio pytest -q passed 1619 passed in 554.01s.

The current head includes the structured/Responses workflow persistence, usage accounting, metadata preservation, native Responses output-budget precedence, and bounded raw workflow retention remediations. No additional source change was necessary from the current review set; formal approval and repository Checks remain merge gates.

@seonghobae

Copy link
Copy Markdown
Contributor Author

Exact-head update: PR 805 advanced to 1d11e7d; all prior 9a3f32d evidence is stale. On the current head, the bounded raw-workflow retention repair is present and the focused changed-path suite passed 164 tests. The full exact-head suite was independently recorded as 1619 passed; compileall, actionlint, and diff-check pass. Current live state is open, non-Draft, mergeable clean, based on f1b0cd4, with zero hosted check-runs and zero approvals. Devin findings on this head are informational or dispositioned; no unresolved source finding is being treated as approval. Decision: WAIT_AND_REMEDIATE. No merge, bypass, direct protected-branch push, fake status, self-approval, or force push.

…ct-rebased' into fix/structured-output-local-controls

# Conflicts:
#	contextual_orchestrator/orchestrator.py
#	contextual_orchestrator/server.py
@seonghobae

Copy link
Copy Markdown
Contributor Author

Verified on exact head 1d11e7d40dc52121d440991969be2967adf2136e: the previously reported Responses translation issues are already fixed in the current source. _responses_to_chat_payload forwards metadata and translates text.format (including json_schema) through _responses_text_format_to_chat_response_format; the local Responses path uses that helper before provider transport.

Fresh targeted verification: Responses persistence/echo contract 12 passed; local gateway plus Responses response-format/text-format/metadata contracts 71 passed. No additional patch is needed for those stale review findings. The PR remains unmergeable until its required independent approval/check gates are current.

@seonghobae

Copy link
Copy Markdown
Contributor Author

Current-head review at e975711a50cebccc296ac77206807c9491483ef8:

  • The reported Responses metadata loss is not present: _responses_to_chat_payload copies bounded metadata.
  • The reported local Responses structured-output loss is not present: text.format is translated to Chat response_format, including json_object and json_schema.
  • Focused current-head validation: tests/test_openai_passthrough.py, tests/test_local_gateway.py, and tests/test_tip_reland_sdk_omit_persist_http_honesty.py: 113 passed.
  • No source patch is warranted. Formal approval and terminal Checks remain the merge gates; no self-approval or bypass was used.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 4 new potential issues.

Open in Devin Review

Comment thread contextual_orchestrator/orchestrator.py Outdated
Comment thread contextual_orchestrator/cost_router.py
Comment on lines +2353 to +2375
workflow = self.conduct(messages, preserve_messages=True, judge=True)
in_flight_output_tokens = 0
in_flight_cost_usd = 0.0
model_by_agent = {agent.id: agent.model for agent in self.agents}
for row in workflow["trace"]:
output_tokens, _reported = _step_output_token_count(row)
in_flight_output_tokens += output_tokens
model = model_by_agent.get(row.get("served_agent_id") or row.get("agent_id"))
price = self.price_per_million.get(model) if model is not None else None
if price is not None:
in_flight_cost_usd += output_tokens / 1_000_000 * price
verification = workflow.get("verification")
verification_usage = verification.get("judge_usage") if isinstance(verification, dict) else None
if isinstance(verification_usage, dict):
judge_output_tokens, _reported = _step_output_token_count(
{"usage": verification_usage, "output": ""}
)
in_flight_output_tokens += judge_output_tokens
judge_agent_id = verification.get("judge_agent_id")
judge_model = model_by_agent.get(judge_agent_id)
judge_price = self.price_per_million.get(judge_model) if judge_model is not None else None
if judge_price is not None:
in_flight_cost_usd += judge_output_tokens / 1_000_000 * judge_price

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: conduct() budget not enforced mid-workflow for structured requests

In _orchestrated_provider_completion, the budget is checked once at entry, then conduct runs the full multi-agent plan plus the model judge (real provider calls) before the in-flight recheck fires ahead of final synthesis. A structured request can overspend the cap during conduct, since conduct never consults the budget per step. This matches pre-existing conduct behavior, so it is not a regression, but the cap is softer than it looks.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in stacked PR #810 at exact head 513a815, targeting the protected PR #765 branch after #805 merged. A synchronized process budget ledger now charges every completed provider call before the next workflow step, retains failed-workflow spend, persists the compact meter in the state store, and avoids double-counting traced conduct usage. The exact-head full suite passed 1645 tests.

Comment thread contextual_orchestrator/orchestrator.py
@seonghobae

Copy link
Copy Markdown
Contributor Author

Reviewed current exact head 3bd723c. The current implementation preserves Responses URL/data image parts through _responses_chat_content -> image_url content, selects the VISION-tagged final agent, and the multimodal normalization regression is present. Structured/Responses requests remain on _orchestrated_provider_completion; the internal FastMLSI judge is the documented bounded direct provider exception, not a public request downgrade. Workflow trace and synthesis usage are persisted before returning workflow_run_id. Current focused evidence: uv run --with pytest --with pytest-asyncio pytest -q tests/test_multimodal_workflow_evidence.py tests/test_openai_passthrough.py tests/test_local_gateway.py => 107 passed. No source change is warranted from the current-head findings; stale reports were resolved on the branch.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 3 new potential issues.

Open in Devin Review

Comment on lines +2658 to +2672
def _remember_workflow_run(self, record: dict[str, Any]) -> None:
"""Retain recent raw evidence and compact spend for evicted runs."""
run_id = record["workflow_run_id"]
with self._workflow_run_lock:
if run_id in self._workflow_runs:
try:
self._run_order.remove(run_id)
except ValueError: # pragma: no cover - defensive state repair
pass
elif self._run_order.maxlen and len(self._run_order) >= self._run_order.maxlen:
evicted_id = self._run_order[-1]
evicted = self._workflow_runs.pop(evicted_id)
self._accumulate_run_spend(self._archived_spend, evicted)
self._workflow_runs[run_id] = record
self._run_order.appendleft(run_id)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Persisted workflow runs unretrievable after 128 newer runs

_remember_workflow_run now drops the oldest run from self._workflow_runs once the recent-run deque reaches its 128-entry limit, but get_workflow_run reads only that in-memory map and never falls back to self._store. With --state-db set, a run persisted to sqlite but older than the 128 most recent returns a KeyError/404, and a restart reloads only the newest 128, so older persisted runs stay unretrievable.

Prompt for agents
The recent-run cache is now bounded to 128 entries by _remember_workflow_run (it pops evicted ids from self._workflow_runs). However get_workflow_run only checks self._workflow_runs and raises KeyError otherwise, and _reload_state re-bounds to 128 on startup. This means a workflow run that was persisted to the durable state store (--state-db) but is older than the 128 most recent is no longer retrievable via get_workflow_run / get_access_report / the workflow_runs API, even though it exists in the store. ADR 0014 states the durable state store is the long-term run-evidence boundary. Consider having get_workflow_run fall back to loading the run by id from self._store when it is not in the in-memory map (the _StateStore currently only exposes load(kind, limit) with no by-id lookup, so a by-id read may need to be added).
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines 3695 to 3712
def spend_analytics(self, price_per_million: dict[str, float] | None = None) -> dict[str, Any]:
"""Estimated token and cost spend per model, aggregated from workflow runs.
"""Token and cost spend per model, aggregated from workflow runs.

Tokens are ESTIMATED from runtime output text (~4 chars/token), not provider-reported
usage. Cost is computed only for models with an operator-supplied price; models without
one are reported under ``unpriced_models`` with a null cost. This is the honest local
floor for spend observability, not a billing system.
Provider-reported usage is preferred; runtime output text provides a
deterministic estimate only when usage is unavailable. Cost is computed
only for models with an operator-supplied price, and unpriced models are
reported explicitly. This is the honest local floor for spend
observability, not a billing system.
"""
prices = {**self.price_per_million, **(price_per_million or {})}
model_by_agent = {agent.id: agent.model for agent in self.agents}
by_model: dict[str, dict[str, Any]] = {}
total_output_tokens = 0
total_prompt_tokens = 0
reported_prompt_tokens = 0
any_reported_prompt = False

for run in self._workflow_runs.values():
total_prompt_tokens += estimate_tokens(run.get("prompt_text", ""))
for step in run["trace"]:
model = model_by_agent.get(step.get("agent_id"), "unknown")
estimated = estimate_tokens(step.get("output", ""))
usage = step.get("usage")
reported_prompt = usage.get("prompt_tokens") if isinstance(usage, dict) else None
if isinstance(reported_prompt, int):
reported_prompt_tokens += reported_prompt
any_reported_prompt = True
reported = usage.get("completion_tokens") if isinstance(usage, dict) else None
if isinstance(reported, int):
effective, is_reported = reported, True
else:
effective, is_reported = estimated, False
bucket = by_model.setdefault(
model, {"estimated_output_tokens": 0, "output_tokens": 0, "step_count": 0, "reported_steps": 0}
)
bucket["estimated_output_tokens"] += estimated
bucket["output_tokens"] += effective
bucket["step_count"] += 1
bucket["reported_steps"] += 1 if is_reported else 0
total_output_tokens += effective
with self._workflow_run_lock:
stats = copy.deepcopy(self._archived_spend)
recent_runs = list(self._workflow_runs.values())
for run in recent_runs:
self._accumulate_run_spend(stats, run)
by_model = stats["by_model"]
total_output_tokens = stats["total_output_tokens"]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Bounded run cache preserves cumulative spend without double counting

spend_analytics now sums the deep-copied _archived_spend accumulator (fed by evicted runs) plus a live pass over remaining in-memory runs. Eviction accumulates before pop, and re-persisting an existing id first removes it from _run_order, so neither path double counts. Verified against test_workflow_reload_bounds_raw_records_and_preserves_spend and the concurrent test_raw_run_retention_is_bounded_without_resetting_spend.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +3338 to +3340
if isinstance(last_error, ProviderResponseError):
raise last_error
raise RuntimeError(f"all {len(candidates)} candidate agents failed for role={role}") from None

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Reasoning-only responses trip the circuit breaker before re-raising

_send_with_retry and _invoke suppress the exception cause and emit package-owned messages, but re-raise ProviderResponseError so missing-content diagnostics survive. A reasoning-only response still counts as a failure via _record_failure in _invoke before the error propagates, so repeated such responses can open an agent's circuit breaker. This matches the prior RuntimeError behavior.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@seonghobae
seonghobae merged commit 5379157 into fix/auto-reasoning-effort-contract-rebased Aug 21, 2026
2 checks passed
@seonghobae
seonghobae deleted the fix/structured-output-orchestration branch August 21, 2026 11:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working priority: critical Immediate blocker, P0, urgent deadlock, or critical incident

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant