fix(captain): 修复 playground 500 — GetConfig nil pointer panic + 清理重复 buildSystemPrompt
根因:captain_assistants.config 中 temperature 字段为字符串 "0.5",
json.Unmarshal 到 float64 字段失败,GetConfig() 返回 nil,
调用方吞掉错误后 BuildAssistantPrompt 解引用 nil cfg 导致 panic。
修复:
- GetConfig() unmarshal 失败时返回空 &cfg{} 而非 nil,防止 nil dereference
- 删除 captain_assistant_service.go 中重复的 buildSystemPrompt 函数,
统一使用 SystemPromptBuilder.BuildAssistantPrompt
- generatePlaygroundLLMResponse / GenerateResponse 增加
ErrProviderNotConfigured 检查,返回 fallback 消息而非 500
This commit is contained in:
@@ -2,6 +2,7 @@ package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/pgvector/pgvector-go"
|
||||
)
|
||||
@@ -118,7 +119,10 @@ func (a *CaptainAssistant) GetConfig() (*AssistantConfig, error) {
|
||||
return &cfg, nil
|
||||
}
|
||||
if err := json.Unmarshal(a.Config, &cfg); err != nil {
|
||||
return nil, err
|
||||
// Return an empty config rather than nil so callers that ignore the
|
||||
// error do not dereference a nil pointer. The raw JSON may contain
|
||||
// type mismatches (e.g. temperature as string) from older data.
|
||||
return &cfg, fmt.Errorf("parse assistant config: %w", err)
|
||||
}
|
||||
var fields map[string]json.RawMessage
|
||||
if err := json.Unmarshal(a.Config, &fields); err == nil {
|
||||
|
||||
@@ -3,6 +3,7 @@ package service
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
@@ -26,6 +27,7 @@ type CaptainAssistantService struct {
|
||||
responseRepo *repository.CaptainAssistantResponseRepo
|
||||
llmProvider llm.Provider
|
||||
cache *redis.Client
|
||||
promptBuilder *SystemPromptBuilder
|
||||
}
|
||||
|
||||
// NewCaptainAssistantService creates a new CaptainAssistantService.
|
||||
@@ -43,6 +45,7 @@ func NewCaptainAssistantService(
|
||||
documentRepo: documentRepo,
|
||||
responseRepo: responseRepo,
|
||||
llmProvider: llmProvider,
|
||||
promptBuilder: NewSystemPromptBuilder(),
|
||||
}
|
||||
if len(cache) > 0 {
|
||||
svc.cache = cache[0]
|
||||
@@ -646,7 +649,7 @@ func (s *CaptainAssistantService) GenerateResponse(ctx context.Context, assistan
|
||||
// Build system prompt from assistant config and response guidelines
|
||||
cfg, _ := assistant.GetConfig()
|
||||
ctx = withAssistantGenerationConfig(ctx, cfg)
|
||||
systemPrompt := buildSystemPrompt(assistant, cfg)
|
||||
systemPrompt := s.promptBuilder.BuildAssistantPrompt(assistant, cfg)
|
||||
|
||||
// Build messages for LLM
|
||||
messages := []llm.ChatMessage{
|
||||
@@ -663,6 +666,10 @@ func (s *CaptainAssistantService) GenerateResponse(ctx context.Context, assistan
|
||||
|
||||
resp, err := s.llmProvider.ChatCompletion(ctx, req)
|
||||
if err != nil {
|
||||
if errors.Is(err, llm.ErrProviderNotConfigured) {
|
||||
applogger.L().Warnf("GenerateResponse: LLM provider not configured, returning fallback")
|
||||
return captainPlaygroundFallbackMessage, nil
|
||||
}
|
||||
applogger.L().Errorf("GenerateResponse LLM call: %v", err)
|
||||
return "", fmt.Errorf("llm generation failed: %w", err)
|
||||
}
|
||||
@@ -716,7 +723,7 @@ func (s *CaptainAssistantService) generatePlaygroundLLMResponse(ctx context.Cont
|
||||
|
||||
cfg, _ := assistant.GetConfig()
|
||||
ctx = withAssistantGenerationConfig(ctx, cfg)
|
||||
messages := []llm.ChatMessage{{Role: "system", Content: buildSystemPrompt(assistant, cfg)}}
|
||||
messages := []llm.ChatMessage{{Role: "system", Content: s.promptBuilder.BuildAssistantPrompt(assistant, cfg)}}
|
||||
for _, message := range history {
|
||||
if message.Role == "" || message.Content == "" {
|
||||
continue
|
||||
@@ -731,6 +738,10 @@ func (s *CaptainAssistantService) generatePlaygroundLLMResponse(ctx context.Cont
|
||||
MaxTokens: 1024,
|
||||
})
|
||||
if err != nil {
|
||||
if errors.Is(err, llm.ErrProviderNotConfigured) {
|
||||
applogger.L().Warnf("GeneratePlaygroundResponse: LLM provider not configured, returning fallback")
|
||||
return captainPlaygroundFallbackMessage, nil
|
||||
}
|
||||
applogger.L().Errorf("GeneratePlaygroundResponse LLM call: %v", err)
|
||||
return "", fmt.Errorf("llm generation failed: %w", err)
|
||||
}
|
||||
@@ -802,21 +813,3 @@ func appendAdditionalPlaygroundMessage(history []PlaygroundMessage, current stri
|
||||
}
|
||||
return append(history, PlaygroundMessage{Role: "user", Content: current})
|
||||
}
|
||||
|
||||
// buildSystemPrompt constructs the system prompt from assistant config and guidelines.
|
||||
func buildSystemPrompt(assistant *model.CaptainAssistant, cfg *model.AssistantConfig) string {
|
||||
prompt := fmt.Sprintf("You are %s, an AI assistant.", assistant.Name)
|
||||
if cfg.ProductName != "" {
|
||||
prompt += fmt.Sprintf(" You represent the product: %s.", cfg.ProductName)
|
||||
}
|
||||
if cfg.Instructions != "" {
|
||||
prompt += fmt.Sprintf("\nInstructions: %s", cfg.Instructions)
|
||||
}
|
||||
if len(assistant.ResponseGuidelines) > 0 && string(assistant.ResponseGuidelines) != "null" {
|
||||
prompt += fmt.Sprintf("\nResponse Guidelines: %s", string(assistant.ResponseGuidelines))
|
||||
}
|
||||
if len(assistant.Guardrails) > 0 && string(assistant.Guardrails) != "null" {
|
||||
prompt += fmt.Sprintf("\nGuardrails: %s", string(assistant.Guardrails))
|
||||
}
|
||||
return prompt
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user