feat: 同步知识库与工作台相关改动
This commit is contained in:
@@ -44,12 +44,12 @@ type ChatResult struct {
|
||||
|
||||
// Client 低层 HTTP 客户端(基于 RouteConfig)
|
||||
type Client struct {
|
||||
baseURL string
|
||||
apiKey string
|
||||
model string
|
||||
maxTokens int
|
||||
baseURL string
|
||||
apiKey string
|
||||
model string
|
||||
maxTokens int
|
||||
temperature float64
|
||||
hc *http.Client
|
||||
hc *http.Client
|
||||
}
|
||||
|
||||
// NewClient 从 RouteConfig 创建客户端
|
||||
@@ -100,7 +100,45 @@ func (c *Client) post(path string, body any) (*http.Response, error) {
|
||||
for k, v := range c.headers() {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
return c.hc.Do(req)
|
||||
// #region debug-point C:llm-post
|
||||
if payload, err := json.Marshal(map[string]any{
|
||||
"sessionId": "knowledge-chat-401",
|
||||
"runId": "pre-fix",
|
||||
"hypothesisId": "C",
|
||||
"location": "backend-go/internal/ai/llm.go:Client.post:request",
|
||||
"msg": "[DEBUG] llm client request",
|
||||
"data": map[string]any{
|
||||
"url": c.url(path),
|
||||
"model": c.model,
|
||||
"hasAPIKey": c.apiKey != "",
|
||||
"authHeader": req.Header.Get("Authorization") != "",
|
||||
},
|
||||
"ts": time.Now().UnixMilli(),
|
||||
}); err == nil {
|
||||
go http.Post("http://127.0.0.1:7777/event", "application/json", strings.NewReader(string(payload)))
|
||||
}
|
||||
// #endregion
|
||||
resp, err := c.hc.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// #region debug-point C:llm-response
|
||||
if payload, err := json.Marshal(map[string]any{
|
||||
"sessionId": "knowledge-chat-401",
|
||||
"runId": "pre-fix",
|
||||
"hypothesisId": "C",
|
||||
"location": "backend-go/internal/ai/llm.go:Client.post:response",
|
||||
"msg": "[DEBUG] llm client response",
|
||||
"data": map[string]any{
|
||||
"url": c.url(path),
|
||||
"status": resp.StatusCode,
|
||||
},
|
||||
"ts": time.Now().UnixMilli(),
|
||||
}); err == nil {
|
||||
go http.Post("http://127.0.0.1:7777/event", "application/json", strings.NewReader(string(payload)))
|
||||
}
|
||||
// #endregion
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────
|
||||
@@ -222,6 +260,24 @@ func (c *Client) GenerateStream(messages []Message, onChunk func(string)) error
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
data, _ := io.ReadAll(resp.Body)
|
||||
// #region debug-point C:llm-non200
|
||||
if payload, err := json.Marshal(map[string]any{
|
||||
"sessionId": "knowledge-chat-401",
|
||||
"runId": "pre-fix",
|
||||
"hypothesisId": "C",
|
||||
"location": "backend-go/internal/ai/llm.go:GenerateStream:non200",
|
||||
"msg": "[DEBUG] llm stream non-200",
|
||||
"data": map[string]any{
|
||||
"status": resp.StatusCode,
|
||||
"bodyPreview": truncate(string(data), 240),
|
||||
"model": c.model,
|
||||
"baseURL": c.baseURL,
|
||||
},
|
||||
"ts": time.Now().UnixMilli(),
|
||||
}); err == nil {
|
||||
go http.Post("http://127.0.0.1:7777/event", "application/json", strings.NewReader(string(payload)))
|
||||
}
|
||||
// #endregion
|
||||
return fmt.Errorf("LLM 返回 %d: %s", resp.StatusCode, truncate(string(data), 200))
|
||||
}
|
||||
|
||||
@@ -335,6 +391,41 @@ func GenerateFullWithFallback(primary *config.RouteConfig, messages []Message) (
|
||||
return nil, nil, fmt.Errorf("所有路由均失败: %w", lastErr)
|
||||
}
|
||||
|
||||
// GenerateStreamWithFallback 流式 + 回退链:返回实际命中的路由
|
||||
func GenerateStreamWithFallback(primary *config.RouteConfig, messages []Message, onChunk func(string)) (*config.RouteConfig, error) {
|
||||
chain := []*config.RouteConfig{primary}
|
||||
if fallbacks, err := config.GetFallbackRoutes(primary.RouteID); err == nil && len(fallbacks) > 0 {
|
||||
chain = append(chain, fallbacks...)
|
||||
}
|
||||
|
||||
var lastErr error
|
||||
for _, route := range chain {
|
||||
if requiresAPIKey(route) && strings.TrimSpace(route.APIKey) == "" {
|
||||
lastErr = fmt.Errorf("[%s] 未配置 API Key", route.RouteID)
|
||||
continue
|
||||
}
|
||||
client := NewClient(route)
|
||||
if err := client.GenerateStream(messages, onChunk); err == nil {
|
||||
return route, nil
|
||||
} else {
|
||||
lastErr = fmt.Errorf("[%s] %w", route.RouteID, err)
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("所有路由均失败: %w", lastErr)
|
||||
}
|
||||
|
||||
func requiresAPIKey(route *config.RouteConfig) bool {
|
||||
if route == nil {
|
||||
return false
|
||||
}
|
||||
baseURL := strings.ToLower(strings.TrimSpace(route.BaseURL))
|
||||
if strings.Contains(baseURL, "openrouter.ai") || strings.Contains(baseURL, "openai.com") {
|
||||
return true
|
||||
}
|
||||
provider := strings.ToLower(strings.TrimSpace(route.Provider))
|
||||
return provider == "openrouter" || provider == "openai"
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────
|
||||
// 配置解析(兼容旧接口)
|
||||
// ──────────────────────────────────────────────
|
||||
@@ -375,11 +466,11 @@ func ResolveLLM(cfg *config.Config) (LLMConfig, bool) {
|
||||
}
|
||||
|
||||
c := LLMConfig{
|
||||
BaseURL: baseURL,
|
||||
APIKey: apiKey,
|
||||
Model: modelName,
|
||||
EmbedModel: embedModel,
|
||||
MaxTokens: 2048,
|
||||
BaseURL: baseURL,
|
||||
APIKey: apiKey,
|
||||
Model: modelName,
|
||||
EmbedModel: embedModel,
|
||||
MaxTokens: 2048,
|
||||
Temperature: 0.7,
|
||||
}
|
||||
if c.BaseURL == "" || c.Model == "" {
|
||||
@@ -394,4 +485,4 @@ func truncate(s string, n int) string {
|
||||
return s
|
||||
}
|
||||
return string(r[:n]) + "..."
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user