package httpapi import ( "encoding/json" "errors" "log/slog" "net/http" "strings" "data-terminal-cloud/internal/memorytool" ) func MemoryToolSearchHandler(service *memorytool.Service, signer *memorytool.TokenSigner, limiter RateLimiter, logger *slog.Logger) http.Handler { if logger == nil { logger = slog.Default() } return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { authorization := strings.TrimSpace(request.Header.Get("Authorization")) if !strings.HasPrefix(authorization, "Bearer ") { WriteError(writer, request, http.StatusUnauthorized, "MEMORY_TOOL_AUTHENTICATION_FAILED", "记忆工具身份验证失败") return } claims, err := signer.Verify(strings.TrimSpace(strings.TrimPrefix(authorization, "Bearer "))) if err != nil { WriteError(writer, request, http.StatusUnauthorized, "MEMORY_TOOL_AUTHENTICATION_FAILED", "记忆工具身份验证失败") return } if limiter != nil && !allowScopes(writer, request, logger, limiter, [][2]string{{"memory_tool", claims.ConversationID}}) { return } decoder := json.NewDecoder(request.Body) decoder.DisallowUnknownFields() var input struct { Query string `json:"query"` Limit int `json:"limit"` } if err := decoder.Decode(&input); err != nil || ensureJSONEOF(decoder) != nil { WriteError(writer, request, http.StatusBadRequest, "MEMORY_TOOL_INVALID_REQUEST", "记忆检索参数不正确") return } input.Query = strings.TrimSpace(input.Query) if input.Limit == 0 { input.Limit = 3 } if input.Query == "" || len([]rune(input.Query)) > 500 || input.Limit < 1 || input.Limit > 5 { WriteError(writer, request, http.StatusBadRequest, "MEMORY_TOOL_INVALID_REQUEST", "记忆检索参数不正确") return } result, err := service.Search(request.Context(), claims, input.Query, input.Limit, RequestID(request.Context())) if err != nil { switch { case errors.Is(err, memorytool.ErrConversationUnavailable): WriteError(writer, request, http.StatusConflict, "MEMORY_TOOL_CONVERSATION_UNAVAILABLE", "本次通话当前不能使用记忆检索") case errors.Is(err, memorytool.ErrEngineModeUnsupported): WriteError(writer, request, http.StatusConflict, "MEMORY_TOOL_ENGINE_MODE_UNSUPPORTED", "本次语音处理方式未启用通话内记忆工具") default: slog.ErrorContext(request.Context(), "memory tool search failed", "conversation_id", claims.ConversationID, "error", err) WriteAPIError(writer, request, http.StatusServiceUnavailable, Error{Code: "MEMORY_TOOL_UNAVAILABLE", Message: "记忆检索暂时不可用,请继续普通交流", Retryable: true}) } return } WriteJSON(writer, request, http.StatusOK, result) }) }