mirror of
https://github.com/CJackHwang/ds2api.git
synced 2026-05-10 19:27:41 +08:00
174 lines
5.2 KiB
Go
174 lines
5.2 KiB
Go
package promptcompat
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
func ResponsesMessagesFromRequest(req map[string]any) []any {
|
|
if msgs, ok := req["messages"].([]any); ok && len(msgs) > 0 {
|
|
return prependInstructionMessage(msgs, req["instructions"])
|
|
}
|
|
if rawInput, ok := req["input"]; ok {
|
|
if msgs := NormalizeResponsesInputAsMessages(rawInput); len(msgs) > 0 {
|
|
return prependInstructionMessage(msgs, req["instructions"])
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func prependInstructionMessage(messages []any, instructions any) []any {
|
|
sys, _ := instructions.(string)
|
|
sys = strings.TrimSpace(sys)
|
|
if sys == "" {
|
|
return messages
|
|
}
|
|
out := make([]any, 0, len(messages)+1)
|
|
out = append(out, map[string]any{"role": "system", "content": sys})
|
|
out = append(out, messages...)
|
|
return out
|
|
}
|
|
|
|
func NormalizeResponsesInputAsMessages(input any) []any {
|
|
switch v := input.(type) {
|
|
case string:
|
|
if strings.TrimSpace(v) == "" {
|
|
return nil
|
|
}
|
|
return []any{map[string]any{"role": "user", "content": v}}
|
|
case []any:
|
|
return normalizeResponsesInputArray(v)
|
|
case map[string]any:
|
|
if msg := normalizeResponsesInputItem(v); msg != nil {
|
|
return []any{msg}
|
|
}
|
|
if txt, _ := v["text"].(string); strings.TrimSpace(txt) != "" {
|
|
return []any{map[string]any{"role": "user", "content": txt}}
|
|
}
|
|
if content, ok := v["content"]; ok {
|
|
if strings.TrimSpace(NormalizeOpenAIContentForPrompt(content)) != "" {
|
|
return []any{map[string]any{"role": "user", "content": content}}
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func normalizeResponsesInputArray(items []any) []any {
|
|
if len(items) == 0 {
|
|
return nil
|
|
}
|
|
out := make([]any, 0, len(items))
|
|
callNameByID := map[string]string{}
|
|
fallbackParts := make([]string, 0, len(items))
|
|
pendingAssistantReasoning := ""
|
|
flushFallback := func() {
|
|
if len(fallbackParts) == 0 {
|
|
return
|
|
}
|
|
if pendingAssistantReasoning != "" {
|
|
out = append(out, map[string]any{"role": "assistant", "reasoning_content": pendingAssistantReasoning})
|
|
pendingAssistantReasoning = ""
|
|
}
|
|
out = append(out, map[string]any{"role": "user", "content": strings.Join(fallbackParts, "\n")})
|
|
fallbackParts = fallbackParts[:0]
|
|
}
|
|
flushPendingReasoning := func() {
|
|
if pendingAssistantReasoning == "" {
|
|
return
|
|
}
|
|
out = append(out, map[string]any{"role": "assistant", "reasoning_content": pendingAssistantReasoning})
|
|
pendingAssistantReasoning = ""
|
|
}
|
|
|
|
for _, item := range items {
|
|
switch x := item.(type) {
|
|
case map[string]any:
|
|
if msg := normalizeResponsesInputItemWithState(x, callNameByID); msg != nil {
|
|
if reasoning := assistantReasoningOnlyContent(msg); reasoning != "" {
|
|
if pendingAssistantReasoning == "" {
|
|
pendingAssistantReasoning = reasoning
|
|
} else {
|
|
pendingAssistantReasoning += "\n" + reasoning
|
|
}
|
|
continue
|
|
}
|
|
if isAssistantToolCallMessage(msg) && pendingAssistantReasoning != "" {
|
|
if strings.TrimSpace(normalizeOpenAIReasoningContentForPrompt(msg["reasoning_content"])) == "" {
|
|
msg["reasoning_content"] = pendingAssistantReasoning
|
|
}
|
|
pendingAssistantReasoning = ""
|
|
} else {
|
|
flushPendingReasoning()
|
|
}
|
|
flushFallback()
|
|
if isAssistantToolCallMessage(msg) && len(out) > 0 {
|
|
if merged := mergeResponsesAssistantToolCalls(out[len(out)-1], msg); merged {
|
|
continue
|
|
}
|
|
}
|
|
out = append(out, msg)
|
|
continue
|
|
}
|
|
if s := normalizeResponsesFallbackPart(x); s != "" {
|
|
fallbackParts = append(fallbackParts, s)
|
|
}
|
|
default:
|
|
if s := strings.TrimSpace(fmt.Sprintf("%v", item)); s != "" {
|
|
fallbackParts = append(fallbackParts, s)
|
|
}
|
|
}
|
|
}
|
|
flushPendingReasoning()
|
|
flushFallback()
|
|
if len(out) == 0 {
|
|
return nil
|
|
}
|
|
return out
|
|
}
|
|
|
|
func assistantReasoningOnlyContent(msg map[string]any) string {
|
|
if !isAssistantMessage(msg) || isAssistantToolCallMessage(msg) {
|
|
return ""
|
|
}
|
|
if _, hasContent := msg["content"]; hasContent {
|
|
normalizedContent := strings.TrimSpace(NormalizeOpenAIContentForPrompt(msg["content"]))
|
|
reasoningFromContent := strings.TrimSpace(extractOpenAIReasoningContentFromMessage(msg["content"]))
|
|
if normalizedContent != "" && normalizedContent != reasoningFromContent {
|
|
return ""
|
|
}
|
|
if reasoningFromContent != "" {
|
|
return reasoningFromContent
|
|
}
|
|
}
|
|
return strings.TrimSpace(normalizeOpenAIReasoningContentForPrompt(msg["reasoning_content"]))
|
|
}
|
|
|
|
func isAssistantMessage(msg map[string]any) bool {
|
|
return strings.EqualFold(strings.TrimSpace(asString(msg["role"])), "assistant")
|
|
}
|
|
|
|
func isAssistantToolCallMessage(msg map[string]any) bool {
|
|
if !isAssistantMessage(msg) {
|
|
return false
|
|
}
|
|
toolCalls, ok := msg["tool_calls"].([]any)
|
|
return ok && len(toolCalls) > 0
|
|
}
|
|
|
|
func mergeResponsesAssistantToolCalls(prev any, next map[string]any) bool {
|
|
prevMsg, ok := prev.(map[string]any)
|
|
if !ok || !isAssistantToolCallMessage(prevMsg) || !isAssistantToolCallMessage(next) {
|
|
return false
|
|
}
|
|
prevCalls, _ := prevMsg["tool_calls"].([]any)
|
|
nextCalls, _ := next["tool_calls"].([]any)
|
|
prevMsg["tool_calls"] = append(prevCalls, nextCalls...)
|
|
if strings.TrimSpace(normalizeOpenAIReasoningContentForPrompt(prevMsg["reasoning_content"])) == "" {
|
|
if reasoning := strings.TrimSpace(normalizeOpenAIReasoningContentForPrompt(next["reasoning_content"])); reasoning != "" {
|
|
prevMsg["reasoning_content"] = reasoning
|
|
}
|
|
}
|
|
return true
|
|
}
|