mirror of
https://github.com/CJackHwang/ds2api.git
synced 2026-05-05 00:45:29 +08:00
- ValidateTurn no longer errors on thinking-only responses, deferring to ShouldRetryEmptyOutput which now also covers thinking-only outputs. - Empty output retry uses multi-turn follow-up with a regeneration prompt suffix and parent_message_id in the same DeepSeek session. - Centralize StripReferenceMarkersEnabled into textclean package to eliminate duplicated hardcoded booleans across 4 protocol handlers. - Log a deprecation warning when the legacy "compat" config key is used. - Document thinking-only retry and reference marker stripping in API.md. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
117 lines
3.2 KiB
Go
117 lines
3.2 KiB
Go
package responses
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"sync"
|
|
|
|
"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/textclean"
|
|
"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
|
|
|
|
responsesMu sync.Mutex
|
|
responses *responseStore
|
|
}
|
|
|
|
func stripReferenceMarkersEnabled() bool {
|
|
return textclean.StripReferenceMarkersEnabled()
|
|
}
|
|
|
|
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 writeOpenAIErrorWithCode(w http.ResponseWriter, status int, message, code string) {
|
|
shared.WriteOpenAIErrorWithCode(w, status, message, code)
|
|
}
|
|
|
|
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 cleanVisibleOutput(text string, stripReferenceMarkers bool) string {
|
|
return shared.CleanVisibleOutput(text, stripReferenceMarkers)
|
|
}
|
|
|
|
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 filterIncrementalToolCallDeltasByAllowed(deltas []toolstream.ToolCallDelta, seenNames map[int]string) []toolstream.ToolCallDelta {
|
|
return shared.FilterIncrementalToolCallDeltasByAllowed(deltas, seenNames)
|
|
}
|