feat(llm): 支持 OpenAI Responses API 格式的 LLM 配置

依赖升级到 norma v0.3.1(新增 FormatOpenAIResponses + reasoning_content 回传
修复)。每个 LLM profile 现在可选第三种格式 openai-responses,打
POST /v1/responses,与 Chat Completions / Anthropic 并列。

- agent.Config:ConfigFrom / FromEnv / Provider() 支持 openai-responses
  (BaseURL 归一化去 /responses 尾巴,默认 model gpt-5)
- server:saveLLMConfig 放开二元 format 归一;模型列表端点复用 OpenAI
  /models(Responses 模型列表仍在 /v1/models)
- db: llm_profiles 的 format CHECK 约束加入 openai-responses,并幂等迁移
  (DROP + ADD CONSTRAINT)补旧库
- web: 格式下拉新增「OpenAI (Responses API)」,类型与回显同步

流式/非流式两条路都走新 adapter;输出、token 统计、操作统计、LLM 录制
四条链路复用中性层,无需改动。
This commit is contained in:
Autumn-27 committed 2026-08-25 11:39:24 -04:00
1 parent 587ba422b4
commit 12541a6dec
9 files changed
+71 -16

No files matched your search

+2 -3
View File
@@ -317,10 +317,9 @@ func (s *Server) loadLLMConfig() (agent.Config, bool) {
// saveLLMConfig persists the LLM config as the active "default" profile in PG.
func (s *Server) saveLLMConfig(cfg agent.Config) error {
// cfg.Provider() already returns one of the three valid format strings
// (anthropic / openai / openai-responses), matching the DB CHECK constraint.
format := cfg.Provider()
if format != "anthropic" {
format = "openai"
}
var id int64
// agent.Config carries no failover fields, so carry the stored ones forward —
// otherwise this legacy endpoint would silently reset the profile's priority,
+4 -2
View File
@@ -1926,11 +1926,13 @@ func (s *Server) pgListModels(w http.ResponseWriter, r *http.Request) {
var candidates []candidate
switch provider {
case "openai":
case "openai", "openai-responses":
// Responses API shares the OpenAI model list at /v1/models; tolerate a full
// endpoint URL of either format.
if baseURL == "" {
baseURL = "https://api.openai.com/v1"
}
b := strings.TrimRight(strings.TrimSuffix(baseURL, "/chat/completions"), "/")
b := strings.TrimRight(strings.TrimSuffix(strings.TrimSuffix(baseURL, "/chat/completions"), "/responses"), "/")
candidates = append(candidates, candidate{b + "/models", bearerHdr()})
default: // anthropic
if baseURL == "" {