/** * `Model.compat` —— 一只模型对 **openai-completions 线路**的兼容声明(core `OpenAICompletionsCompat`; * 声明面 settings-schema 1.10.0 `ModelEntry.compat`)。本模块是**两条腿共用的唯一判据点 + 唯一铸点**, * 与 {@link import("./mcp-tool-faces.js").readMcpToolFaces} 同一个「判据单点、多腿一致」的形。 * * ## 它治的病(1.10.0 CHANGELOG + core 型面顶注双向亲读) * 引擎的 openai brain 一直按这五键发请求,但声明写不进来: * · settings-schema ≤1.9.0 的 `ModelEntry` 没有 `compat` 键 ⇒ 目录条目写了在 parse 期被 strip 剥掉; * · `extraBody` 也穿不过(引擎的 OpenAI 保留键表拦下 `chat_template_kwargs` / `enable_thinking`); * · env 侧只有 `MODEL_REASONING_EFFORT_LEVELS` 一根旋钮,`thinkingFormat` 无处可写。 * 真需求 = vLLM / Qwen 类**缺省开思考**的网关:关思考只能靠 `chat_template_kwargs.enable_thinking=false` * (`thinkingFormat: "qwen-chat-template"`),否则 auto 档每一轮都白烧一段思考。 * * ## 三张对表全部**从 core 的型面铸**,本仓一个词都不枚举 * `THINKING_LEVEL_TABLE` / `THINKING_FORMAT_TABLE` / `MAX_TOKENS_FIELD_TABLE` 都是 `Record<闭集词, true>`, * `COMPAT_KEY_READERS` 是 `{ [K in keyof Required]: 读器 }` 的**映射型**: * core 加词/删词、加键/删键,四张表当场 tsc 红(`MODEL_DEGRADE_ON` 的 `Set` 同款纪律)。 * 映射型这一步是刻意的 —— 只钉「键集相等」会留下一个洞:core 加一键、有人把它补进白名单却忘了写读器, * 于是这个键被**允许但不读** = 静默丢。读器与键集是**同一张表**,那个洞在结构上不存在。 * * ## 坏声明的方向:**丢 compat 键、模型本体保留、一条点名 warn**(降级通道,不是拒绝面) * 与 `toolFaces` 的「整条 server 不进」**刻意分歧**,理由是键的方向不同:`toolFaces` 声明的是写围栏 / * 敏感路径策略(**收窄**键,丢它 = 运维声明的保护静默失效);`compat` 声明的是**线路拼法**(这台网关吃 * 哪种 thinking 参数、max tokens 叫什么名),丢它 = 回落到 core 的自动推断 = **今天这一刻的行为**, * 权限/审批/凭据一格不动。[ref] 要的是「安全轴不许静默 fail-open」,本键不在安全轴上,而丢键仍然 * **响亮**(`model_compat_dropped` 点名模型与哪一段不合)—— 与 `mcp_content_class_dropped` 同形。 * 反方向(整只模型不进)的代价则不成比例:一个拼错的 `thinkingFormat` 会让这只模型从目录里消失, * 用户面直接是 `request.unknown_reference`。 * * 判形**整只判**:任一键不合 ⇒ 整只声明不收(和 `toolFaces` 每只工具「applied whole or not at all」 * 同律)。留下好的那几键 = 让运维以为整条声明生效了,而实际发出去的请求是半张脸。 */ import type { Model } from "@sema-agent/core"; /** core `OpenAICompletionsCompat` —— core 的 index 没有按名导出它,从 `Model` 的**型面**取 * (`Model` 的 `compat` 按 api 分叉;openai 那支就是它)。这不是抄一份结构,是引用同一个型。 */ export type OpenAICompletionsCompat = NonNullable["compat"]>; /** 六档思考梯(core `ThinkingLevel`)。`MODEL_DEFAULT_THINKING` / `MODEL_REASONING_EFFORT_LEVELS` / * `compat.reasoningEffortLevels` 三处共用**这一张**表 —— [ref] 件⑤ 起就是「上游加一档而这里不补 ⇒ * 编译红」的锁步姿势,本模块只是把它挪到了它真正的家(compat 的一个键)。 */ export type ModelThinkingLevel = NonNullable; /** 六档闭集(有序:低→高,取自上表的书写序),目录行 `enumValues` 与拒因文案的单一真源。 */ export declare const THINKING_LEVELS: readonly ModelThinkingLevel[]; export declare const isThinkingTier: (w: unknown) => w is ModelThinkingLevel; /** `thinkingFormat` 七词闭集(core:openai / openrouter / deepseek / together / zai / qwen / qwen-chat-template)。 */ export type ModelThinkingFormat = NonNullable; export declare const THINKING_FORMATS: readonly ModelThinkingFormat[]; export declare const isThinkingFormat: (w: unknown) => w is ModelThinkingFormat; /** `maxTokensField` 两词闭集(缺席 ⇒ core 按模型 id 推断,见 `inferMaxTokensField`)。 */ export type ModelMaxTokensField = NonNullable; export declare const MAX_TOKENS_FIELDS: readonly ModelMaxTokensField[]; type CompatKey = keyof Required; /** 五键闭集(书写序取自上表),未知键拒因文案的单一真源。 */ export declare const MODEL_COMPAT_KEYS: readonly CompatKey[]; /** * 一条目录条目的 `compat` 判形结果: * · `{ ok: true, compat }` —— 合形(`compat` 缺席 = 这条条目没有声明,或声明了一个**空**对象: * 两者对 core 同义,都不铸键); * · `{ ok: false, issue }` —— 判不出形,调用方**整只丢**这条声明并留一句 warn(见模块头注)。 * `issue` 只带键名与哪一段不合,**不带值** —— 与 `source` / `toolFaces` 同一条留痕纪律。 */ export type ModelCompatRead = { ok: true; compat?: OpenAICompletionsCompat; } | { ok: false; issue: string; }; /** 从任一腿的原始条目里读出 `compat` —— **本地 config.d 腿与 center 腿共用的唯一判据点**。 */ export declare function readModelCompat(v: unknown): ModelCompatRead; /** * `Model.compat` 的**唯一铸点**(env 腿与目录腿都经这里)。层序 = 优先级,**前层逐键赢、后层补缺席** * (BL-8:目录条目声明 ?? env 缺省 —— 不是整只替换,否则一条只写了 `thinkingFormat` 的目录条目会把 * 部署级的 `MODEL_REASONING_EFFORT_LEVELS` 静默吃掉,正是 `contextWindow`/`maxTokens` 那一族的老病)。 * * 归一两条(两腿因此对同一个空值给出同一个下场):值 `undefined` = 这一层没声明这一键; * `reasoningEffortLevels` 为**空表** = 没声明这一键(env 腿的空 CSV 与目录腿的 `[]` 同义)。 * 全空 ⇒ 回 `undefined` = **不铸这个键**(缺席 = core 走自己的推断 = 今日行为)。 */ export declare function buildModelCompat(...layers: ReadonlyArray): OpenAICompletionsCompat | undefined; export {}; //# sourceMappingURL=model-compat.d.ts.map