Skip to content

refactor(runtime): single owner for provider finish-reason semantics #3802

Description

@Astro-Han

finishReason semantics are now decided in two places with two vocabularies.

  • classifyModelFinishBoundary returns 'authoritative' | 'incomplete' | 'retryable-network-failure' and handles network_error, other, unknown, and an output-free stop.
  • settleModelStepOutcome separately maps content-filter and error to terminal failures, in ModelStepOutcome['kind'].

Both answer the same question — what does this finish reason mean. Adding a provider behaviour means remembering to touch both.

Upstream of both, chunkFinishReason (packages/runtime/src/model-adapter.ts:651, from #2297) un-maps the AI SDK's unified: 'other' back to the raw provider spelling, on the premise that other plus a spelling means an ordinary finished turn. That premise does not hold for the class of providers that report abnormal termination through finish_reason. Zhipu's API FAQ documents exactly this for SSE calls, and network_error (#3772) and sensitive are both observed values; OpenRouter separately emits error.

Prior art puts the map at the translation seam rather than in downstream policy: LiteLLM's map_finish_reason() (litellm#24373) and pydantic-ai's per-provider _CHAT_FINISH_REASON_MAP (pydantic-ai#7678).

The decision this needs: which layer owns provider-spelling to Maka finish semantics, so that supporting a new spelling is a one-line change in one table. No implementation is preferred here.

Related: #3777, #3772, #2297.

简体中文

finishReason 的语义目前在两个地方判定,用的是两套词表。

  • classifyModelFinishBoundary 返回 'authoritative' | 'incomplete' | 'retryable-network-failure',处理 network_errorotherunknown 和无输出的 stop
  • settleModelStepOutcome 另外把 content-filtererror 映射成终止失败,用的是 ModelStepOutcome['kind']

两者回答的是同一个问题——这个 finish reason 是什么意思。新增一种供应商行为,就得记得两边都改。

在它们上游,chunkFinishReasonpackages/runtime/src/model-adapter.ts:651,来自 #2297)把 AI SDK 的 unified: 'other' 换回供应商原始拼写,前提是「other 带拼写 = 一次普通结束」。对于那一类通过 finish_reason 上报异常终止的供应商,这个前提不成立。智谱 API FAQ 对流式调用正是这么写的,network_error#3772)和 sensitive 都是实际观察到的值;OpenRouter 另外还会发 error

先例都把这张表放在翻译层而不是下游策略里:LiteLLM 的 map_finish_reason()litellm#24373)和 pydantic-ai 的按供应商 _CHAT_FINISH_REASON_MAPpydantic-ai#7678)。

需要定的是:供应商拼写到 Maka finish 语义的映射由哪一层拥有,使得支持一个新拼写只需要在一张表里改一行。这里不预设实现方案。

本 issue 由 Claude 协助起草。

Metadata

Metadata

Assignees

Labels

help wantedExtra attention is needed

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions