Skip to content

feat(context): meter final requests and recover safely - #3850

Open
JasonW404 wants to merge 3 commits into
split/context-model-governancefrom
split/context-request-recovery
Open

feat(context): meter final requests and recover safely#3850
JasonW404 wants to merge 3 commits into
split/context-model-governancefrom
split/context-request-recovery

Conversation

@JasonW404

@JasonW404 JasonW404 commented Sep 3, 2026

Copy link
Copy Markdown
Member

概要

对实际待发送的完整请求进行计量,并以 Provider 响应状态驱动安全恢复;输入只观测和触发计划动作,输出始终使用模型 profile 的最大输出值。

新增能力

  • 计量 messages、tools、response format、多模态和协议开销组成的最终请求。
  • 保留 Provider 返回的 input/cache read/cache write/output/reasoning/total/finish reason。
  • 支持受限次数的主动重建、Provider overflow 恢复和纯文本 length continuation。
  • 增加上下文组成事件和监控证据。

对旧行为的调整

  • 移除基于猜测供应商规则的 input + max_tokens 本地硬拒绝。
  • API 请求的 max_tokens 始终采用模型 profile 的 max_output_tokens
  • 有潜在副作用或不完整工具调用的响应不会被盲目重试/续写。
  • 中途调用错误不会覆盖最终成功 completion 的 usage 数据。

对 Nexent 的提升

避免预算策略导致模型异常截断,同时通过 compaction、overflow 重建和安全 continuation 支撑 Agent 长时间运行。

规模与依赖

  • 15 个文件,3,915 行新增、300 行删除(4,215 行变更)。
  • 前置 PR:feat(models): integrate governed model configuration #3849 模型配置治理集成(当前 base 分支)。
  • SPEC:Final Request Budgeting and Recovery;Provider-observed Usage(底层归一化部分)。

验证

  • 最终请求预算 13、请求计数 6、Provider usage 6、OpenAI 适配器 91 个测试通过。
  • 上下文组成/事件、监控证据和监控回归共 103 个测试通过。
  • 本地在前两层 squash 结果上模拟本 PR 的 git merge --squash;最终结果提交 96cac2c15,测试通过且文件树与本分支一致。
  • 补充验证 OpenAI 长上下文模型 22、VLM 18 个兼容测试。

合并说明

这是堆叠变更的第三层;建议在前置 PR 合并后把 base 更新为 develop

Copilot AI 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.

🟡 Changes recommended

Monitoring evidence currently isn’t actually allowlisted/sanitized (risking sensitive data leakage) and tool-call streaming assembly can duplicate IDs/names when providers repeat fields across chunks.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR adds “final request” metering and recovery to the SDK model layer, so budgeting decisions are based on the fully-rendered provider request (including tools/media/protocol overhead), and recovery/continuations are driven by provider-observed outcomes rather than local heuristics. It also enriches monitoring with content-free evidence and normalized per-call usage records.

Changes:

  • Introduces a final-request metering + calibration system (final_request_budget) and a runtime provider counting path (provider_request_count) to estimate/verify the physical request size.
  • Adds normalized, content-free provider usage recording (provider_usage) and wires it into OpenAIModel streaming, retries, overflow recovery, and length continuations.
  • Extends monitoring/event emission for final-request evidence and context budget events; adds extensive test coverage across these flows.
File summaries
File Description
test/sdk/monitor/test_monitoring.py Updates expected budget snapshot field value (uncertainty_reserve_basis).
test/sdk/monitor/test_context_budget_evidence.py Adds tests for final-request evidence enrichment + allowlisting expectations.
test/sdk/core/models/test_provider_usage.py Adds tests for provider usage normalization and schema contract.
test/sdk/core/models/test_provider_request_count.py Adds tests for runtime provider-side input token counting and SSRF/shape gating.
test/sdk/core/models/test_openai_llm.py Expands tests for provider usage records, tool-call streaming assembly, max_tokens rules, overflow recovery, and length continuation.
test/sdk/core/models/test_final_request_budget.py Adds tests for request shape metering, calibration behavior, and recovery guards.
test/sdk/core/agents/test_context_composition.py Adds tests for context composition segmentation + reconciliation behavior.
test/sdk/core/agents/test_context_budget_event.py Adds tests ensuring budget events are content-free and allowlist compression reasons.
sdk/nexent/monitor/monitoring.py Adds ContextVar-backed final-request evidence and enriches monitoring records with it.
sdk/nexent/core/models/provider_usage.py Adds normalization utilities and a per-call usage record model.
sdk/nexent/core/models/provider_request_count.py Adds runtime provider count client with strict capability + SSRF safeguards.
sdk/nexent/core/models/openai_llm.py Integrates final-request metering, provider usage recording, overflow recovery, and length continuation into the model adapter.
sdk/nexent/core/models/final_request_budget.py Implements final-request shape metering, calibration store, and safety guards for recovery.
sdk/nexent/core/agents/context/composition.py Adds content-free context segment estimation + reconciliation to provider denominators.
sdk/nexent/core/agents/context_budget_event.py Adds content-free budget event construction with allowlisted compression reasons.
Review details

Suppressed comments (1)

sdk/nexent/core/models/openai_llm.py:693

  • Tool-call function names are accumulated with +=, which can duplicate the name if a provider repeats it across chunks. Names should be set once (first non-empty), while arguments can continue to be concatenated.
                        arguments = getattr(function, "arguments", None)
                        if name:
                            fragment["name"] += str(name)
                        if arguments:
                            fragment["arguments"] += str(arguments)
  • Files reviewed: 15/15 changed files
  • Comments generated: 3
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +2124 to +2136
def _enrich_record_with_final_request_evidence(record: Dict[str, Any]) -> None:
evidence = dict(_monitoring_final_request_evidence.get() or {})
if not evidence:
return
prompt_usage = record.get("input_tokens")
if isinstance(prompt_usage, int) and prompt_usage > 0:
evidence["provider_prompt_usage_tokens"] = prompt_usage
error_text = str(record.get("error_message") or "").lower()
if any(marker in error_text for marker in (
"context_length_exceeded", "maximum context length", "input tokens exceed",
)):
evidence["provider_overflow"] = True
record["context_budget_evidence"] = evidence
Comment on lines +680 to +686
call_id = getattr(tool_call, "id", None)
call_type = getattr(tool_call, "type", None)
function = getattr(tool_call, "function", None)
if call_id:
fragment["id"] += str(call_id)
if call_type:
fragment["type"] = str(call_type)
Comment on lines +211 to +214
def _valid_estimate(value: Any) -> Optional[int]:
parsed = _coerce_non_negative_int(value)
return parsed if parsed is not None else None

@JasonW404
JasonW404 force-pushed the split/context-request-recovery branch from 4d5294f to 4e92cb2 Compare September 3, 2026 06:45
@JasonW404
JasonW404 force-pushed the split/context-request-recovery branch from 4e92cb2 to 3a7cff4 Compare September 3, 2026 07:32
@JasonW404
JasonW404 force-pushed the split/context-request-recovery branch from 3a7cff4 to 35aac9f Compare September 3, 2026 08:43
@JasonW404
JasonW404 force-pushed the split/context-request-recovery branch from 35aac9f to 870634c Compare September 3, 2026 08:53
@JasonW404
JasonW404 force-pushed the split/context-request-recovery branch from 929a8fc to dfc98d1 Compare September 3, 2026 09:10
@JasonW404

Copy link
Copy Markdown
Member Author

补充 2026-09-03 变更:OpenAI adapter 在最终请求发出前应用统一有效策略:effort 型 reasoning 映射为 reasoning_effort,toggle 型映射为 extra_body.enable_thinking,并保留无关 extra_body 字段。真实 qwen3.7-plus 调用验证默认 effort=medium,Langfuse trace b24ef4b634847667317e87e240c82a6d 记录 policy_source=nexent_default,provider usage=30/196/226,finish_reason=stop。另修复未知 usage 子字段以 None 写入 OTEL 的告警。

@JasonW404

Copy link
Copy Markdown
Member Author

补充修复后复测:最新 Langfuse trace 5bcc0a3ad7eada4f972aeeda93e86a77,effective reasoning=true/medium、policy_source=nexent_default;provider usage=30/200/230、reasoning=191、finish_reason=stop。未知 fresh-input/cache-write/visible-output 属性未写入 span,运行无 OTEL None 属性告警。

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.

2 participants