feat(database): add repairSmokeAIAdviceIndexes function and corresponding tests
- Implemented repairSmokeAIAdviceIndexes to manage the unique index for fa_smoke_ai_advice. - Added unit tests for the new function to ensure correct index recreation and validation. - Updated AutoMigrate to include the new index repair function.
This commit is contained in:
@@ -112,7 +112,7 @@ func (s *SmokeAIAdviceService) GetOrGenerate(ctx context.Context, user *usermode
|
||||
return nil, err
|
||||
}
|
||||
|
||||
adviceText, modelName, tokensIn, tokensOut, err := s.callAI(ctx, snapshot)
|
||||
adviceText, modelName, tokensIn, tokensOut, err := s.callAI(ctx, int(user.ID), snapshot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -338,10 +338,11 @@ func loadAdviceUserProfile(ctx context.Context, db *gorm.DB, uid int) *adviceUse
|
||||
return &out
|
||||
}
|
||||
|
||||
func (s *SmokeAIAdviceService) callAI(ctx context.Context, snap adviceSnapshot) (string, string, *int, *int, error) {
|
||||
func (s *SmokeAIAdviceService) callAI(ctx context.Context, uid int, snap adviceSnapshot) (string, string, *int, *int, error) {
|
||||
if s.cfg.APIKey == "" || s.cfg.Model == "" || s.cfg.BaseURL == "" {
|
||||
return "", "", nil, nil, ErrAIServiceDisabled
|
||||
}
|
||||
requestModel := preferredSmokeAIModel(s.cfg.Model)
|
||||
|
||||
systemPrompt := strings.TrimSpace(`
|
||||
你是一名专业的戒烟教练与行为改变顾问。你需要基于用户昨天的抽烟总量与时间节点,给出可执行、可量化的戒烟/控烟建议。
|
||||
@@ -358,9 +359,16 @@ func (s *SmokeAIAdviceService) callAI(ctx context.Context, snap adviceSnapshot)
|
||||
`)
|
||||
|
||||
userPrompt := fmt.Sprintf("用户昨日数据(JSON):\n%s", mustJSON(snap))
|
||||
appendSmokeAIDebugLog("daily_advice.request", map[string]interface{}{
|
||||
"uid": uid,
|
||||
"model": requestModel,
|
||||
"system_prompt": systemPrompt,
|
||||
"user_prompt": userPrompt,
|
||||
"input": snap,
|
||||
})
|
||||
|
||||
reqBody := chatCompletionRequest{
|
||||
Model: s.cfg.Model,
|
||||
Model: requestModel,
|
||||
Messages: []chatMessage{
|
||||
{Role: "system", Content: systemPrompt},
|
||||
{Role: "user", Content: userPrompt},
|
||||
@@ -391,6 +399,12 @@ func (s *SmokeAIAdviceService) callAI(ctx context.Context, snap adviceSnapshot)
|
||||
if err != nil {
|
||||
return "", "", nil, nil, fmt.Errorf("read ai response: %w", err)
|
||||
}
|
||||
appendSmokeAIDebugLog("daily_advice.response", map[string]interface{}{
|
||||
"uid": uid,
|
||||
"model": requestModel,
|
||||
"http_status": resp.StatusCode,
|
||||
"response_body": string(body),
|
||||
})
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return "", "", nil, nil, fmt.Errorf("ai http %d: %s", resp.StatusCode, truncateString(string(body), 512))
|
||||
}
|
||||
@@ -407,10 +421,13 @@ func (s *SmokeAIAdviceService) callAI(ctx context.Context, snap adviceSnapshot)
|
||||
if content == "" {
|
||||
return "", "", nil, nil, errors.New("ai response content is empty")
|
||||
}
|
||||
if jsonPart := extractJSONObject(content); jsonPart != "" {
|
||||
content = jsonPart
|
||||
}
|
||||
|
||||
modelName := parsed.Model
|
||||
if modelName == "" {
|
||||
modelName = s.cfg.Model
|
||||
modelName = requestModel
|
||||
}
|
||||
|
||||
var tokensIn, tokensOut *int
|
||||
@@ -518,7 +535,7 @@ func (s *SmokeAIAdviceService) GetOrGenerateDailySummary(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
adviceText, modelName, tokensIn, tokensOut, err := s.callAIDailySummary(ctx, snapshot)
|
||||
adviceText, modelName, tokensIn, tokensOut, err := s.callAIDailySummary(ctx, int(user.ID), snapshot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -545,10 +562,11 @@ func (s *SmokeAIAdviceService) GetOrGenerateDailySummary(
|
||||
return &record, nil
|
||||
}
|
||||
|
||||
func (s *SmokeAIAdviceService) callAIDailySummary(ctx context.Context, snap adviceSnapshot) (string, string, *int, *int, error) {
|
||||
func (s *SmokeAIAdviceService) callAIDailySummary(ctx context.Context, uid int, snap adviceSnapshot) (string, string, *int, *int, error) {
|
||||
if s.cfg.APIKey == "" || s.cfg.Model == "" || s.cfg.BaseURL == "" {
|
||||
return "", "", nil, nil, ErrAIServiceDisabled
|
||||
}
|
||||
requestModel := preferredSmokeAIModel(s.cfg.Model)
|
||||
|
||||
systemPrompt := strings.TrimSpace(`
|
||||
你是一名专业的戒烟教练。请基于用户今天的抽烟数据,生成一份简洁的每日总结。
|
||||
@@ -567,9 +585,16 @@ func (s *SmokeAIAdviceService) callAIDailySummary(ctx context.Context, snap advi
|
||||
`)
|
||||
|
||||
userPrompt := fmt.Sprintf("用户今日抽烟数据(JSON):\n%s", mustJSON(snap))
|
||||
appendSmokeAIDebugLog("daily_summary.request", map[string]interface{}{
|
||||
"uid": uid,
|
||||
"model": requestModel,
|
||||
"system_prompt": systemPrompt,
|
||||
"user_prompt": userPrompt,
|
||||
"input": snap,
|
||||
})
|
||||
|
||||
reqBody := chatCompletionRequest{
|
||||
Model: s.cfg.Model,
|
||||
Model: requestModel,
|
||||
Messages: []chatMessage{
|
||||
{Role: "system", Content: systemPrompt},
|
||||
{Role: "user", Content: userPrompt},
|
||||
@@ -600,6 +625,12 @@ func (s *SmokeAIAdviceService) callAIDailySummary(ctx context.Context, snap advi
|
||||
if err != nil {
|
||||
return "", "", nil, nil, fmt.Errorf("read ai response: %w", err)
|
||||
}
|
||||
appendSmokeAIDebugLog("daily_summary.response", map[string]interface{}{
|
||||
"uid": uid,
|
||||
"model": requestModel,
|
||||
"http_status": resp.StatusCode,
|
||||
"response_body": string(body),
|
||||
})
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return "", "", nil, nil, fmt.Errorf("ai http %d: %s", resp.StatusCode, truncateString(string(body), 512))
|
||||
}
|
||||
@@ -619,7 +650,7 @@ func (s *SmokeAIAdviceService) callAIDailySummary(ctx context.Context, snap advi
|
||||
|
||||
modelName := parsed.Model
|
||||
if modelName == "" {
|
||||
modelName = s.cfg.Model
|
||||
modelName = requestModel
|
||||
}
|
||||
|
||||
var tokensIn, tokensOut *int
|
||||
|
||||
Reference in New Issue
Block a user