
在 effect-smol 中为 OpenAI 兼容模型透传自定义请求属性openai-compat 模型配置深度解析【免费下载链接】t3code项目地址: https://gitcode.com/GitHub_Trending/t3/t3code导读本文围绕 effect-smol 仓库中effect/ai-openai-compat包的模型配置能力展开核心内容源于其变更记录条目允许在 openai-compat 的模型配置model config与聊天请求chat request类型中声明自定义请求属性并将模型级别的自定义属性原样转发到 chat-completions 请求体中。读完本文你将掌握如何为 Effect AI 的 OpenAI 兼容语言模型注入供应商私有参数如vendor_setting、reasoning、service_tier等理解其底层转发机制、已知字段白名单与冲突处理规则并能通过仓库内的测试用例验证行为。文章以 变更记录 为主线辅以源码与测试佐证。一、变更背景从 Responses API 到 Chat Completions 的属性桥接1.1 这条变更记录在说什么在 effect-smol 仓库的 变更记录 中记录了这样一条针对effect/ai-openai-compat的补丁patch变更Allow custom request properties in openai-compat model config and chat request types, and forward model-level custom properties to chat-completions payloads.翻译过来即三件事模型配置允许自定义请求属性model()/make()/layer()传入的配置对象可以携带任意供应商自定义字段聊天请求类型允许自定义请求属性底层 chat request 类型即 chat-completions 请求体同样开放自定义字段模型级自定义属性转发到 chat-completions payload运行时这些字段会被原样拼进最终发给兼容端点的请求体。这条变更的实用价值在于OpenAI 兼容生态中存在大量非标准但广泛使用的厂商扩展字段例如部分网关要求的vendor_setting、供应商专属的推理控制参数等此前这些字段会被 openai-compat 严格过滤无法随请求下发本次变更使它们得以透传。1.2 涉及的核心模块变更直接作用于packages/ai/openai-compat包关键文件为OpenAiLanguageModel.ts模型配置类型定义、请求构造与响应转换OpenAiClient.tsOpenAI 兼容客户端与请求/响应类型定义OpenAiLanguageModel.test.ts验证自定义属性透传的测试用例。二、模型配置类型自定义属性的入口2.1ConfigOptions与ModelConfig在 OpenAiLanguageModel.ts 中配置类型定义如下type ConfigOptions Simplify Partial OmitCreateResponse, input | tools | tool_choice | stream | text { /** File ID prefixes used to identify file IDs in Responses API */ readonly fileIdPrefixes?: ReadonlyArraystring | undefined /** Configures text response verbosity: low | medium | high */ readonly text?: { readonly verbosity?: low | medium | high | undefined } | undefined /** Whether to use strict JSON schema validation. Defaults to true. */ readonly strictJsonSchema?: boolean | undefined } type ModelConfig OmitConfigOptions, model { readonly [x: string]: unknown }解读ConfigOptions继承了CreateResponse中除input、tools、tool_choice、stream、text之外的全部字段如temperature、top_p、max_output_tokens、user、seed、service_tier、reasoning、store、conversation等并额外提供三个 openai-compat 特有项ModelConfig在ConfigOptions基础上追加了{ readonly [x: string]: unknown }索引签名——这正是模型配置允许自定义请求属性的类型级体现任何不在已知字段列表中的键都会被类型系统接受为供应商自定义字段打开入口。2.2 配置的三种注入方式配置可通过三个构造函数注入OpenAiLanguageModel.ts构造函数签名适用场景modelmodel(modelId, config?)创建携带 provider/model 元数据的模型描述符配合Effect.provide使用makemake({ model, config? })命令式地构造LanguageModel.Service需要OpenAiClient依赖layerlayer({ model, config? })作为Layer组合进应用依赖图由其他层提供OpenAiClient三者最终共享同一份ModelConfig形状因此自定义属性在任一入口均可声明。2.3 运行时合并顺序make内部通过makeConfig完成配置合并OpenAiLanguageModel.tsEffect.succeed({ model, ...providerConfig, ...Context.getOrUndefined(services, Config) })合并优先级为Effect Context 中的Config服务 providerConfig传入值 模型 id。这意味着运行时通过withConfigOverride源码提供的自定义属性会覆盖模型构造时声明的同名属性适合同一模型在不同作用域使用不同供应商参数的场景。三、请求类型ChatCompletionRequest与索引签名3.1 请求体类型定义在 OpenAiClient.ts 中chat-completions 请求体类型ChatCompletionRequest同样以索引签名收尾export type ChatCompletionRequest { // 已知字段model、messages、temperature、top_p、max_tokens、user、 // seed、parallel_tool_calls、service_tier、reasoning、stream、 // stream_options、response_format、tools、tool_choice 等 readonly [x: string]: unknown } export type CreateResponseRequestJson ChatCompletionRequestCreateResponseRequestJson就是OpenAiClient的createResponse/createResponseStream实际发送的请求体类型OpenAiClient.ts。索引签名使自定义属性在类型层面可写。3.2 已知属性白名单为避免自定义属性与内置字段冲突OpenAiLanguageModel.ts 维护了一个已知属性集合createResponseKnownProperties包含metadata、top_logprobs、temperature、top_p、user、safety_identifier、prompt_cache_key、service_tier、prompt_cache_retention、previous_response_id、model、reasoning、background、max_output_tokens、max_tool_calls、text、tools、tool_choice、truncation、input、include、parallel_tool_calls、store、instructions、stream、conversation、modalities、seed。这些字段由 openai-compat 显式处理不应作为自定义属性重复注入。四、转发机制extractCustomRequestProperties与 payload 构造4.1 自定义属性的提取转发核心在 OpenAiLanguageModel.tsconst extractCustomRequestProperties (payload: CreateResponse): Recordstring, unknown { const customProperties: Recordstring, unknown {} for (const [key, value] of Object.entries(payload)) { if (!createResponseKnownProperties.has(key)) { Rec.assignProperty(customProperties, key, value) } } return customProperties }逻辑清晰遍历模型配置展开后的请求对象凡是不在白名单中的键全部收进customProperties随后被展开进最终 chat-completions payload。4.2 组装进请求体toChatCompletionsRequestOpenAiLanguageModel.ts将内部请求转换为对外 payloadconst toChatCompletionsRequest (payload: CreateResponse): CreateResponseRequestJson { const messages toChatMessages(payload.input) const responseFormat toChatResponseFormat(payload.text?.format) const tools payload.tools ! undefined ? payload.tools.map(toChatTool)... const toolChoice toChatToolChoice(payload.tool_choice) return { ...extractCustomRequestProperties(payload), // ← 自定义属性最先展开 model: payload.model ?? , messages: messages.length 0 ? messages : [{ role: user, content: }], ...(payload.temperature ! undefined ? { temperature: payload.temperature } : undefined), ...(payload.top_p ! undefined ? { top_p: payload.top_p } : undefined), ...(payload.max_output_tokens ! undefined ? { max_tokens: payload.max_output_tokens } : undefined), ...(payload.user ! undefined ? { user: payload.user } : undefined), ...(payload.seed ! undefined ? { seed: payload.seed } : undefined), ...(payload.parallel_tool_calls ! undefined ? { parallel_tool_calls: payload.parallel_tool_calls } : undefined), ...(payload.service_tier ! undefined ? { service_tier: payload.service_tier } : undefined), ...(payload.reasoning ! undefined ? { reasoning: payload.reasoning } : undefined), ...(responseFormat ! undefined ? { response_format: responseFormat } : undefined), ...(tools.length 0 ? { tools } : undefined), ...(toolChoice ! undefined ? { tool_choice: toolChoice } : undefined) } }值得注意的细节自定义属性位于展开顺序最前后续显式字段如model、temperature会覆盖同名自定义键——这与白名单机制形成双重保险max_output_tokens被映射为 chat-completions 的max_tokens这是 Responses API 与 Chat Completions 之间的字段名桥接store、conversation、include等 Responses 专用字段不直接透传而是通过prepareMessages在消息转换阶段处理如store false时追加reasoning.encrypted_content的 include 项见 OpenAiLanguageModel.ts。五、实操示例与测试验证5.1 供应商自定义属性透传仓库测试用例 OpenAiLanguageModel.test.ts 直接验证了本条变更yield* LanguageModel.generateText({ prompt: hello }).pipe( Effect.provide(OpenAiLanguageModel.model(gpt-4o-mini, { vendor_setting: { mode: strict } })), Effect.provide(layer) ) // 断言捕获到的请求体携带自定义属性 const requestBody yield* getRequestBody(capturedRequest) assert.deepStrictEqual(requestBody.vendor_setting, { mode: strict })测试通过一个 mock HttpClient 拦截真实请求断言vendor_setting原样出现在 chat-completions 请求体中。这证明自定义属性不仅是类型允许运行时也确实会转发。5.2 已知字段的正常处理同一测试文件前一个用例L77-L97验证了已知字段的正常路径OpenAiLanguageModel.model(gpt-5, { reasoning: { effort: medium, summary: auto } }) // 断言 requestBody.reasoning 等于 { effort: medium, summary: auto }reasoning属于白名单字段由显式分支转发行为不受自定义属性机制影响。5.3 嵌入模型侧的自定义字段变更同时覆盖了 openai-compat 的嵌入embedding路径。OpenAiEmbeddingModel.ts 与 OpenAiEmbeddingModel.test.ts 中自定义字段同样可通过模型配置传入并透传到嵌入请求且允许自定义嵌入模型名如my-custom-embedding-model。这意味着兼容层不仅对 chat-completions 开放自定义属性对 embeddings 端点也保持一致行为。5.4 端到端验证建议若要在本地复现该行为可参考 README 的安装方式npm install effectrc effect/ai-openai-compatrc随后构造一个 mock 或真实 HTTP 客户端捕获请求体按 5.1 的模式断言自定义字段。仓库的vitest.config.ts已配置好测试运行环境可直接运行packages/ai/openai-compat下的测试套件。六、设计要点与使用约束6.1 使用要点总结要点说明自定义属性入口model()/make()/layer()的 config 参数以及 Effect Context 中的Config服务类型支持ModelConfig与ChatCompletionRequest均带[x: string]: unknown索引签名转发机制extractCustomRequestProperties过滤白名单后展开进 payload优先级ContextConfig 构造 config 模型 idwithConfigOverride可做作用域级覆盖冲突保护白名单字段由显式分支处理自定义展开位于最前、可被标准字段覆盖覆盖范围语言模型chat-completions与嵌入模型embeddings两条路径均生效6.2 需要留意的边界strictJsonSchema与fileIdPrefixes是 openai-compat 内部消费项在makeRequest中它们会被解构剔除const { fileIdPrefixes: _fip, strictJsonSchema: _sjs, ...apiConfig } config见 OpenAiLanguageModel.ts不会进入请求体因此不要指望它们能透传到服务端messages与model始终由适配层填充自定义属性不应尝试覆盖它们自定义属性不做转换对象、数组、嵌套结构均按原值展开是否被供应商端点接受由上游 API 决定当前机制面向 OpenAI Chat Completions 协议形态的兼容端点若目标端点要求字段位于请求其他位置如 header则应改用OpenAiConfig.withClientTransformOpenAiConfig.ts做 HTTP 客户端级改造两者互补而非替代。七、总结本次变更让effect/ai-openai-compat在严格类型 开放透传之间取得了平衡已知字段继续走精心设计的转换逻辑如max_output_tokens → max_tokens、消息/工具/结构化输出的桥接未知字段则通过索引签名与白名单过滤机制安全地透传到 chat-completions 与 embeddings 请求体。对于需要对接带私有扩展参数供应商的团队这一能力将显著降低适配成本仓库中的 OpenAiLanguageModel.test.ts 测试用例提供了可直接参照的行为契约。【免费下载链接】t3code项目地址: https://gitcode.com/GitHub_Trending/t3/t3code创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考