文档首页/dialogue:统一对话转换、续接与适配 SSE

dialogue:统一对话转换、续接与适配 SSE

English · 上层架构

PipeDialogue 对同协议 OpenAI 调用保留原生 SSE 并校验终态,其他组合复用统一流状态机;ResponseHistory 保留原生推理/工具项。标准入口仍经过鉴权、预算和护栏;不改用 bypass 执行链。完整边界见兼容对照。

如何由其他模块接入

这里的“对外”指仓库内其他 Go 包。Go 的 internal 规则限制了仓库外的直接导入;HTTP 客户端使用已挂载的网关接口。

在 responses 续接恢复历史之后调用 ResponsesRequestBody,把历史与增量合并,再执行护栏和预算;已水合的部署交给 BuildDialogue/BuildRegisteredOperation,最后由执行器发送。

入口或资源 调用方式与返回结果
ResponsesRequestBody(body, history) 返回合并后的新请求或错误
BuildDialogue / BuildRegisteredOperation 构造显式 transport 的上游请求
ProbeDialogue(ctx, client, dep) 发送真实最小探测请求

接入 demo

将下面代码保存为仓库内的 demo/main.go,在仓库根目录执行 go run ./demo;运行后删除本次创建的 demo 目录。

package main

import (
	"fmt"
	dpdialogue "github.com/sunqirui1987/xhub/internal/dataplane/dialogue"
	llmdialogue "github.com/sunqirui1987/xhub/internal/llm/dialogue"
)

// main 演示本页 API 的最小调用;无参数和返回值,打印上述预期结果,依赖与副作用见本页说明。
func main() {
	history := []llmdialogue.Turn{{Role: "user", Text: "Hello"}, {Role: "assistant", Text: "Hi"}}
	body, err := dpdialogue.ResponsesRequestBody(map[string]any{"input": "Continue", "previous_response_id": "demo-response"}, history)
	if err != nil {
		panic(err)
	}
	d, err := llmdialogue.ParseDialogue("openai-responses", body)
	if err != nil {
		panic(err)
	}
	fmt.Println(len(d.Turns), body["previous_response_id"] == nil)
}

输出 3 true:两条父历史加一条本轮输入,父 ID 已在网关消费。

错误、状态与副作用

当前输入仍必须有效。StripGatewayFields 原地修改映射;ProbeDialogue 会访问外部服务并可能产生费用,不能当纯校验调用。

职责

统一对话转换、续接与适配 SSE。只转换支持的文本和函数工具,不可表达的能力明确失败;完整原生能力走 bypass。

Review 顺序

dialogue.go: BuildDialogue, ResponsesRequestBody, ProbeDialogue → dialogue_stream.go: PipeDialogue

先读消费方接口或输入契约,再沿入口阅读辅助函数及旁边的单元测试;涉及请求生命周期时检查上层调用方。

依赖

llm 管理协议值和官方 SDK 类型,provider 声明传输;原生透传在 transport。

验证

同目录测试:dialogue_probe_test.go, dialogue_stream_test.go, registered_operation_test.go, responses_continuation_test.go, sdk_stream_test.go

仓库根目录执行:go test ./internal/dataplane/dialogue -count=1

真实 HTTP/持久化契约放 cmd/regression;浏览器 → 网关 → 本地上游流程放 frontend/e2e。测试清理私有 schema 与临时夹具;离线测试不假设有付费供应商凭据。

输入关键词搜索全文,例如:预算、路由、Docker