feat(llm): add model-specific reasoning effort controls
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write
|
||||
2026-07-24-adapter-owned-reasoning-effort-capabilities.md: 806e808ab18d617003f757200f0cdee5853476f5
|
||||
2026-07-24-adapter-owned-reasoning-effort-capabilities.zh.md: 9be8b6f4f7bcbe063ff90492ac44b9590860f67f
|
||||
@@ -0,0 +1,33 @@
|
||||
# Agent Note: Adapter-owned reasoning effort capabilities
|
||||
|
||||
Status: implemented
|
||||
|
||||
English | [中文](2026-07-24-adapter-owned-reasoning-effort-capabilities.zh.md)
|
||||
|
||||
## Problem
|
||||
|
||||
Reasoning strength was adapter configuration only, so a conversation could not discover or change the selected model's supported levels between requests. Promoting one adapter's level union into `dsh-llm` would make every provider and model adopt names it may not support, while a provider-specific options bag would make the loop unable to validate or durably reconstruct the effective request.
|
||||
|
||||
## Decision
|
||||
|
||||
`dsh-llm` represents a reasoning effort as the opaque branded `ReasoningEffortId`. An adapter's `resolveModelReasoning(provider, model)` returns a non-empty ordered list of ids with display metadata and may name one configured default. The core validates metadata, requires an explicit or configured effort to appear exactly in that list, and never clamps or aliases a value.
|
||||
|
||||
`LlmCallConfig` and `GenerateOptions` carry the optional effort. The agent loop resolves the post-`agent/request` config before writing `request/header`, so defaults and dynamic changes are model-visible only after becoming durable facts. A route with no registered adapter retains its proposed config so an `llm/stream` middleware can own and short-circuit it; terminal dispatch still rejects an unhandled route. A resumed loop retains the logged effort only when its initial provider/model route is unchanged; a route change discards the previous model's opaque id. The terminal `LlmService` adapter boundary repeats resolution for direct calls that do not pass through the loop.
|
||||
|
||||
The native DeepSeek adapter advertises `high` and `max`, defaults to configured effort or `high`, and exposes no effort capability while thinking is disabled. The pi-ai adapter derives each exact model's list from `getSupportedThinkingLevels()`, excludes `off`, preserves an absent profile default as a provider default, and leaves provider wire-value mapping inside pi-ai.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
**Define the pi-ai `ThinkingLevel` union in core.** Rejected because current pi-ai canonical names are an adapter implementation detail; a future provider can expose a different identifier without requiring a core release.
|
||||
|
||||
**Carry an untyped provider options object.** Rejected because the loop could neither validate a selected value nor put a stable provider-neutral fact in the request header.
|
||||
|
||||
**Clamp unsupported levels.** Rejected because a silent substitution makes the user's selected control differ from the logged request intent and hides stale deployment configuration.
|
||||
|
||||
**Include `off` as an effort.** Rejected because disabling reasoning is a mode capability with different request and output semantics, not a reasoning-strength level.
|
||||
|
||||
## Consequences
|
||||
|
||||
Clients can query one exact route and render the adapter's order and names without knowing a global enum. Adapter configuration remains the deployment-default owner, while `agent/request` can replace the effective effort on each step. Invalid metadata fails with `INVALID_MODEL_REASONING`, and unsupported explicit or configured values fail with `UNSUPPORTED_REASONING_EFFORT` before provider I/O.
|
||||
|
||||
The capability query is asynchronous and exact-model resolution may fail for adapters backed by authoritative catalogs. Keyless service, adapter, loop, session, and request-header tests pin validation, defaulting, dynamic changes, logging, and resume behavior; runnable snapshots pin the resolved effort in real assembled request headers, while key-gated adapter tests exercise provider serialization.
|
||||
@@ -0,0 +1,33 @@
|
||||
# Agent Note:适配器持有的推理强度能力
|
||||
|
||||
Status: implemented
|
||||
|
||||
[English](2026-07-24-adapter-owned-reasoning-effort-capabilities.md) | 中文
|
||||
|
||||
## 问题
|
||||
|
||||
推理强度过去只能在适配器中配置,因此对话无法在多次请求之间发现或更改所选模型支持的等级。若将某个适配器的等级联合类型提升到 `dsh-llm`,所有提供方和模型都必须采用一套自身可能并不支持的名称;若改用提供方特有的 options 对象,主循环又无法校验最终生效的请求,也无法通过持久化记录准确重建该请求。
|
||||
|
||||
## 决策
|
||||
|
||||
`dsh-llm` 使用不透明的品牌类型 `ReasoningEffortId` 表示推理强度。适配器的 `resolveModelReasoning(provider, model)` 返回非空的有序 ID 列表及其展示元数据,并可指定一个由配置确定的默认值。核心会校验元数据,要求显式指定或配置指定的推理强度与列表中的某个 ID 完全一致,且绝不自动调整或为值提供别名。
|
||||
|
||||
`LlmCallConfig` 和 `GenerateOptions` 携带可选的推理强度。agent loop(智能体循环)在 `agent/request` 处理完成后、写入 `request/header` 前解析配置,因此默认值和动态变更只有成为持久化事实后才对模型可见。没有已注册适配器的路由会保留原定配置,使 `llm/stream` 中间件可以接管并短路该请求;若仍未得到处理,最终分发会拒绝该路由。恢复后的主循环仅在初始提供方/模型路由未变时保留日志中记录的推理强度;如果路由发生变化,则丢弃上一模型的不透明 ID。最终的 `LlmService` 适配器边界会再次执行解析,以覆盖未经过主循环的直接调用。
|
||||
|
||||
原生 DeepSeek 适配器声明 `high` 和 `max`,默认使用配置指定的推理强度,若未配置则使用 `high`;禁用思考时不暴露推理强度能力。pi-ai 适配器通过 `getSupportedThinkingLevels()` 按具体模型推导等级列表,排除 `off`,在 profile 未指定默认值时保留提供方默认行为,并将提供方协议值的映射留在 pi-ai 内部。
|
||||
|
||||
## 备选方案
|
||||
|
||||
**在核心中定义 pi-ai 的 `ThinkingLevel` 联合类型。** 不予采纳:pi-ai 当前的规范名称属于适配器实现细节;未来的提供方可以暴露不同的标识符,而无需为此发布新的核心版本。
|
||||
|
||||
**携带无类型约束的提供方 options 对象。** 不予采纳:主循环既无法校验选定值,也无法在请求头中写入稳定且与提供方无关的事实。
|
||||
|
||||
**自动调整不支持的等级。** 不予采纳:静默替换会导致用户选定的控制项与日志记录的请求意图不一致,还会掩盖陈旧的部署配置。
|
||||
|
||||
**将 `off` 列为推理强度。** 不予采纳:禁用推理属于具有不同请求和输出语义的模式能力,而不是推理强度等级。
|
||||
|
||||
## 影响
|
||||
|
||||
客户端可以查询一条确切路由,并按适配器给出的顺序和名称渲染等级,而无需了解全局枚举。适配器配置仍负责提供部署默认值,`agent/request` 则可以在每个步骤替换实际生效的推理强度。元数据无效时抛出 `INVALID_MODEL_REASONING`;显式指定或配置指定的值不受支持时,会在提供方 I/O 前抛出 `UNSUPPORTED_REASONING_EFFORT`。
|
||||
|
||||
能力查询采用异步方式;对于由权威目录支持的适配器,确切模型解析可能失败。无密钥的服务、适配器、主循环、会话和请求头测试为校验、默认值解析、动态变更、日志记录和恢复行为提供回归保障;可运行快照锁定实际组装请求头中的已解析推理强度,仅在有密钥时运行的适配器测试则覆盖提供方序列化。
|
||||
Reference in New Issue
Block a user