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:
@@ -102,6 +102,9 @@ func AutoMigrate(models ...interface{}) error {
|
|||||||
if err := repairSmokeAINextSmokeIndexes(DB); err != nil {
|
if err := repairSmokeAINextSmokeIndexes(DB); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := repairSmokeAIAdviceIndexes(DB); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,3 +56,47 @@ func repairSmokeAINextSmokeIndexes(db *gorm.DB) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func repairSmokeAIAdviceIndexes(db *gorm.DB) error {
|
||||||
|
if db == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
tableName = "fa_smoke_ai_advice"
|
||||||
|
indexName = "uniq_smoke_ai_advice"
|
||||||
|
)
|
||||||
|
|
||||||
|
var rows []mysqlIndexColumn
|
||||||
|
if err := db.Raw(fmt.Sprintf("SHOW INDEX FROM `%s` WHERE Key_name = ?", tableName), indexName).Scan(&rows).Error; err != nil {
|
||||||
|
return fmt.Errorf("inspect %s: %w", indexName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := []string{"uid", "type", "advice_date", "prompt_version"}
|
||||||
|
actual := make([]string, 0, len(rows))
|
||||||
|
for _, row := range rows {
|
||||||
|
actual = append(actual, row.ColumnName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if slices.Equal(actual, expected) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(rows) > 0 {
|
||||||
|
if err := db.Exec(fmt.Sprintf("ALTER TABLE `%s` DROP INDEX `%s`", tableName, indexName)).Error; err != nil {
|
||||||
|
return fmt.Errorf("drop %s: %w", indexName, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.Exec(
|
||||||
|
fmt.Sprintf(
|
||||||
|
"ALTER TABLE `%s` ADD UNIQUE KEY `%s` (`uid`,`type`,`advice_date`,`prompt_version`)",
|
||||||
|
tableName,
|
||||||
|
indexName,
|
||||||
|
),
|
||||||
|
).Error; err != nil {
|
||||||
|
return fmt.Errorf("create %s: %w", indexName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -79,3 +79,54 @@ func TestRepairSmokeAINextSmokeIndexesKeepsCorrectIndex(t *testing.T) {
|
|||||||
t.Fatalf("unmet expectations: %v", err)
|
t.Fatalf("unmet expectations: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRepairSmokeAIAdviceIndexesRecreatesBrokenIndex(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
db, mock, cleanup := newMockDB(t)
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
mock.ExpectQuery("SHOW INDEX FROM `fa_smoke_ai_advice` WHERE Key_name = \\?").
|
||||||
|
WithArgs("uniq_smoke_ai_advice").
|
||||||
|
WillReturnRows(
|
||||||
|
sqlmock.NewRows([]string{"Key_name", "Seq_in_index", "Column_name"}).
|
||||||
|
AddRow("uniq_smoke_ai_advice", 1, "uid").
|
||||||
|
AddRow("uniq_smoke_ai_advice", 2, "advice_date").
|
||||||
|
AddRow("uniq_smoke_ai_advice", 3, "prompt_version"),
|
||||||
|
)
|
||||||
|
mock.ExpectExec("ALTER TABLE `fa_smoke_ai_advice` DROP INDEX `uniq_smoke_ai_advice`").
|
||||||
|
WillReturnResult(sqlmock.NewResult(0, 0))
|
||||||
|
mock.ExpectExec("ALTER TABLE `fa_smoke_ai_advice` ADD UNIQUE KEY `uniq_smoke_ai_advice` \\(`uid`,`type`,`advice_date`,`prompt_version`\\)").
|
||||||
|
WillReturnResult(sqlmock.NewResult(0, 0))
|
||||||
|
|
||||||
|
if err := repairSmokeAIAdviceIndexes(db); err != nil {
|
||||||
|
t.Fatalf("repairSmokeAIAdviceIndexes: %v", err)
|
||||||
|
}
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Fatalf("unmet expectations: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRepairSmokeAIAdviceIndexesKeepsCorrectIndex(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
db, mock, cleanup := newMockDB(t)
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
mock.ExpectQuery("SHOW INDEX FROM `fa_smoke_ai_advice` WHERE Key_name = \\?").
|
||||||
|
WithArgs("uniq_smoke_ai_advice").
|
||||||
|
WillReturnRows(
|
||||||
|
sqlmock.NewRows([]string{"Key_name", "Seq_in_index", "Column_name"}).
|
||||||
|
AddRow("uniq_smoke_ai_advice", 1, "uid").
|
||||||
|
AddRow("uniq_smoke_ai_advice", 2, "type").
|
||||||
|
AddRow("uniq_smoke_ai_advice", 3, "advice_date").
|
||||||
|
AddRow("uniq_smoke_ai_advice", 4, "prompt_version"),
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := repairSmokeAIAdviceIndexes(db); err != nil {
|
||||||
|
t.Fatalf("repairSmokeAIAdviceIndexes: %v", err)
|
||||||
|
}
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Fatalf("unmet expectations: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ func (s *SmokeAIAdviceService) GetOrGenerate(ctx context.Context, user *usermode
|
|||||||
return nil, err
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -338,10 +338,11 @@ func loadAdviceUserProfile(ctx context.Context, db *gorm.DB, uid int) *adviceUse
|
|||||||
return &out
|
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 == "" {
|
if s.cfg.APIKey == "" || s.cfg.Model == "" || s.cfg.BaseURL == "" {
|
||||||
return "", "", nil, nil, ErrAIServiceDisabled
|
return "", "", nil, nil, ErrAIServiceDisabled
|
||||||
}
|
}
|
||||||
|
requestModel := preferredSmokeAIModel(s.cfg.Model)
|
||||||
|
|
||||||
systemPrompt := strings.TrimSpace(`
|
systemPrompt := strings.TrimSpace(`
|
||||||
你是一名专业的戒烟教练与行为改变顾问。你需要基于用户昨天的抽烟总量与时间节点,给出可执行、可量化的戒烟/控烟建议。
|
你是一名专业的戒烟教练与行为改变顾问。你需要基于用户昨天的抽烟总量与时间节点,给出可执行、可量化的戒烟/控烟建议。
|
||||||
@@ -358,9 +359,16 @@ func (s *SmokeAIAdviceService) callAI(ctx context.Context, snap adviceSnapshot)
|
|||||||
`)
|
`)
|
||||||
|
|
||||||
userPrompt := fmt.Sprintf("用户昨日数据(JSON):\n%s", mustJSON(snap))
|
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{
|
reqBody := chatCompletionRequest{
|
||||||
Model: s.cfg.Model,
|
Model: requestModel,
|
||||||
Messages: []chatMessage{
|
Messages: []chatMessage{
|
||||||
{Role: "system", Content: systemPrompt},
|
{Role: "system", Content: systemPrompt},
|
||||||
{Role: "user", Content: userPrompt},
|
{Role: "user", Content: userPrompt},
|
||||||
@@ -391,6 +399,12 @@ func (s *SmokeAIAdviceService) callAI(ctx context.Context, snap adviceSnapshot)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", nil, nil, fmt.Errorf("read ai response: %w", err)
|
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 {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return "", "", nil, nil, fmt.Errorf("ai http %d: %s", resp.StatusCode, truncateString(string(body), 512))
|
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 == "" {
|
if content == "" {
|
||||||
return "", "", nil, nil, errors.New("ai response content is empty")
|
return "", "", nil, nil, errors.New("ai response content is empty")
|
||||||
}
|
}
|
||||||
|
if jsonPart := extractJSONObject(content); jsonPart != "" {
|
||||||
|
content = jsonPart
|
||||||
|
}
|
||||||
|
|
||||||
modelName := parsed.Model
|
modelName := parsed.Model
|
||||||
if modelName == "" {
|
if modelName == "" {
|
||||||
modelName = s.cfg.Model
|
modelName = requestModel
|
||||||
}
|
}
|
||||||
|
|
||||||
var tokensIn, tokensOut *int
|
var tokensIn, tokensOut *int
|
||||||
@@ -518,7 +535,7 @@ func (s *SmokeAIAdviceService) GetOrGenerateDailySummary(
|
|||||||
return nil, err
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -545,10 +562,11 @@ func (s *SmokeAIAdviceService) GetOrGenerateDailySummary(
|
|||||||
return &record, nil
|
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 == "" {
|
if s.cfg.APIKey == "" || s.cfg.Model == "" || s.cfg.BaseURL == "" {
|
||||||
return "", "", nil, nil, ErrAIServiceDisabled
|
return "", "", nil, nil, ErrAIServiceDisabled
|
||||||
}
|
}
|
||||||
|
requestModel := preferredSmokeAIModel(s.cfg.Model)
|
||||||
|
|
||||||
systemPrompt := strings.TrimSpace(`
|
systemPrompt := strings.TrimSpace(`
|
||||||
你是一名专业的戒烟教练。请基于用户今天的抽烟数据,生成一份简洁的每日总结。
|
你是一名专业的戒烟教练。请基于用户今天的抽烟数据,生成一份简洁的每日总结。
|
||||||
@@ -567,9 +585,16 @@ func (s *SmokeAIAdviceService) callAIDailySummary(ctx context.Context, snap advi
|
|||||||
`)
|
`)
|
||||||
|
|
||||||
userPrompt := fmt.Sprintf("用户今日抽烟数据(JSON):\n%s", mustJSON(snap))
|
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{
|
reqBody := chatCompletionRequest{
|
||||||
Model: s.cfg.Model,
|
Model: requestModel,
|
||||||
Messages: []chatMessage{
|
Messages: []chatMessage{
|
||||||
{Role: "system", Content: systemPrompt},
|
{Role: "system", Content: systemPrompt},
|
||||||
{Role: "user", Content: userPrompt},
|
{Role: "user", Content: userPrompt},
|
||||||
@@ -600,6 +625,12 @@ func (s *SmokeAIAdviceService) callAIDailySummary(ctx context.Context, snap advi
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", nil, nil, fmt.Errorf("read ai response: %w", err)
|
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 {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return "", "", nil, nil, fmt.Errorf("ai http %d: %s", resp.StatusCode, truncateString(string(body), 512))
|
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
|
modelName := parsed.Model
|
||||||
if modelName == "" {
|
if modelName == "" {
|
||||||
modelName = s.cfg.Model
|
modelName = requestModel
|
||||||
}
|
}
|
||||||
|
|
||||||
var tokensIn, tokensOut *int
|
var tokensIn, tokensOut *int
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const smokeAIDebugLogDir = "/www/wwwroot/code/wx_service/runtime_logs"
|
||||||
|
|
||||||
|
func smokeAIDebugLogPath(now time.Time) string {
|
||||||
|
day := now.In(time.Local).Format("2006-01-02")
|
||||||
|
return filepath.Join(smokeAIDebugLogDir, fmt.Sprintf("smoke_ai_%s.log", day))
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendSmokeAIDebugLog(scene string, payload map[string]interface{}) {
|
||||||
|
if payload == nil {
|
||||||
|
payload = map[string]interface{}{}
|
||||||
|
}
|
||||||
|
now := time.Now().In(time.Local)
|
||||||
|
payload["scene"] = scene
|
||||||
|
payload["ts"] = now.Format(time.RFC3339)
|
||||||
|
|
||||||
|
body, err := json.MarshalIndent(payload, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[smoke_ai_debug] marshal failed scene=%s err=%v", scene, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.MkdirAll(smokeAIDebugLogDir, 0o755); err != nil {
|
||||||
|
log.Printf("[smoke_ai_debug] mkdir failed scene=%s err=%v", scene, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.OpenFile(smokeAIDebugLogPath(now), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o644)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[smoke_ai_debug] open failed scene=%s err=%v", scene, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
if _, err := f.Write(append(body, '\n', '\n')); err != nil {
|
||||||
|
log.Printf("[smoke_ai_debug] write failed scene=%s err=%v", scene, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// preferredSmokeAIModel 避免把高延迟 reasoning 模型用于首页/计划类交互接口。
|
||||||
|
func preferredSmokeAIModel(model string) string {
|
||||||
|
normalized := strings.TrimSpace(model)
|
||||||
|
lower := strings.ToLower(normalized)
|
||||||
|
if strings.Contains(lower, "deepseek") && strings.Contains(lower, "reasoner") {
|
||||||
|
return "deepseek-chat"
|
||||||
|
}
|
||||||
|
return normalized
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestPreferredSmokeAIModel(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{name: "deepseek reasoner fallback", input: "deepseek-reasoner", want: "deepseek-chat"},
|
||||||
|
{name: "trim whitespace", input: " deepseek-reasoner ", want: "deepseek-chat"},
|
||||||
|
{name: "keep normal model", input: "gpt-4o-mini", want: "gpt-4o-mini"},
|
||||||
|
{name: "keep deepseek chat", input: "deepseek-chat", want: "deepseek-chat"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
tc := tc
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
if got := preferredSmokeAIModel(tc.input); got != tc.want {
|
||||||
|
t.Fatalf("preferredSmokeAIModel(%q)=%q, want=%q", tc.input, got, tc.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -154,7 +154,7 @@ func (s *SmokeAINextSmokeService) GetOrGenerate(ctx context.Context, user *userm
|
|||||||
}
|
}
|
||||||
inputJSON, _ := json.Marshal(input)
|
inputJSON, _ := json.Marshal(input)
|
||||||
|
|
||||||
output, outputJSON, modelName, tokensIn, tokensOut, err := s.callAI(ctx, input)
|
output, outputJSON, modelName, tokensIn, tokensOut, err := s.callAI(ctx, int(user.ID), input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return AINextSmokeSuggestion{}, err
|
return AINextSmokeSuggestion{}, err
|
||||||
}
|
}
|
||||||
@@ -249,6 +249,14 @@ func (s *SmokeAINextSmokeService) GetOrGenerate(ctx context.Context, user *userm
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return AINextSmokeSuggestion{}, err
|
return AINextSmokeSuggestion{}, err
|
||||||
}
|
}
|
||||||
|
if len(nodes) > 0 {
|
||||||
|
if firstNodeAt, err := parseFlexibleTime(nodes[0], planDate); err == nil {
|
||||||
|
suggestedAt = firstNodeAt.In(time.Local)
|
||||||
|
if suggestedAt.Before(notBeforeAt) {
|
||||||
|
suggestedAt = notBeforeAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if cachedAdvice != nil {
|
if cachedAdvice != nil {
|
||||||
if err := s.db.WithContext(ctx).
|
if err := s.db.WithContext(ctx).
|
||||||
@@ -612,7 +620,9 @@ func (s *SmokeAINextSmokeService) saveNodes(ctx context.Context, uid int, planDa
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SmokeAINextSmokeService) callAI(ctx context.Context, input aiNextSmokeInput) (aiNextSmokeOutput, []byte, string, *int, *int, error) {
|
func (s *SmokeAINextSmokeService) callAI(ctx context.Context, uid int, input aiNextSmokeInput) (aiNextSmokeOutput, []byte, string, *int, *int, error) {
|
||||||
|
requestModel := preferredSmokeAIModel(s.cfg.Model)
|
||||||
|
|
||||||
systemPrompt := strings.TrimSpace(`
|
systemPrompt := strings.TrimSpace(`
|
||||||
你是一名专业的戒烟教练与行为改变顾问。你将收到一段 JSON,包含:
|
你是一名专业的戒烟教练与行为改变顾问。你将收到一段 JSON,包含:
|
||||||
- 现在时间(as_of)
|
- 现在时间(as_of)
|
||||||
@@ -638,9 +648,16 @@ func (s *SmokeAINextSmokeService) callAI(ctx context.Context, input aiNextSmokeI
|
|||||||
`)
|
`)
|
||||||
|
|
||||||
userPrompt := fmt.Sprintf("输入(JSON):\n%s", mustJSON(input))
|
userPrompt := fmt.Sprintf("输入(JSON):\n%s", mustJSON(input))
|
||||||
|
appendSmokeAIDebugLog("next_smoke.request", map[string]interface{}{
|
||||||
|
"uid": uid,
|
||||||
|
"model": requestModel,
|
||||||
|
"system_prompt": systemPrompt,
|
||||||
|
"user_prompt": userPrompt,
|
||||||
|
"input": input,
|
||||||
|
})
|
||||||
|
|
||||||
reqBody := chatCompletionRequest{
|
reqBody := chatCompletionRequest{
|
||||||
Model: s.cfg.Model,
|
Model: requestModel,
|
||||||
Messages: []chatMessage{
|
Messages: []chatMessage{
|
||||||
{Role: "system", Content: systemPrompt},
|
{Role: "system", Content: systemPrompt},
|
||||||
{Role: "user", Content: userPrompt},
|
{Role: "user", Content: userPrompt},
|
||||||
@@ -671,6 +688,12 @@ func (s *SmokeAINextSmokeService) callAI(ctx context.Context, input aiNextSmokeI
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return aiNextSmokeOutput{}, nil, "", nil, nil, fmt.Errorf("read ai response: %w", err)
|
return aiNextSmokeOutput{}, nil, "", nil, nil, fmt.Errorf("read ai response: %w", err)
|
||||||
}
|
}
|
||||||
|
appendSmokeAIDebugLog("next_smoke.response", map[string]interface{}{
|
||||||
|
"uid": uid,
|
||||||
|
"model": requestModel,
|
||||||
|
"http_status": resp.StatusCode,
|
||||||
|
"response_body": string(body),
|
||||||
|
})
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return aiNextSmokeOutput{}, nil, "", nil, nil, fmt.Errorf("ai http %d: %s", resp.StatusCode, truncateString(string(body), 512))
|
return aiNextSmokeOutput{}, nil, "", nil, nil, fmt.Errorf("ai http %d: %s", resp.StatusCode, truncateString(string(body), 512))
|
||||||
}
|
}
|
||||||
@@ -700,7 +723,7 @@ func (s *SmokeAINextSmokeService) callAI(ctx context.Context, input aiNextSmokeI
|
|||||||
|
|
||||||
modelName := parsed.Model
|
modelName := parsed.Model
|
||||||
if modelName == "" {
|
if modelName == "" {
|
||||||
modelName = s.cfg.Model
|
modelName = requestModel
|
||||||
}
|
}
|
||||||
|
|
||||||
var tokensIn, tokensOut *int
|
var tokensIn, tokensOut *int
|
||||||
|
|||||||
Reference in New Issue
Block a user