mirror of
https://github.com/CJackHwang/ds2api.git
synced 2026-05-04 08:25:26 +08:00
177 lines
4.1 KiB
Go
177 lines
4.1 KiB
Go
package promptcompat
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"ds2api/internal/prompt"
|
|
)
|
|
|
|
const assistantReasoningLabel = "reasoning_content"
|
|
|
|
func NormalizeOpenAIMessagesForPrompt(raw []any, traceID string) []map[string]any {
|
|
_ = traceID
|
|
out := make([]map[string]any, 0, len(raw))
|
|
for _, item := range raw {
|
|
msg, ok := item.(map[string]any)
|
|
if !ok {
|
|
continue
|
|
}
|
|
role := strings.ToLower(strings.TrimSpace(asString(msg["role"])))
|
|
switch role {
|
|
case "assistant":
|
|
content := buildAssistantContentForPrompt(msg)
|
|
if content == "" {
|
|
continue
|
|
}
|
|
out = append(out, map[string]any{
|
|
"role": "assistant",
|
|
"content": content,
|
|
})
|
|
case "tool", "function":
|
|
content := buildToolContentForPrompt(msg)
|
|
out = append(out, map[string]any{
|
|
"role": "tool",
|
|
"content": content,
|
|
})
|
|
case "user", "system", "developer":
|
|
out = append(out, map[string]any{
|
|
"role": normalizeOpenAIRoleForPrompt(role),
|
|
"content": NormalizeOpenAIContentForPrompt(msg["content"]),
|
|
})
|
|
default:
|
|
content := NormalizeOpenAIContentForPrompt(msg["content"])
|
|
if content == "" {
|
|
continue
|
|
}
|
|
if role == "" {
|
|
role = "user"
|
|
}
|
|
out = append(out, map[string]any{
|
|
"role": normalizeOpenAIRoleForPrompt(role),
|
|
"content": content,
|
|
})
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
func buildAssistantContentForPrompt(msg map[string]any) string {
|
|
content := strings.TrimSpace(NormalizeOpenAIContentForPrompt(msg["content"]))
|
|
reasoning := strings.TrimSpace(normalizeOpenAIReasoningContentForPrompt(msg["reasoning_content"]))
|
|
if reasoning == "" {
|
|
reasoning = strings.TrimSpace(extractOpenAIReasoningContentFromMessage(msg["content"]))
|
|
}
|
|
toolHistory := prompt.FormatToolCallsForPrompt(msg["tool_calls"])
|
|
parts := make([]string, 0, 3)
|
|
if reasoning != "" {
|
|
parts = append(parts, formatPromptLabeledBlock(assistantReasoningLabel, reasoning))
|
|
}
|
|
if content != "" {
|
|
parts = append(parts, content)
|
|
}
|
|
if toolHistory != "" {
|
|
parts = append(parts, toolHistory)
|
|
}
|
|
switch len(parts) {
|
|
case 0:
|
|
return ""
|
|
case 1:
|
|
return parts[0]
|
|
default:
|
|
return strings.Join(parts, "\n\n")
|
|
}
|
|
}
|
|
|
|
func normalizeOpenAIReasoningContentForPrompt(v any) string {
|
|
switch x := v.(type) {
|
|
case string:
|
|
return x
|
|
case []any:
|
|
return strings.Join(extractOpenAIReasoningPartsFromItems(x), "\n")
|
|
case map[string]any:
|
|
return extractOpenAIReasoningTextFromItem(x)
|
|
default:
|
|
return ""
|
|
}
|
|
}
|
|
|
|
func extractOpenAIReasoningContentFromMessage(v any) string {
|
|
switch x := v.(type) {
|
|
case []any:
|
|
return strings.Join(extractOpenAIReasoningPartsFromItems(x), "\n")
|
|
case map[string]any:
|
|
return extractOpenAIReasoningTextFromItem(x)
|
|
default:
|
|
return ""
|
|
}
|
|
}
|
|
|
|
func extractOpenAIReasoningPartsFromItems(items []any) []string {
|
|
parts := make([]string, 0, len(items))
|
|
for _, item := range items {
|
|
if text := extractOpenAIReasoningTextFromItemMap(item); text != "" {
|
|
parts = append(parts, text)
|
|
}
|
|
}
|
|
return parts
|
|
}
|
|
|
|
func extractOpenAIReasoningTextFromItemMap(item any) string {
|
|
m, ok := item.(map[string]any)
|
|
if !ok {
|
|
return ""
|
|
}
|
|
return extractOpenAIReasoningTextFromItem(m)
|
|
}
|
|
|
|
func extractOpenAIReasoningTextFromItem(m map[string]any) string {
|
|
if m == nil {
|
|
return ""
|
|
}
|
|
switch strings.ToLower(strings.TrimSpace(asString(m["type"]))) {
|
|
case "reasoning", "thinking":
|
|
for _, key := range []string{"text", "thinking", "content"} {
|
|
if text := strings.TrimSpace(asString(m[key])); text != "" {
|
|
return text
|
|
}
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func formatPromptLabeledBlock(label, text string) string {
|
|
label = strings.TrimSpace(label)
|
|
text = strings.TrimSpace(text)
|
|
if label == "" {
|
|
return text
|
|
}
|
|
return "[" + label + "]\n" + text + "\n[/" + label + "]"
|
|
}
|
|
|
|
func buildToolContentForPrompt(msg map[string]any) string {
|
|
content := NormalizeOpenAIContentForPrompt(msg["content"])
|
|
if strings.TrimSpace(content) == "" {
|
|
return "null"
|
|
}
|
|
return content
|
|
}
|
|
|
|
func NormalizeOpenAIContentForPrompt(v any) string {
|
|
return prompt.NormalizeContent(v)
|
|
}
|
|
|
|
func normalizeOpenAIRoleForPrompt(role string) string {
|
|
role = strings.ToLower(strings.TrimSpace(role))
|
|
if role == "developer" {
|
|
return "system"
|
|
}
|
|
return role
|
|
}
|
|
|
|
func asString(v any) string {
|
|
if s, ok := v.(string); ok {
|
|
return s
|
|
}
|
|
return ""
|
|
}
|