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:
1 parent
587ba422b4
commit
12541a6dec
9 files changed
+71
-16
No files matched your search
+18
-2
@@ -132,6 +132,12 @@ func FromEnv() (Config, bool) {
|
||||
if c.Model == "" {
|
||||
c.Model = "gpt-4o"
|
||||
}
|
||||
case "openai-responses":
|
||||
c.Format = llm.FormatOpenAIResponses
|
||||
c.APIKey = oaiKey
|
||||
if c.Model == "" {
|
||||
c.Model = "gpt-5"
|
||||
}
|
||||
default:
|
||||
c.Format = llm.FormatAnthropic
|
||||
c.APIKey = anthKey
|
||||
@@ -165,6 +171,13 @@ func ConfigFrom(provider, model, baseURL, apiKey, proxy string) Config {
|
||||
if c.Model == "" {
|
||||
c.Model = "gpt-4o"
|
||||
}
|
||||
case "openai-responses":
|
||||
c.Format = llm.FormatOpenAIResponses
|
||||
// provider appends "/responses"; tolerate a full endpoint URL.
|
||||
c.BaseURL = strings.TrimRight(strings.TrimSuffix(c.BaseURL, "/responses"), "/")
|
||||
if c.Model == "" {
|
||||
c.Model = "gpt-5"
|
||||
}
|
||||
default:
|
||||
c.Format = llm.FormatAnthropic
|
||||
// provider appends "/v1/messages".
|
||||
@@ -186,10 +199,13 @@ func isFalsy(s string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Provider returns the short provider name ("anthropic"/"openai").
|
||||
// Provider returns the short provider name ("anthropic"/"openai"/"openai-responses").
|
||||
func (c Config) Provider() string {
|
||||
if c.Format == llm.FormatOpenAI {
|
||||
switch c.Format {
|
||||
case llm.FormatOpenAI:
|
||||
return "openai"
|
||||
case llm.FormatOpenAIResponses:
|
||||
return "openai-responses"
|
||||
}
|
||||
return "anthropic"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/Autumn-27/norma/llm"
|
||||
)
|
||||
|
||||
// TestConfigFromOpenAIResponses locks the openai-responses format wiring:
|
||||
// ConfigFrom resolves the Responses format, strips a full /responses endpoint
|
||||
// back to the API base, and Provider() round-trips the short name. NewProvider
|
||||
// must build a working provider for it.
|
||||
func TestConfigFromOpenAIResponses(t *testing.T) {
|
||||
c := ConfigFrom("openai-responses", "gpt-5", "https://gw.example/v1/responses", "sk-x", "")
|
||||
if c.Format != llm.FormatOpenAIResponses {
|
||||
t.Fatalf("format=%v, want FormatOpenAIResponses", c.Format)
|
||||
}
|
||||
if c.BaseURL != "https://gw.example/v1" {
|
||||
t.Fatalf("base_url=%q, want the /responses suffix stripped", c.BaseURL)
|
||||
}
|
||||
if c.Provider() != "openai-responses" {
|
||||
t.Fatalf("Provider()=%q", c.Provider())
|
||||
}
|
||||
if !c.Stream { // default streaming preserved
|
||||
t.Fatal("Stream should default true")
|
||||
}
|
||||
if _, err := c.NewProvider(); err != nil {
|
||||
t.Fatalf("NewProvider: %v", err)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user