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

No labels
No labels

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions