65 lines
2.7 KiB
Go
65 lines
2.7 KiB
Go
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)
|
||
|
|
})
|
||
|
|
}
|