mirror of
https://github.com/CJackHwang/ds2api.git
synced 2026-05-16 22:25:15 +08:00
feat: align Go/Node DSML tool-call parsing drift tolerance and update API docs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
"ds2api/internal/auth"
|
||||
"ds2api/internal/config"
|
||||
"ds2api/internal/httpapi/openai/history"
|
||||
"ds2api/internal/promptcompat"
|
||||
"ds2api/internal/util"
|
||||
|
||||
@@ -96,7 +97,7 @@ func (h *Handler) handleVercelStreamPrepare(w http.ResponseWriter, r *http.Reque
|
||||
}
|
||||
|
||||
payload := stdReq.CompletionPayload(sessionID)
|
||||
leaseID := h.holdStreamLease(a)
|
||||
leaseID := h.holdStreamLease(a, stdReq)
|
||||
if leaseID == "" {
|
||||
writeOpenAIError(w, http.StatusInternalServerError, "failed to create stream lease")
|
||||
return
|
||||
@@ -185,6 +186,80 @@ func (h *Handler) handleVercelStreamPow(w http.ResponseWriter, r *http.Request)
|
||||
})
|
||||
}
|
||||
|
||||
func (h *Handler) handleVercelStreamSwitch(w http.ResponseWriter, r *http.Request) {
|
||||
if !config.IsVercel() {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
h.sweepExpiredStreamLeases()
|
||||
internalSecret := vercelInternalSecret()
|
||||
internalToken := strings.TrimSpace(r.Header.Get("X-Ds2-Internal-Token"))
|
||||
if internalSecret == "" || subtle.ConstantTimeCompare([]byte(internalToken), []byte(internalSecret)) != 1 {
|
||||
writeOpenAIError(w, http.StatusUnauthorized, "unauthorized internal request")
|
||||
return
|
||||
}
|
||||
|
||||
var req map[string]any
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
writeOpenAIError(w, http.StatusBadRequest, "invalid json")
|
||||
return
|
||||
}
|
||||
leaseID, _ := req["lease_id"].(string)
|
||||
leaseID = strings.TrimSpace(leaseID)
|
||||
if leaseID == "" {
|
||||
writeOpenAIError(w, http.StatusBadRequest, "lease_id is required")
|
||||
return
|
||||
}
|
||||
lease, ok := h.lookupStreamLease(leaseID)
|
||||
if !ok || lease.Auth == nil {
|
||||
writeOpenAIError(w, http.StatusNotFound, "stream lease not found or expired")
|
||||
return
|
||||
}
|
||||
a := lease.Auth
|
||||
if !a.UseConfigToken || !a.SwitchAccount(r.Context()) {
|
||||
writeOpenAIErrorWithCode(w, http.StatusTooManyRequests, "Upstream account hit a rate limit and returned reasoning without visible output.", "upstream_empty_output")
|
||||
return
|
||||
}
|
||||
|
||||
stdReq := lease.Standard
|
||||
var err error
|
||||
if stdReq.CurrentInputFileApplied {
|
||||
stdReq, err = (history.Service{Store: h.Store, DS: h.DS}).ReuploadAppliedCurrentInputFile(r.Context(), a, stdReq)
|
||||
if err != nil {
|
||||
status, message := mapCurrentInputFileError(err)
|
||||
writeOpenAIError(w, status, message)
|
||||
return
|
||||
}
|
||||
}
|
||||
sessionID, err := h.DS.CreateSession(r.Context(), a, 3)
|
||||
if err != nil {
|
||||
writeOpenAIError(w, http.StatusUnauthorized, "Account token is invalid. Please re-login the account in admin.")
|
||||
return
|
||||
}
|
||||
powHeader, err := h.DS.GetPow(r.Context(), a, 3)
|
||||
if err != nil {
|
||||
writeOpenAIError(w, http.StatusUnauthorized, "Failed to get PoW (invalid token or unknown error).")
|
||||
return
|
||||
}
|
||||
if strings.TrimSpace(a.DeepSeekToken) == "" {
|
||||
writeOpenAIError(w, http.StatusUnauthorized, "Account token is invalid. Please re-login the account in admin.")
|
||||
return
|
||||
}
|
||||
h.updateStreamLeaseStandard(leaseID, stdReq)
|
||||
writeJSON(w, http.StatusOK, map[string]any{
|
||||
"session_id": sessionID,
|
||||
"lease_id": leaseID,
|
||||
"model": stdReq.ResponseModel,
|
||||
"final_prompt": stdReq.FinalPrompt,
|
||||
"thinking_enabled": stdReq.Thinking,
|
||||
"search_enabled": stdReq.Search,
|
||||
"tool_names": stdReq.ToolNames,
|
||||
"deepseek_token": a.DeepSeekToken,
|
||||
"pow_header": powHeader,
|
||||
"payload": stdReq.CompletionPayload(sessionID),
|
||||
})
|
||||
}
|
||||
|
||||
func isVercelStreamPrepareRequest(r *http.Request) bool {
|
||||
if r == nil {
|
||||
return false
|
||||
@@ -206,6 +281,13 @@ func isVercelStreamPowRequest(r *http.Request) bool {
|
||||
return strings.TrimSpace(r.URL.Query().Get("__stream_pow")) == "1"
|
||||
}
|
||||
|
||||
func isVercelStreamSwitchRequest(r *http.Request) bool {
|
||||
if r == nil {
|
||||
return false
|
||||
}
|
||||
return strings.TrimSpace(r.URL.Query().Get("__stream_switch")) == "1"
|
||||
}
|
||||
|
||||
func vercelInternalSecret() string {
|
||||
if v := strings.TrimSpace(os.Getenv("DS2API_VERCEL_INTERNAL_SECRET")); v != "" {
|
||||
return v
|
||||
@@ -216,10 +298,14 @@ func vercelInternalSecret() string {
|
||||
return "admin"
|
||||
}
|
||||
|
||||
func (h *Handler) holdStreamLease(a *auth.RequestAuth) string {
|
||||
func (h *Handler) holdStreamLease(a *auth.RequestAuth, standards ...promptcompat.StandardRequest) string {
|
||||
if a == nil {
|
||||
return ""
|
||||
}
|
||||
var stdReq promptcompat.StandardRequest
|
||||
if len(standards) > 0 {
|
||||
stdReq = standards[0]
|
||||
}
|
||||
now := time.Now()
|
||||
ttl := streamLeaseTTL()
|
||||
if ttl <= 0 {
|
||||
@@ -234,6 +320,7 @@ func (h *Handler) holdStreamLease(a *auth.RequestAuth) string {
|
||||
leaseID := newLeaseID()
|
||||
h.streamLeases[leaseID] = streamLease{
|
||||
Auth: a,
|
||||
Standard: stdReq,
|
||||
ExpiresAt: now.Add(ttl),
|
||||
}
|
||||
h.leaseMu.Unlock()
|
||||
@@ -241,20 +328,43 @@ func (h *Handler) holdStreamLease(a *auth.RequestAuth) string {
|
||||
return leaseID
|
||||
}
|
||||
|
||||
func (h *Handler) lookupStreamLeaseAuth(leaseID string) *auth.RequestAuth {
|
||||
func (h *Handler) lookupStreamLease(leaseID string) (streamLease, bool) {
|
||||
leaseID = strings.TrimSpace(leaseID)
|
||||
if leaseID == "" {
|
||||
return nil
|
||||
return streamLease{}, false
|
||||
}
|
||||
h.leaseMu.Lock()
|
||||
lease, ok := h.streamLeases[leaseID]
|
||||
h.leaseMu.Unlock()
|
||||
if !ok || time.Now().After(lease.ExpiresAt) {
|
||||
return streamLease{}, false
|
||||
}
|
||||
return lease, true
|
||||
}
|
||||
|
||||
func (h *Handler) lookupStreamLeaseAuth(leaseID string) *auth.RequestAuth {
|
||||
lease, ok := h.lookupStreamLease(leaseID)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return lease.Auth
|
||||
}
|
||||
|
||||
func (h *Handler) updateStreamLeaseStandard(leaseID string, stdReq promptcompat.StandardRequest) {
|
||||
leaseID = strings.TrimSpace(leaseID)
|
||||
if leaseID == "" {
|
||||
return
|
||||
}
|
||||
h.leaseMu.Lock()
|
||||
defer h.leaseMu.Unlock()
|
||||
lease, ok := h.streamLeases[leaseID]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
lease.Standard = stdReq
|
||||
h.streamLeases[leaseID] = lease
|
||||
}
|
||||
|
||||
func (h *Handler) releaseStreamLease(leaseID string) bool {
|
||||
leaseID = strings.TrimSpace(leaseID)
|
||||
if leaseID == "" {
|
||||
|
||||
Reference in New Issue
Block a user