package eino import ( "context" "encoding/base64" "github.com/cloudwego/eino/compose" "github.com/cloudwego/eino/schema" "github.com/hhs/camtalk/internal/ai/llm" "github.com/hhs/camtalk/internal/logger" "github.com/hhs/camtalk/internal/models" ) // HistoryInput 历史组装节点的输入,包含 STT 输出和原始请求信息。 type HistoryInput struct { STTOutput *STTOutput SessionID string RequestID string ImageData []byte Scenario string DetailLevel string } // NewHistoryLambda 创建历史组装 Lambda 节点。 // 输入: HistoryInput → 输出: []*schema.Message // // 构建系统提示词,组装历史消息和当前用户输入(含多模态图片)。 func NewHistoryLambda(historyFetcher func(ctx context.Context, sessionID string, maxHistory int) ([]models.Message, error), maxHistory int) *compose.Lambda { return compose.InvokableLambda(func(ctx context.Context, input *HistoryInput) ([]*schema.Message, error) { log := logger.Log requestID := input.RequestID // 构建系统提示词 scenarioPrompt := llm.GetScenarioPrompt(input.Scenario, input.STTOutput.Language) systemPrompt := llm.BuildSystemPrompt(input.STTOutput.Language, input.DetailLevel, scenarioPrompt) // 构建 system message(含图片) systemMsg := &schema.Message{ Role: schema.System, Content: systemPrompt, } // 如果有图片,添加到 system message 的多模态内容中 if len(input.ImageData) > 0 { base64Str := base64.StdEncoding.EncodeToString(input.ImageData) mimeType := detectImageMimeType(input.ImageData) systemMsg.UserInputMultiContent = []schema.MessageInputPart{ { Type: schema.ChatMessagePartTypeImageURL, Image: &schema.MessageInputImage{ MessagePartCommon: schema.MessagePartCommon{ Base64Data: &base64Str, MIMEType: mimeType, }, Detail: schema.ImageURLDetailAuto, }, }, } } messages := []*schema.Message{systemMsg} // 获取并追加历史消息 if historyFetcher != nil && input.SessionID != "" { history, err := historyFetcher(ctx, input.SessionID, maxHistory) if err != nil { log.Warnw("获取历史消息失败,继续处理", "error", err, "request_id", requestID) } else { for _, msg := range history { messages = append(messages, &schema.Message{ Role: schema.RoleType(msg.Role), Content: msg.Content, }) } } } // 追加当前用户输入 messages = append(messages, &schema.Message{ Role: schema.User, Content: input.STTOutput.Text, }) log.Infow("历史组装完成", "request_id", requestID, "message_count", len(messages), "has_image", len(input.ImageData) > 0, "scenario", input.Scenario) return messages, nil }) } // detectImageMimeType 简单检测图片 MIME 类型。 func detectImageMimeType(data []byte) string { if len(data) < 4 { return "image/jpeg" } // JPEG: FF D8 FF if data[0] == 0xFF && data[1] == 0xD8 && data[2] == 0xFF { return "image/jpeg" } // PNG: 89 50 4E 47 if data[0] == 0x89 && data[1] == 0x50 && data[2] == 0x4E && data[3] == 0x47 { return "image/png" } // GIF: 47 49 46 38 if data[0] == 0x47 && data[1] == 0x49 && data[2] == 0x46 { return "image/gif" } // WebP: 52 49 46 46 if data[0] == 0x52 && data[1] == 0x49 && data[2] == 0x46 && data[3] == 0x46 { return "image/webp" } return "image/jpeg" // 默认 }