apache / apache/maka

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

Open
#3,802 2 comments 0 reactions 1 assignee Claimed by @chinawch007 View on GitHub
Dominant language
TypeScript
Stars
5.4k
Forks
502
Avg merge
1d 2h
Merged PRs (30d)
715

Description

`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](https://github.com/BerriAI/litellm/pull/24373)) and pydantic-ai's per-provider `_CHAT_FINISH_REASON_MAP` ([pydantic-ai#7678](https://github.com/pydantic/pydantic-ai/issues/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_error`、`other`、`unknown` 和无输出的 `stop`。
- `settleModelStepOutcome` 另外把 `content-filter` 和 `error` 映射成终止失败,用的是 `ModelStepOutcome['kind']`。

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

在它们上游,`chunkFinishReason`(`packages/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](https://github.com/BerriAI/litellm/pull/24373))和 pydantic-ai 的按供应商 `_CHAT_FINISH_REASON_MAP`([pydantic-ai#7678](https://github.com/pydantic/pydantic-ai/issues/7678))。

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

本 issue 由 Claude 协助起草。

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.