mirror of
https://github.com/CJackHwang/ds2api.git
synced 2026-05-18 07:05:08 +08:00
refactor: thread tool schemas through responses tool outputs
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -9,19 +9,19 @@ import (
|
|||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
func BuildResponseObject(responseID, model, finalPrompt, finalThinking, finalText string, toolNames []string) map[string]any {
|
func BuildResponseObject(responseID, model, finalPrompt, finalThinking, finalText string, toolNames []string, toolsRaw any) map[string]any {
|
||||||
// Strict mode: only standalone, structured tool-call payloads are treated
|
// Strict mode: only standalone, structured tool-call payloads are treated
|
||||||
// as executable tool calls.
|
// as executable tool calls.
|
||||||
detected := toolcall.ParseAssistantToolCallsDetailed(finalText, finalThinking, toolNames)
|
detected := toolcall.ParseAssistantToolCallsDetailed(finalText, finalThinking, toolNames)
|
||||||
return BuildResponseObjectWithToolCalls(responseID, model, finalPrompt, finalThinking, finalText, detected.Calls)
|
return BuildResponseObjectWithToolCalls(responseID, model, finalPrompt, finalThinking, finalText, detected.Calls, toolsRaw)
|
||||||
}
|
}
|
||||||
|
|
||||||
func BuildResponseObjectWithToolCalls(responseID, model, finalPrompt, finalThinking, finalText string, detected []toolcall.ParsedToolCall) map[string]any {
|
func BuildResponseObjectWithToolCalls(responseID, model, finalPrompt, finalThinking, finalText string, detected []toolcall.ParsedToolCall, toolsRaw any) map[string]any {
|
||||||
exposedOutputText := finalText
|
exposedOutputText := finalText
|
||||||
output := make([]any, 0, 2)
|
output := make([]any, 0, 2)
|
||||||
if len(detected) > 0 {
|
if len(detected) > 0 {
|
||||||
exposedOutputText = ""
|
exposedOutputText = ""
|
||||||
output = append(output, toResponsesFunctionCallItems(detected)...)
|
output = append(output, toResponsesFunctionCallItems(detected, toolsRaw)...)
|
||||||
} else {
|
} else {
|
||||||
content := make([]any, 0, 2)
|
content := make([]any, 0, 2)
|
||||||
if finalThinking != "" {
|
if finalThinking != "" {
|
||||||
@@ -74,12 +74,13 @@ func BuildResponseObjectFromItems(responseID, model, finalPrompt, finalThinking,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func toResponsesFunctionCallItems(toolCalls []toolcall.ParsedToolCall) []any {
|
func toResponsesFunctionCallItems(toolCalls []toolcall.ParsedToolCall, toolsRaw any) []any {
|
||||||
if len(toolCalls) == 0 {
|
if len(toolCalls) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
normalizedCalls := toolcall.NormalizeParsedToolCallsForSchemas(toolCalls, toolsRaw)
|
||||||
out := make([]any, 0, len(toolCalls))
|
out := make([]any, 0, len(toolCalls))
|
||||||
for _, tc := range toolCalls {
|
for _, tc := range normalizedCalls {
|
||||||
if strings.TrimSpace(tc.Name) == "" {
|
if strings.TrimSpace(tc.Name) == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,14 +27,14 @@ type responsesNonStreamResult struct {
|
|||||||
responseMessageID int
|
responseMessageID int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) handleResponsesNonStreamWithRetry(w http.ResponseWriter, ctx context.Context, a *auth.RequestAuth, resp *http.Response, payload map[string]any, pow, owner, responseID, model, finalPrompt string, thinkingEnabled, searchEnabled bool, toolNames []string, toolChoice promptcompat.ToolChoicePolicy, traceID string) {
|
func (h *Handler) handleResponsesNonStreamWithRetry(w http.ResponseWriter, ctx context.Context, a *auth.RequestAuth, resp *http.Response, payload map[string]any, pow, owner, responseID, model, finalPrompt string, thinkingEnabled, searchEnabled bool, toolNames []string, toolsRaw any, toolChoice promptcompat.ToolChoicePolicy, traceID string) {
|
||||||
attempts := 0
|
attempts := 0
|
||||||
currentResp := resp
|
currentResp := resp
|
||||||
usagePrompt := finalPrompt
|
usagePrompt := finalPrompt
|
||||||
accumulatedThinking := ""
|
accumulatedThinking := ""
|
||||||
accumulatedToolDetectionThinking := ""
|
accumulatedToolDetectionThinking := ""
|
||||||
for {
|
for {
|
||||||
result, ok := h.collectResponsesNonStreamAttempt(w, currentResp, responseID, model, usagePrompt, thinkingEnabled, searchEnabled, toolNames)
|
result, ok := h.collectResponsesNonStreamAttempt(w, currentResp, responseID, model, usagePrompt, thinkingEnabled, searchEnabled, toolNames, toolsRaw)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -43,7 +43,7 @@ func (h *Handler) handleResponsesNonStreamWithRetry(w http.ResponseWriter, ctx c
|
|||||||
result.thinking = accumulatedThinking
|
result.thinking = accumulatedThinking
|
||||||
result.toolDetectionThinking = accumulatedToolDetectionThinking
|
result.toolDetectionThinking = accumulatedToolDetectionThinking
|
||||||
result.parsed = detectAssistantToolCalls(result.text, result.thinking, result.toolDetectionThinking, toolNames)
|
result.parsed = detectAssistantToolCalls(result.text, result.thinking, result.toolDetectionThinking, toolNames)
|
||||||
result.body = openaifmt.BuildResponseObjectWithToolCalls(responseID, model, usagePrompt, result.thinking, result.text, result.parsed.Calls)
|
result.body = openaifmt.BuildResponseObjectWithToolCalls(responseID, model, usagePrompt, result.thinking, result.text, result.parsed.Calls, toolsRaw)
|
||||||
|
|
||||||
if !shouldRetryResponsesNonStream(result, attempts) {
|
if !shouldRetryResponsesNonStream(result, attempts) {
|
||||||
h.finishResponsesNonStreamResult(w, result, attempts, owner, responseID, toolChoice, traceID)
|
h.finishResponsesNonStreamResult(w, result, attempts, owner, responseID, toolChoice, traceID)
|
||||||
@@ -68,7 +68,7 @@ func (h *Handler) handleResponsesNonStreamWithRetry(w http.ResponseWriter, ctx c
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) collectResponsesNonStreamAttempt(w http.ResponseWriter, resp *http.Response, responseID, model, usagePrompt string, thinkingEnabled, searchEnabled bool, toolNames []string) (responsesNonStreamResult, bool) {
|
func (h *Handler) collectResponsesNonStreamAttempt(w http.ResponseWriter, resp *http.Response, responseID, model, usagePrompt string, thinkingEnabled, searchEnabled bool, toolNames []string, toolsRaw any) (responsesNonStreamResult, bool) {
|
||||||
defer func() { _ = resp.Body.Close() }()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
body, _ := io.ReadAll(resp.Body)
|
body, _ := io.ReadAll(resp.Body)
|
||||||
@@ -84,7 +84,7 @@ func (h *Handler) collectResponsesNonStreamAttempt(w http.ResponseWriter, resp *
|
|||||||
sanitizedText = replaceCitationMarkersWithLinks(sanitizedText, result.CitationLinks)
|
sanitizedText = replaceCitationMarkersWithLinks(sanitizedText, result.CitationLinks)
|
||||||
}
|
}
|
||||||
textParsed := detectAssistantToolCalls(sanitizedText, sanitizedThinking, toolDetectionThinking, toolNames)
|
textParsed := detectAssistantToolCalls(sanitizedText, sanitizedThinking, toolDetectionThinking, toolNames)
|
||||||
responseObj := openaifmt.BuildResponseObjectWithToolCalls(responseID, model, usagePrompt, sanitizedThinking, sanitizedText, textParsed.Calls)
|
responseObj := openaifmt.BuildResponseObjectWithToolCalls(responseID, model, usagePrompt, sanitizedThinking, sanitizedText, textParsed.Calls, toolsRaw)
|
||||||
return responsesNonStreamResult{
|
return responsesNonStreamResult{
|
||||||
thinking: sanitizedThinking,
|
thinking: sanitizedThinking,
|
||||||
toolDetectionThinking: toolDetectionThinking,
|
toolDetectionThinking: toolDetectionThinking,
|
||||||
@@ -123,8 +123,8 @@ func shouldRetryResponsesNonStream(result responsesNonStreamResult, attempts int
|
|||||||
strings.TrimSpace(result.text) == ""
|
strings.TrimSpace(result.text) == ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) handleResponsesStreamWithRetry(w http.ResponseWriter, r *http.Request, a *auth.RequestAuth, resp *http.Response, payload map[string]any, pow, owner, responseID, model, finalPrompt string, thinkingEnabled, searchEnabled bool, toolNames []string, toolChoice promptcompat.ToolChoicePolicy, traceID string) {
|
func (h *Handler) handleResponsesStreamWithRetry(w http.ResponseWriter, r *http.Request, a *auth.RequestAuth, resp *http.Response, payload map[string]any, pow, owner, responseID, model, finalPrompt string, thinkingEnabled, searchEnabled bool, toolNames []string, toolsRaw any, toolChoice promptcompat.ToolChoicePolicy, traceID string) {
|
||||||
streamRuntime, initialType, ok := h.prepareResponsesStreamRuntime(w, resp, owner, responseID, model, finalPrompt, thinkingEnabled, searchEnabled, toolNames, toolChoice, traceID)
|
streamRuntime, initialType, ok := h.prepareResponsesStreamRuntime(w, resp, owner, responseID, model, finalPrompt, thinkingEnabled, searchEnabled, toolNames, toolsRaw, toolChoice, traceID)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -165,7 +165,7 @@ func (h *Handler) handleResponsesStreamWithRetry(w http.ResponseWriter, r *http.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) prepareResponsesStreamRuntime(w http.ResponseWriter, resp *http.Response, owner, responseID, model, finalPrompt string, thinkingEnabled, searchEnabled bool, toolNames []string, toolChoice promptcompat.ToolChoicePolicy, traceID string) (*responsesStreamRuntime, string, bool) {
|
func (h *Handler) prepareResponsesStreamRuntime(w http.ResponseWriter, resp *http.Response, owner, responseID, model, finalPrompt string, thinkingEnabled, searchEnabled bool, toolNames []string, toolsRaw any, toolChoice promptcompat.ToolChoicePolicy, traceID string) (*responsesStreamRuntime, string, bool) {
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
defer func() { _ = resp.Body.Close() }()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
body, _ := io.ReadAll(resp.Body)
|
body, _ := io.ReadAll(resp.Body)
|
||||||
@@ -184,7 +184,7 @@ func (h *Handler) prepareResponsesStreamRuntime(w http.ResponseWriter, resp *htt
|
|||||||
}
|
}
|
||||||
streamRuntime := newResponsesStreamRuntime(
|
streamRuntime := newResponsesStreamRuntime(
|
||||||
w, rc, canFlush, responseID, model, finalPrompt, thinkingEnabled, searchEnabled,
|
w, rc, canFlush, responseID, model, finalPrompt, thinkingEnabled, searchEnabled,
|
||||||
h.compatStripReferenceMarkers(), toolNames, len(toolNames) > 0,
|
h.compatStripReferenceMarkers(), toolNames, toolsRaw, len(toolNames) > 0,
|
||||||
h.toolcallFeatureMatchEnabled() && h.toolcallEarlyEmitHighConfidence(),
|
h.toolcallFeatureMatchEnabled() && h.toolcallEarlyEmitHighConfidence(),
|
||||||
toolChoice, traceID, func(obj map[string]any) {
|
toolChoice, traceID, func(obj map[string]any) {
|
||||||
h.getResponseStore().put(owner, responseID, obj)
|
h.getResponseStore().put(owner, responseID, obj)
|
||||||
|
|||||||
@@ -115,13 +115,13 @@ func (h *Handler) Responses(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
responseID := "resp_" + strings.ReplaceAll(uuid.NewString(), "-", "")
|
responseID := "resp_" + strings.ReplaceAll(uuid.NewString(), "-", "")
|
||||||
if stdReq.Stream {
|
if stdReq.Stream {
|
||||||
h.handleResponsesStreamWithRetry(w, r, a, resp, payload, pow, owner, responseID, stdReq.ResponseModel, stdReq.FinalPrompt, stdReq.Thinking, stdReq.Search, stdReq.ToolNames, stdReq.ToolChoice, traceID)
|
h.handleResponsesStreamWithRetry(w, r, a, resp, payload, pow, owner, responseID, stdReq.ResponseModel, stdReq.FinalPrompt, stdReq.Thinking, stdReq.Search, stdReq.ToolNames, stdReq.ToolsRaw, stdReq.ToolChoice, traceID)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
h.handleResponsesNonStreamWithRetry(w, r.Context(), a, resp, payload, pow, owner, responseID, stdReq.ResponseModel, stdReq.FinalPrompt, stdReq.Thinking, stdReq.Search, stdReq.ToolNames, stdReq.ToolChoice, traceID)
|
h.handleResponsesNonStreamWithRetry(w, r.Context(), a, resp, payload, pow, owner, responseID, stdReq.ResponseModel, stdReq.FinalPrompt, stdReq.Thinking, stdReq.Search, stdReq.ToolNames, stdReq.ToolsRaw, stdReq.ToolChoice, traceID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) handleResponsesNonStream(w http.ResponseWriter, resp *http.Response, owner, responseID, model, finalPrompt string, thinkingEnabled, searchEnabled bool, toolNames []string, toolChoice promptcompat.ToolChoicePolicy, traceID string) {
|
func (h *Handler) handleResponsesNonStream(w http.ResponseWriter, resp *http.Response, owner, responseID, model, finalPrompt string, thinkingEnabled, searchEnabled bool, toolNames []string, toolsRaw any, toolChoice promptcompat.ToolChoicePolicy, traceID string) {
|
||||||
defer func() { _ = resp.Body.Close() }()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
body, _ := io.ReadAll(resp.Body)
|
body, _ := io.ReadAll(resp.Body)
|
||||||
@@ -148,12 +148,12 @@ func (h *Handler) handleResponsesNonStream(w http.ResponseWriter, resp *http.Res
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
responseObj := openaifmt.BuildResponseObjectWithToolCalls(responseID, model, finalPrompt, sanitizedThinking, sanitizedText, textParsed.Calls)
|
responseObj := openaifmt.BuildResponseObjectWithToolCalls(responseID, model, finalPrompt, sanitizedThinking, sanitizedText, textParsed.Calls, toolsRaw)
|
||||||
h.getResponseStore().put(owner, responseID, responseObj)
|
h.getResponseStore().put(owner, responseID, responseObj)
|
||||||
writeJSON(w, http.StatusOK, responseObj)
|
writeJSON(w, http.StatusOK, responseObj)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) handleResponsesStream(w http.ResponseWriter, r *http.Request, resp *http.Response, owner, responseID, model, finalPrompt string, thinkingEnabled, searchEnabled bool, toolNames []string, toolChoice promptcompat.ToolChoicePolicy, traceID string) {
|
func (h *Handler) handleResponsesStream(w http.ResponseWriter, r *http.Request, resp *http.Response, owner, responseID, model, finalPrompt string, thinkingEnabled, searchEnabled bool, toolNames []string, toolsRaw any, toolChoice promptcompat.ToolChoicePolicy, traceID string) {
|
||||||
defer func() { _ = resp.Body.Close() }()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
body, _ := io.ReadAll(resp.Body)
|
body, _ := io.ReadAll(resp.Body)
|
||||||
@@ -186,6 +186,7 @@ func (h *Handler) handleResponsesStream(w http.ResponseWriter, r *http.Request,
|
|||||||
searchEnabled,
|
searchEnabled,
|
||||||
stripReferenceMarkers,
|
stripReferenceMarkers,
|
||||||
toolNames,
|
toolNames,
|
||||||
|
toolsRaw,
|
||||||
bufferToolContent,
|
bufferToolContent,
|
||||||
emitEarlyToolDeltas,
|
emitEarlyToolDeltas,
|
||||||
toolChoice,
|
toolChoice,
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ type responsesStreamRuntime struct {
|
|||||||
model string
|
model string
|
||||||
finalPrompt string
|
finalPrompt string
|
||||||
toolNames []string
|
toolNames []string
|
||||||
|
toolsRaw any
|
||||||
traceID string
|
traceID string
|
||||||
toolChoice promptcompat.ToolChoicePolicy
|
toolChoice promptcompat.ToolChoicePolicy
|
||||||
|
|
||||||
@@ -72,6 +73,7 @@ func newResponsesStreamRuntime(
|
|||||||
searchEnabled bool,
|
searchEnabled bool,
|
||||||
stripReferenceMarkers bool,
|
stripReferenceMarkers bool,
|
||||||
toolNames []string,
|
toolNames []string,
|
||||||
|
toolsRaw any,
|
||||||
bufferToolContent bool,
|
bufferToolContent bool,
|
||||||
emitEarlyToolDeltas bool,
|
emitEarlyToolDeltas bool,
|
||||||
toolChoice promptcompat.ToolChoicePolicy,
|
toolChoice promptcompat.ToolChoicePolicy,
|
||||||
@@ -89,6 +91,7 @@ func newResponsesStreamRuntime(
|
|||||||
searchEnabled: searchEnabled,
|
searchEnabled: searchEnabled,
|
||||||
stripReferenceMarkers: stripReferenceMarkers,
|
stripReferenceMarkers: stripReferenceMarkers,
|
||||||
toolNames: toolNames,
|
toolNames: toolNames,
|
||||||
|
toolsRaw: toolsRaw,
|
||||||
bufferToolContent: bufferToolContent,
|
bufferToolContent: bufferToolContent,
|
||||||
emitEarlyToolDeltas: emitEarlyToolDeltas,
|
emitEarlyToolDeltas: emitEarlyToolDeltas,
|
||||||
streamToolCallIDs: map[int]string{},
|
streamToolCallIDs: map[int]string{},
|
||||||
|
|||||||
@@ -220,7 +220,8 @@ func (s *responsesStreamRuntime) emitFunctionCallDeltaEvents(deltas []toolstream
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *responsesStreamRuntime) emitFunctionCallDoneEvents(calls []toolcall.ParsedToolCall) {
|
func (s *responsesStreamRuntime) emitFunctionCallDoneEvents(calls []toolcall.ParsedToolCall) {
|
||||||
for idx, tc := range calls {
|
normalizedCalls := toolcall.NormalizeParsedToolCallsForSchemas(calls, s.toolsRaw)
|
||||||
|
for idx, tc := range normalizedCalls {
|
||||||
if strings.TrimSpace(tc.Name) == "" {
|
if strings.TrimSpace(tc.Name) == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,7 +109,8 @@ func (s *responsesStreamRuntime) buildCompletedResponseObject(finalThinking, fin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for idx, tc := range calls {
|
normalizedCalls := toolcall.NormalizeParsedToolCallsForSchemas(calls, s.toolsRaw)
|
||||||
|
for idx, tc := range normalizedCalls {
|
||||||
if strings.TrimSpace(tc.Name) == "" {
|
if strings.TrimSpace(tc.Name) == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user