185 lines
4.0 KiB
Go
185 lines
4.0 KiB
Go
package openai
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
)
|
|
|
|
type ChatCompletionRequest struct {
|
|
Model string `json:"model"`
|
|
Messages []interface{} `json:"messages"`
|
|
Stream bool `json:"stream"`
|
|
Tools []interface{} `json:"tools"`
|
|
ToolChoice interface{} `json:"tool_choice"`
|
|
Functions []interface{} `json:"functions"`
|
|
FunctionCall interface{} `json:"function_call"`
|
|
}
|
|
|
|
func NormalizeModelID(raw string) string {
|
|
trimmed := strings.TrimSpace(raw)
|
|
if trimmed == "" {
|
|
return ""
|
|
}
|
|
parts := strings.Split(trimmed, "/")
|
|
last := parts[len(parts)-1]
|
|
if last == "" {
|
|
return ""
|
|
}
|
|
return last
|
|
}
|
|
|
|
func imageURLToText(imageURL interface{}) string {
|
|
if imageURL == nil {
|
|
return "[Image]"
|
|
}
|
|
var url string
|
|
switch v := imageURL.(type) {
|
|
case string:
|
|
url = v
|
|
case map[string]interface{}:
|
|
if u, ok := v["url"].(string); ok {
|
|
url = u
|
|
}
|
|
}
|
|
if url == "" {
|
|
return "[Image]"
|
|
}
|
|
if strings.HasPrefix(url, "data:") {
|
|
end := strings.Index(url, ";")
|
|
mime := "image"
|
|
if end > 5 {
|
|
mime = url[5:end]
|
|
}
|
|
return "[Image: base64 " + mime + "]"
|
|
}
|
|
return "[Image: " + url + "]"
|
|
}
|
|
|
|
func MessageContentToText(content interface{}) string {
|
|
if content == nil {
|
|
return ""
|
|
}
|
|
switch v := content.(type) {
|
|
case string:
|
|
return v
|
|
case []interface{}:
|
|
var parts []string
|
|
for _, p := range v {
|
|
if p == nil {
|
|
continue
|
|
}
|
|
switch part := p.(type) {
|
|
case string:
|
|
parts = append(parts, part)
|
|
case map[string]interface{}:
|
|
typ, _ := part["type"].(string)
|
|
switch typ {
|
|
case "text":
|
|
if t, ok := part["text"].(string); ok {
|
|
parts = append(parts, t)
|
|
}
|
|
case "image_url":
|
|
parts = append(parts, imageURLToText(part["image_url"]))
|
|
case "image":
|
|
src := part["source"]
|
|
if src == nil {
|
|
src = part["url"]
|
|
}
|
|
parts = append(parts, imageURLToText(src))
|
|
}
|
|
}
|
|
}
|
|
return strings.Join(parts, " ")
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func ToolsToSystemText(tools []interface{}, functions []interface{}) string {
|
|
var defs []interface{}
|
|
|
|
for _, t := range tools {
|
|
if m, ok := t.(map[string]interface{}); ok {
|
|
if m["type"] == "function" {
|
|
if fn := m["function"]; fn != nil {
|
|
defs = append(defs, fn)
|
|
}
|
|
} else {
|
|
defs = append(defs, t)
|
|
}
|
|
}
|
|
}
|
|
defs = append(defs, functions...)
|
|
|
|
if len(defs) == 0 {
|
|
return ""
|
|
}
|
|
|
|
var lines []string
|
|
lines = append(lines, "Available tools (respond with a JSON object to call one):", "")
|
|
|
|
for _, raw := range defs {
|
|
fn, ok := raw.(map[string]interface{})
|
|
if !ok {
|
|
continue
|
|
}
|
|
name, _ := fn["name"].(string)
|
|
desc, _ := fn["description"].(string)
|
|
params := "{}"
|
|
if p := fn["parameters"]; p != nil {
|
|
if b, err := json.MarshalIndent(p, "", " "); err == nil {
|
|
params = string(b)
|
|
}
|
|
}
|
|
lines = append(lines, "Function: "+name+"\nDescription: "+desc+"\nParameters: "+params)
|
|
}
|
|
|
|
return strings.Join(lines, "\n")
|
|
}
|
|
|
|
type SimpleMessage struct {
|
|
Role string `json:"role"`
|
|
Content string `json:"content"`
|
|
}
|
|
|
|
func BuildPromptFromMessages(messages []interface{}) string {
|
|
var systemParts []string
|
|
var convo []string
|
|
|
|
for _, raw := range messages {
|
|
m, ok := raw.(map[string]interface{})
|
|
if !ok {
|
|
continue
|
|
}
|
|
role, _ := m["role"].(string)
|
|
text := MessageContentToText(m["content"])
|
|
if text == "" {
|
|
continue
|
|
}
|
|
switch role {
|
|
case "system", "developer":
|
|
systemParts = append(systemParts, text)
|
|
case "user":
|
|
convo = append(convo, "User: "+text)
|
|
case "assistant":
|
|
convo = append(convo, "Assistant: "+text)
|
|
case "tool", "function":
|
|
convo = append(convo, "Tool: "+text)
|
|
}
|
|
}
|
|
|
|
system := ""
|
|
if len(systemParts) > 0 {
|
|
system = "System:\n" + strings.Join(systemParts, "\n\n") + "\n\n"
|
|
}
|
|
transcript := strings.Join(convo, "\n\n")
|
|
return system + transcript + "\n\nAssistant:"
|
|
}
|
|
|
|
func BuildPromptFromSimpleMessages(messages []SimpleMessage) string {
|
|
ifaces := make([]interface{}, len(messages))
|
|
for i, m := range messages {
|
|
ifaces[i] = map[string]interface{}{"role": m.Role, "content": m.Content}
|
|
}
|
|
return BuildPromptFromMessages(ifaces)
|
|
}
|