dialogue
同协议原生字段保留、跨协议对话编解码与严格能力校验。
OpenAI 同协议使用 Dialogue.Native / DialogueResult.Native 保留 Agent 字段与原生输出,跨协议只使用已验证投影;不能等价表达的推理密文、工具或扩展明确拒绝。完整范围、LiteLLM 来源及供应商实测见兼容对照。
如何由其他模块接入
这里的“对外”指仓库内其他 Go 包。Go 的 internal 规则限制了仓库外的直接导入;HTTP 客户端使用已挂载的网关接口。
适配入口明确指定源协议 ParseDialogue,检查错误,再 EncodeDialogue 为目标协议和上游型号。响应使用 ParseDialogueResult/EncodeDialogueResult;网络发送由 dataplane 负责。
| 入口或资源 | 调用方式与返回结果 |
|---|---|
| ParseDialogue(protocol, body) | 保存原协议及已验证共享投影 |
| EncodeDialogue(dialogue, protocol, model) | 编码为目标协议请求映射 |
| ParseDialogueResult / EncodeDialogueResult | 响应事实编解码 |
接入 demo
将下面代码保存为仓库内的 demo/main.go,在仓库根目录执行 go run ./demo;运行后删除本次创建的 demo 目录。
package main
import (
"fmt"
"github.com/sunqirui1987/xhub/internal/llm/dialogue"
)
// main 演示本页 API 的最小调用;无参数和返回值,打印上述预期结果,依赖与副作用见本页说明。
func main() {
d, err := dialogue.ParseDialogue("openai-responses", map[string]any{"input": "Hello"})
if err != nil {
panic(err)
}
body, err := dialogue.EncodeDialogue(d, "anthropic-messages", "demo-model")
if err != nil {
panic(err)
}
fmt.Println(len(d.Turns), body["model"], body["max_tokens"])
}输出 1 demo-model 4096;无需凭据或网络。协议标识为 openai-chat、openai-responses、anthropic-messages、gemini、vertex。
错误、状态与副作用
不支持的字段、角色和工具结构必须返回错误,不能静默丢弃能力。网关控制字段由上层先消费;完整原厂扩展走原生透传。
源码与验证
代码入口:dialogue_google.go, model.go, request.go, response.go, usage.go, validation.go
单元测试与源代码放在一起:dialogue_google_test.go, dialogue_sdk_test.go, dialogue_test.go, google_boundaries_test.go, request_boundaries_test.go, response_boundaries_test.go
go test ./internal/llm/dialogue/...
model 定义统一表示,request/response 编解码,validation 校验字段,usage 统一用量,dialogue_google 处理 Google 协议。无网络调用。
真实后台回归在 cmd/regression,浏览器流程在 frontend/e2e。离线测试使用本地 HTTP 上游,不证明生产凭据或外部厂商服务可用性。
覆盖率检查
运行 scripts/core-codec-coverage.sh(使用 bash),注册表和对话编解码各自必须达到 95% 语句覆盖率。测试失败、缺少报告或低于下限都会返回失败。覆盖率来自各包自身的单元测试,不混入后台回归或浏览器测试。
Codex 到原生 Messages 的参数边界
本次只放行有明确对应关系的参数,不能将此项表述为 Codex 默认推理模式已完整可用:
strict:false转为普通 Messages 工具,schema 保持不变;strict:true仍拒绝。parallel_tool_calls转为tool_choice.disable_parallel_tool_use的反值,保持指定工具和强制工具选择。没有工具或选择none时无需附加并行开关。映射依据:Anthropic 并行工具文档。text.format.type=text是默认文本输出,可规范化;结构化输出和verbosity仍拒绝。prompt_cache_key只接受字符串,跨协议不转发这个 OpenAI 缓存路由提示,不保证缓存命中,也不自动开启可能增加缓存写入费用的cache_control。prompt_cache_retention没有等价承诺,仍拒绝。- 只有显式
reasoning.effort=none(Chat 为reasoning_effort=none)才映射为thinking.type=disabled,且必须由所选上游型号支持关闭推理。未提供推理、推理为 null/空对象,或只有合法 summary 而没有 effort 时不设置 thinking,保留上游默认;部分型号强制 adaptive 并拒绝 disabled,不能给普通文字请求擅自注入关闭设置。非法 summary 和非 null 的reasoning.context仍拒绝。可选摘要和include=reasoning.encrypted_content不会制造不存在的推理或密文,原生 Messages 设置保持原样。上游自动返回 signed thinking 的跨协议响应与回放仍属于 #56 未实现边界,保留默认不代表该能力已修复。 low等非零推理努力仍在调用前拒绝。当前原生 Anthropic 签名 thinking 的跨协议响应、流和工具续轮尚未形成完整可重放链,不能直接启用 adaptive 再等付费响应到达后断流,也不能伪造签名或把低推理偷偷改为关闭。
同协议请求完整保留原始字段。本目录单测覆盖参数映射、边界和发送前拒绝;真实 Codex 默认模式仍需后续签名推理端到端工作,显式 none 的测试不得充当默认模式验收。
Messages 拒绝响应与后续对话
原生 Messages 保留 stop_reason=refusal、stop_details、空 content:[]、实测用量和完整 message_stop 生命周期。合法模型拒绝不能被误判成断流。
没有部分输出且 stop_details.explanation 提供真实说明时,Chat 使用 message.refusal(流式为 delta.refusal),Responses 使用标准 refusal 内容块及完整拒绝事件生命周期。不编造拒绝说明。缺失/空说明、已产生部分文本/工具/推理、畸形详情和截断流无法等价转换时明确失败,不能输出空白成功。
后续请求中,本桥接生成的单一标准 Chat/Responses 公开拒绝会投影为助手历史文本,原样保留说明。这样用户改问正常问题时,可以继续同一 Messages 上游或切换模型,公开拒绝不会被当作依赖原连接的密文。本轮响应仍保留显式拒绝标记;同协议请求原文不变。合法混合拒绝/其他输出或空拒绝历史仍可原生重放,仅拒绝跨协议转换。拒绝块含未知字段、字段类型非法或非助手角色的拒绝历史则在解析时明确报错。
依据:Anthropic 拒绝文档、OpenAI 流事件规范。适配层保留上游报告的 token 用量,不根据拒绝类别推测上游发票费用。