Files
ds2api/internal/httpapi/openai/chat/handler.go
2026-04-28 13:38:57 +08:00

154 lines
4.5 KiB
Go

package chat
import (
"context"
"net/http"
"sync"
"time"
"ds2api/internal/auth"
"ds2api/internal/chathistory"
"ds2api/internal/httpapi/openai/files"
"ds2api/internal/httpapi/openai/history"
"ds2api/internal/httpapi/openai/shared"
"ds2api/internal/promptcompat"
"ds2api/internal/toolcall"
"ds2api/internal/toolstream"
)
const openAIGeneralMaxSize = shared.GeneralMaxSize
var writeJSON = shared.WriteJSON
type Handler struct {
Store shared.ConfigReader
Auth shared.AuthResolver
DS shared.DeepSeekCaller
ChatHistory *chathistory.Store
leaseMu sync.Mutex
streamLeases map[string]streamLease
}
type streamLease struct {
Auth *auth.RequestAuth
ExpiresAt time.Time
}
func (h *Handler) compatStripReferenceMarkers() bool {
if h == nil {
return true
}
return shared.CompatStripReferenceMarkers(h.Store)
}
func (h *Handler) applyCurrentInputFile(ctx context.Context, a *auth.RequestAuth, stdReq promptcompat.StandardRequest) (promptcompat.StandardRequest, error) {
if h == nil {
return stdReq, nil
}
stdReq = shared.ApplyThinkingInjection(h.Store, stdReq)
svc := history.Service{Store: h.Store, DS: h.DS}
out, err := svc.ApplyCurrentInputFile(ctx, a, stdReq)
if err != nil || out.CurrentInputFileApplied {
return out, err
}
return out, nil
}
func (h *Handler) preprocessInlineFileInputs(ctx context.Context, a *auth.RequestAuth, req map[string]any) error {
if h == nil {
return nil
}
return (&files.Handler{Store: h.Store, Auth: h.Auth, DS: h.DS, ChatHistory: h.ChatHistory}).PreprocessInlineFileInputs(ctx, a, req)
}
func (h *Handler) toolcallFeatureMatchEnabled() bool {
if h == nil {
return shared.ToolcallFeatureMatchEnabled(nil)
}
return shared.ToolcallFeatureMatchEnabled(h.Store)
}
func (h *Handler) toolcallEarlyEmitHighConfidence() bool {
if h == nil {
return shared.ToolcallEarlyEmitHighConfidence(nil)
}
return shared.ToolcallEarlyEmitHighConfidence(h.Store)
}
func writeOpenAIError(w http.ResponseWriter, status int, message string) {
shared.WriteOpenAIError(w, status, message)
}
func openAIErrorType(status int) string {
return shared.OpenAIErrorType(status)
}
func writeOpenAIInlineFileError(w http.ResponseWriter, err error) {
files.WriteInlineFileError(w, err)
}
func mapCurrentInputFileError(err error) (int, string) {
return history.MapError(err)
}
func requestTraceID(r *http.Request) string {
return shared.RequestTraceID(r)
}
func asString(v any) string {
return shared.AsString(v)
}
func cleanVisibleOutput(text string, stripReferenceMarkers bool) string {
return shared.CleanVisibleOutput(text, stripReferenceMarkers)
}
func replaceCitationMarkersWithLinks(text string, links map[int]string) string {
return shared.ReplaceCitationMarkersWithLinks(text, links)
}
func shouldWriteUpstreamEmptyOutputError(text string) bool {
return shared.ShouldWriteUpstreamEmptyOutputError(text)
}
func upstreamEmptyOutputDetail(contentFilter bool, text, thinking string) (int, string, string) {
return shared.UpstreamEmptyOutputDetail(contentFilter, text, thinking)
}
func writeUpstreamEmptyOutputError(w http.ResponseWriter, text, thinking string, contentFilter bool) bool {
return shared.WriteUpstreamEmptyOutputError(w, text, thinking, contentFilter)
}
func emptyOutputRetryEnabled() bool {
return shared.EmptyOutputRetryEnabled()
}
func emptyOutputRetryMaxAttempts() int {
return shared.EmptyOutputRetryMaxAttempts()
}
func clonePayloadForEmptyOutputRetry(payload map[string]any, parentMessageID int) map[string]any {
return shared.ClonePayloadForEmptyOutputRetry(payload, parentMessageID)
}
func usagePromptWithEmptyOutputRetry(originalPrompt string, retryAttempts int) string {
return shared.UsagePromptWithEmptyOutputRetry(originalPrompt, retryAttempts)
}
func formatIncrementalStreamToolCallDeltas(deltas []toolstream.ToolCallDelta, ids map[int]string) []map[string]any {
return shared.FormatIncrementalStreamToolCallDeltas(deltas, ids)
}
func filterIncrementalToolCallDeltasByAllowed(deltas []toolstream.ToolCallDelta, seenNames map[int]string) []toolstream.ToolCallDelta {
return shared.FilterIncrementalToolCallDeltasByAllowed(deltas, seenNames)
}
func formatFinalStreamToolCallsWithStableIDs(calls []toolcall.ParsedToolCall, ids map[int]string, toolsRaw any) []map[string]any {
return shared.FormatFinalStreamToolCallsWithStableIDs(calls, ids, toolsRaw)
}
func detectAssistantToolCalls(text, exposedThinking, detectionThinking string, toolNames []string) toolcall.ToolCallParseResult {
return shared.DetectAssistantToolCalls(text, exposedThinking, detectionThinking, toolNames)
}