Deduplicate ParseDataAudioURL function

This commit is contained in:
Kunal Karmakar
2026-04-19 06:26:52 +00:00
parent e901e70c14
commit bc077db0ee
4 changed files with 36 additions and 121 deletions
+3 -65
View File
@@ -127,7 +127,7 @@ func SerializeMessages(messages []Message) []any {
continue
}
if format, data, ok := parseDataAudioURL(mediaURL); ok {
if format, data, ok := ParseDataAudioURL(mediaURL); ok {
parts = append(parts, map[string]any{
"type": "input_audio",
"input_audio": map[string]any{
@@ -205,7 +205,8 @@ func serializeToolCalls(toolCalls []ToolCall) []openaiToolCall {
return out
}
func parseDataAudioURL(mediaURL string) (format, data string, ok bool) {
// ParseDataAudioURL extracts the format and base64 data from a data:audio/... URL.
func ParseDataAudioURL(mediaURL string) (format, data string, ok bool) {
if !strings.HasPrefix(mediaURL, "data:audio/") {
return "", "", false
}
@@ -478,69 +479,6 @@ func AsInt(v any) (int, bool) {
}
}
// ExtractProtocol extracts the effective protocol and model identifier from a
// model configuration.
//
// The explicit Provider field takes precedence. When Provider is empty, the
// protocol is inferred from Model. Plain model names default to "openai".
// Provider-prefixed models strip the first slash-separated segment from the
// returned model ID.
//
// The returned protocol is normalized to the provider's canonical spelling.
// Examples:
// - Model "openai/gpt-4o" -> ("openai", "gpt-4o")
// - Model "nvidia/z-ai/glm-5.1" -> ("nvidia", "z-ai/glm-5.1")
// - Provider "nvidia", Model "z-ai/glm-5.1" -> ("nvidia", "z-ai/glm-5.1")
// - Provider "openai", Model "openai/gpt-4o" -> ("openai", "openai/gpt-4o")
// - Model "gpt-4o" -> ("openai", "gpt-4o")
func ExtractProtocol(model string) (protocol, modelID string) {
if cfg == nil {
return "", ""
}
model := strings.TrimSpace(cfg.Model)
if provider := strings.TrimSpace(cfg.Provider); provider != "" {
return NormalizeProvider(provider), model
}
if model == "" {
return "", ""
}
protocol, rest, found := strings.Cut(model, "/")
if !found {
return "openai", model
}
protocol = strings.TrimSpace(protocol)
if protocol == "" {
return "", strings.TrimSpace(rest)
}
return NormalizeProvider(protocol), strings.TrimSpace(rest)
}
// NormalizeAnthropicBaseURL ensures the Anthropic base URL is properly formatted.
// It removes a trailing /v1 suffix if present (to avoid duplication), then
// re-appends /v1 when appendV1Suffix is true. An empty apiBase falls back to
// defaultBaseURL.
func NormalizeAnthropicBaseURL(apiBase, defaultBaseURL string, appendV1Suffix bool) string {
base := strings.TrimSpace(apiBase)
if base == "" {
return defaultBaseURL
}
base = strings.TrimRight(base, "/")
if before, ok := strings.CutSuffix(base, "/v1"); ok {
base = before
}
if base == "" {
return defaultBaseURL
}
if appendV1Suffix {
return base + "/v1"
}
return base
}
// AsFloat converts various numeric types to float64.
func AsFloat(v any) (float64, bool) {
switch val := v.(type) {
+31
View File
@@ -691,6 +691,37 @@ func TestExtractProtocol(t *testing.T) {
}
}
// --- ParseDataAudioURL tests ---
func TestParseDataAudioURL(t *testing.T) {
tests := []struct {
name string
mediaURL string
wantFormat string
wantData string
wantOK bool
}{
{"valid mp3", "data:audio/mp3;base64,SGVsbG8=", "mp3", "SGVsbG8=", true},
{"valid wav", "data:audio/wav;base64,AAAA", "wav", "AAAA", true},
{"not audio", "data:image/png;base64,abc", "", "", false},
{"no comma", "data:audio/mp3;base64", "", "", false},
{"empty data", "data:audio/mp3;base64,", "", "", false},
{"empty string", "", "", "", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
format, data, ok := ParseDataAudioURL(tt.mediaURL)
if ok != tt.wantOK || format != tt.wantFormat || data != tt.wantData {
t.Errorf(
"ParseDataAudioURL(%q) = (%q, %q, %v), want (%q, %q, %v)",
tt.mediaURL, format, data, ok,
tt.wantFormat, tt.wantData, tt.wantOK,
)
}
})
}
}
// --- NormalizeAnthropicBaseURL tests ---
func TestNormalizeAnthropicBaseURL(t *testing.T) {
@@ -10,6 +10,7 @@ import (
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/responses"
"github.com/sipeed/picoclaw/pkg/providers/common"
"github.com/sipeed/picoclaw/pkg/providers/protocoltypes"
)
@@ -118,7 +119,7 @@ func BuildMultipartContent(text string, media []string) responses.ResponseInputM
},
})
} else if strings.HasPrefix(mediaURL, "data:audio/") {
if format, data, ok := ParseDataAudioURL(mediaURL); ok {
if format, data, ok := common.ParseDataAudioURL(mediaURL); ok {
parts = append(parts, responses.ResponseInputContentUnionParam{
OfInputFile: &responses.ResponseInputFileParam{
FileData: openai.Opt(data),
@@ -132,25 +133,6 @@ func BuildMultipartContent(text string, media []string) responses.ResponseInputM
return parts
}
// ParseDataAudioURL extracts the format and base64 data from a data:audio/... URL.
func ParseDataAudioURL(mediaURL string) (format, data string, ok bool) {
if !strings.HasPrefix(mediaURL, "data:audio/") {
return "", "", false
}
payload := strings.TrimPrefix(mediaURL, "data:audio/")
meta, data, found := strings.Cut(payload, ",")
if !found {
return "", "", false
}
format, _, _ = strings.Cut(meta, ";")
format = strings.TrimSpace(format)
data = strings.TrimSpace(data)
if format == "" || data == "" {
return "", "", false
}
return format, data, true
}
// ResolveToolCall extracts the function name and JSON arguments string from a ToolCall.
// Returns ok=false if the tool call has no name or if arguments fail to marshal.
func ResolveToolCall(tc protocoltypes.ToolCall) (name string, arguments string, ok bool) {
@@ -506,42 +506,6 @@ func TestParseResponseBody_CanceledStatus(t *testing.T) {
}
}
// --- ParseDataAudioURL tests ---
func TestParseDataAudioURL_Valid(t *testing.T) {
format, data, ok := ParseDataAudioURL("data:audio/mp3;base64,SGVsbG8=")
if !ok {
t.Fatal("expected ok=true")
}
if format != "mp3" {
t.Errorf("format = %q, want %q", format, "mp3")
}
if data != "SGVsbG8=" {
t.Errorf("data = %q, want %q", data, "SGVsbG8=")
}
}
func TestParseDataAudioURL_NotAudio(t *testing.T) {
_, _, ok := ParseDataAudioURL("data:image/png;base64,abc")
if ok {
t.Error("expected ok=false for non-audio URL")
}
}
func TestParseDataAudioURL_MalformedNoComma(t *testing.T) {
_, _, ok := ParseDataAudioURL("data:audio/mp3;base64")
if ok {
t.Error("expected ok=false for malformed URL")
}
}
func TestParseDataAudioURL_EmptyData(t *testing.T) {
_, _, ok := ParseDataAudioURL("data:audio/mp3;base64,")
if ok {
t.Error("expected ok=false for empty data")
}
}
// --- BuildMultipartContent tests ---
func TestBuildMultipartContent_TextOnly(t *testing.T) {