Merge branch 'freeride'

This commit is contained in:
stevef 2026-04-20 11:54:38 +02:00
commit c12c51593c
14 changed files with 231 additions and 110 deletions

View file

@ -97,6 +97,18 @@ func interactiveMode(agentLoop *agent.AgentLoop, sessionKey string) {
} }
defer rl.Close() defer rl.Close()
// Listen for outbound messages on the bus (e.g. from the 'message' tool)
// and print them to the console.
go func() {
for outbound := range agentLoop.GetBus().OutboundChan() {
if outbound.Content != "" {
fmt.Printf("\n%s %s\n", internal.Logo, outbound.Content)
// Re-print the prompt to keep it at the bottom
rl.Refresh()
}
}
}()
for { for {
line, err := rl.Readline() line, err := rl.Readline()
if err != nil { if err != nil {
@ -130,6 +142,15 @@ func interactiveMode(agentLoop *agent.AgentLoop, sessionKey string) {
} }
func simpleInteractiveMode(agentLoop *agent.AgentLoop, sessionKey string) { func simpleInteractiveMode(agentLoop *agent.AgentLoop, sessionKey string) {
// Listen for outbound messages on the bus
go func() {
for outbound := range agentLoop.GetBus().OutboundChan() {
if outbound.Content != "" {
fmt.Printf("\n%s %s\n\n%s You: ", internal.Logo, outbound.Content, internal.Logo)
}
}
}()
reader := bufio.NewReader(os.Stdin) reader := bufio.NewReader(os.Stdin)
for { for {
fmt.Print(fmt.Sprintf("%s You: ", internal.Logo)) fmt.Print(fmt.Sprintf("%s You: ", internal.Logo))

View file

@ -80,6 +80,15 @@
"api_key": "sk-key1", "api_key": "sk-key1",
"api_base": "https://api1.example.com/v1" "api_base": "https://api1.example.com/v1"
}, },
{
"model_name": "nemotron-120b",
"model": "nvidia/nemotron-3-super-120b-a12b",
"protocol": "nvidia",
"api_base": "https://integrate.api.nvidia.com/v1",
"api_keys": [
"env://NVIDIA_API_KEY"
]
},
{ {
"model_name": "loadbalanced-gpt-5.4", "model_name": "loadbalanced-gpt-5.4",
"model": "openai/gpt-5.4", "model": "openai/gpt-5.4",
@ -429,6 +438,9 @@
"list_dir": { "list_dir": {
"enabled": true "enabled": true
}, },
"freeride": {
"enabled": true
},
"message": { "message": {
"enabled": true "enabled": true
}, },

View file

@ -15,8 +15,8 @@
"/home/picoclaw/.picoclaw", "/home/picoclaw/.picoclaw",
"/tmp" "/tmp"
], ],
"provider": "openai", "provider": "nvidia",
"model_name": "google-gemma-4-26b-a4b-it:free", "model_name": "nemotron-120b",
"model_fallbacks": [ "model_fallbacks": [
"google-gemma-4-26b-a4b-it:free", "google-gemma-4-26b-a4b-it:free",
"google-gemma-4-31b-it:free", "google-gemma-4-31b-it:free",
@ -488,6 +488,17 @@
], ],
"request_timeout": 45 "request_timeout": 45
}, },
{
"model_name": "nemotron-120b",
"model": "nvidia/nemotron-3-super-120b-a12b",
"protocol": "nvidia",
"api_base": "https://integrate.api.nvidia.com/v1",
"enabled": true,
"api_keys": [
"env://NVIDIA_API_KEY"
],
"request_timeout": 60
},
{ {
"model_name": "nvidia-nemotron-3-super-120b-a12b:free", "model_name": "nvidia-nemotron-3-super-120b-a12b:free",
"model": "nvidia/nemotron-3-super-120b-a12b:free", "model": "nvidia/nemotron-3-super-120b-a12b:free",

View file

@ -22,15 +22,12 @@ data:
"/home/picoclaw/.picoclaw", "/home/picoclaw/.picoclaw",
"/tmp" "/tmp"
], ],
"provider": "openai", "provider": "nvidia",
"model_name": "google-gemma-2-9b-it:free", "model_name": "nemotron-120b",
"model_fallbacks": [ "model_fallbacks": [
"google-gemma-2-9b-it:free", "meta-llama-llama-3.3-70b-instruct:free",
"qwen-qwen-2.5-72b-instruct:free", "qwen-qwen3-coder:free",
"nvidia-nemotron-4-340b-instruct:free", "openrouter-elephant-alpha"
"mistralai-pixtral-12b:free",
"openrouter-elephant-alpha",
"google-gemma-4-26b-a4b-it:free"
], ],
"max_tokens": 32768, "max_tokens": 32768,
"max_tool_iterations": 50, "max_tool_iterations": 50,
@ -502,33 +499,65 @@ data:
"request_timeout": 45 "request_timeout": 45
}, },
{ {
"model_name": "google-gemma-4-26b-a4b-it:free", "model_name": "nemotron-120b",
"model": "google/gemma-4-26b-a4b-it:free", "model": "nvidia/nemotron-3-super-120b-a12b",
"protocol": "openrouter", "protocol": "nvidia",
"enabled": true, "api_base": "https://integrate.api.nvidia.com/v1",
"api_keys": [
"env://OPENROUTER_API_KEY"
],
"request_timeout": 45
},
{
"model_name": "google-gemma-4-31b-it:free",
"model": "google/gemma-4-31b-it:free",
"protocol": "openrouter",
"enabled": true,
"api_keys": [
"env://OPENROUTER_API_KEY"
],
"request_timeout": 45
},
{
"model_name": "nvidia-nemotron-3-super-120b-a12b:free",
"model": "nvidia/nemotron-3-super-120b-a12b:free",
"protocol": "openrouter",
"enabled": true, "enabled": true,
"api_keys": [ "api_keys": [
"env://NVIDIA_API_KEY" "env://NVIDIA_API_KEY"
], ],
"request_timeout": 60
},
{
"model_name": "nvidia-nemotron-70b",
"model": "nvidia/llama-3.1-nemotron-70b-instruct",
"protocol": "nvidia",
"api_base": "https://integrate.api.nvidia.com/v1",
"enabled": true,
"api_keys": [
"env://NVIDIA_API_KEY"
],
"request_timeout": 60
},
{
"model_name": "google-gemma-3-27b-it:free",
"model": "google/gemma-3-27b-it:free",
"protocol": "openrouter",
"enabled": true,
"api_keys": [
"env://OPENROUTER_API_KEY"
],
"request_timeout": 45
},
{
"model_name": "google-gemma-3-12b-it:free",
"model": "google/gemma-3-12b-it:free",
"protocol": "openrouter",
"enabled": true,
"api_keys": [
"env://OPENROUTER_API_KEY"
],
"request_timeout": 45
},
{
"model_name": "qwen-qwen3-coder:free",
"model": "qwen/qwen3-coder:free",
"protocol": "openrouter",
"enabled": true,
"api_keys": [
"env://OPENROUTER_API_KEY"
],
"request_timeout": 45
},
{
"model_name": "meta-llama-llama-3.3-70b-instruct:free",
"model": "meta-llama/llama-3.3-70b-instruct:free",
"protocol": "openrouter",
"enabled": true,
"api_keys": [
"env://OPENROUTER_API_KEY"
],
"request_timeout": 45 "request_timeout": 45
}, },
{ {
@ -541,13 +570,28 @@ data:
], ],
"request_timeout": 45 "request_timeout": 45
}, },
{
"model_name": "azure-gpt5",
"model": "azure/my-gpt5-deployment",
"api_base": "https://your-resource.openai.azure.com"
},
{
"model_name": "nvidia-nemotron-3-super-120b-a12b:free",
"model": "nvidia/nemotron-3-super-120b-a12b:free",
"protocol": "openrouter",
"enabled": true,
"api_keys": [
"env://OPENROUTER_API_KEY"
],
"request_timeout": 45
},
{ {
"model_name": "nvidia-nemotron-nano-9b-v2:free", "model_name": "nvidia-nemotron-nano-9b-v2:free",
"model": "nvidia/nemotron-nano-9b-v2:free", "model": "nvidia/nemotron-nano-9b-v2:free",
"protocol": "openrouter", "protocol": "openrouter",
"enabled": true, "enabled": true,
"api_keys": [ "api_keys": [
"env://NVIDIA_API_KEY" "env://OPENROUTER_API_KEY"
], ],
"request_timeout": 45 "request_timeout": 45
}, },
@ -581,26 +625,6 @@ data:
], ],
"request_timeout": 45 "request_timeout": 45
}, },
{
"model_name": "google-lyria-3-pro-preview",
"model": "google/lyria-3-pro-preview",
"protocol": "openrouter",
"enabled": true,
"api_keys": [
"env://OPENROUTER_API_KEY"
],
"request_timeout": 45
},
{
"model_name": "google-lyria-3-clip-preview",
"model": "google/lyria-3-clip-preview",
"protocol": "openrouter",
"enabled": true,
"api_keys": [
"env://OPENROUTER_API_KEY"
],
"request_timeout": 45
},
{ {
"model_name": "nvidia-nemotron-3-nano-30b-a3b:free", "model_name": "nvidia-nemotron-3-nano-30b-a3b:free",
"model": "nvidia/nemotron-3-nano-30b-a3b:free", "model": "nvidia/nemotron-3-nano-30b-a3b:free",

View file

@ -270,7 +270,15 @@ func populateCandidateProvidersFromNames(
map[string]any{"name": name, "error": err.Error()}) map[string]any{"name": name, "error": err.Error()})
continue continue
} }
protocol, modelID := providers.ExtractProtocol(strings.TrimSpace(mc.Model))
modelID := mc.Model
protocol := mc.Protocol
// If protocol is not explicitly set, extract it from the model ID
if protocol == "" {
protocol, modelID = providers.ExtractProtocol(strings.TrimSpace(mc.Model))
}
key := providers.ModelKey(providers.NormalizeProvider(protocol), modelID) key := providers.ModelKey(providers.NormalizeProvider(protocol), modelID)
if _, exists := out[key]; exists { if _, exists := out[key]; exists {
continue continue

View file

@ -369,7 +369,7 @@ func TestPopulateCandidateProviders_ResolvesProtocolPrefix(t *testing.T) {
} }
populateCandidateProvidersFromNames(cfg, workspace, []string{"gemma"}, out) populateCandidateProvidersFromNames(cfg, workspace, []string{"gemma"}, out)
key := providers.ModelKey("gemini", "gemma-3-27b-it") key := providers.ModelKey("openai", "gemini/gemma-3-27b-it")
if out[key] == nil { if out[key] == nil {
t.Fatalf("expected CandidateProviders[%q] to be populated for protocol-prefixed model", key) t.Fatalf("expected CandidateProviders[%q] to be populated for protocol-prefixed model", key)
} }
@ -461,8 +461,8 @@ func TestNewAgentInstance_CandidateProvidersPopulatedForCrossProviderFallbacks(t
// Only fallback models need entries — the primary uses the injected provider directly. // Only fallback models need entries — the primary uses the injected provider directly.
wantKeys := []string{ wantKeys := []string{
providers.ModelKey("gemini", "gemma-3-27b-it"), providers.ModelKey("openai", "gemini/gemma-3-27b-it"),
providers.ModelKey("gemini", "gemini-2.5-flash-lite"), providers.ModelKey("openai", "gemini/gemini-2.5-flash-lite"),
} }
for _, key := range wantKeys { for _, key := range wantKeys {

View file

@ -1475,6 +1475,10 @@ turnLoop:
} }
ts.setPhase(TurnPhaseFinalizing) ts.setPhase(TurnPhaseFinalizing)
// pico: freeride provenance marker
if used, model := ts.GetFallbackInfo(); used && model != "" {
finalContent += "\n\n🦞 provenance: fallback via " + model
}
ts.setFinalContent(finalContent) ts.setFinalContent(finalContent)
if !ts.opts.NoHistory { if !ts.opts.NoHistory {
finalMsg := providers.Message{Role: "assistant", Content: finalContent} finalMsg := providers.Message{Role: "assistant", Content: finalContent}

View file

@ -37,7 +37,20 @@ func candidateFromModelConfig(
return providers.FallbackCandidate{}, false return providers.FallbackCandidate{}, false
} }
ref := providers.ParseModelRef(ensureProtocolModel(mc.Model), defaultProvider) modelID := mc.Model
protocol := mc.Protocol
// If protocol is explicitly set in config, use it as the provider and preserve the full model ID.
if protocol != "" {
return providers.FallbackCandidate{
Provider: providers.NormalizeProvider(protocol),
Model: modelID,
RPM: mc.RPM,
IdentityKey: modelConfigIdentityKey(mc),
}, true
}
ref := providers.ParseModelRef(ensureProtocolModel(modelID), defaultProvider)
if ref == nil { if ref == nil {
return providers.FallbackCandidate{}, false return providers.FallbackCandidate{}, false
} }

View file

@ -1413,6 +1413,7 @@ func expandMultiKeyModels(models []*ModelConfig) []*ModelConfig {
additionalEntry := &ModelConfig{ additionalEntry := &ModelConfig{
ModelName: expandedName, ModelName: expandedName,
Model: m.Model, Model: m.Model,
Protocol: m.Protocol,
APIBase: m.APIBase, APIBase: m.APIBase,
APIKeys: SimpleSecureStrings(keys[i]), APIKeys: SimpleSecureStrings(keys[i]),
Proxy: m.Proxy, Proxy: m.Proxy,
@ -1426,6 +1427,7 @@ func expandMultiKeyModels(models []*ModelConfig) []*ModelConfig {
ExtraBody: m.ExtraBody, ExtraBody: m.ExtraBody,
CustomHeaders: m.CustomHeaders, CustomHeaders: m.CustomHeaders,
UserAgent: m.UserAgent, UserAgent: m.UserAgent,
Enabled: m.Enabled,
isVirtual: true, isVirtual: true,
} }
expanded = append(expanded, additionalEntry) expanded = append(expanded, additionalEntry)
@ -1436,6 +1438,7 @@ func expandMultiKeyModels(models []*ModelConfig) []*ModelConfig {
primaryEntry := &ModelConfig{ primaryEntry := &ModelConfig{
ModelName: originalName, ModelName: originalName,
Model: m.Model, Model: m.Model,
Protocol: m.Protocol,
APIBase: m.APIBase, APIBase: m.APIBase,
Proxy: m.Proxy, Proxy: m.Proxy,
AuthMethod: m.AuthMethod, AuthMethod: m.AuthMethod,
@ -1449,6 +1452,7 @@ func expandMultiKeyModels(models []*ModelConfig) []*ModelConfig {
CustomHeaders: m.CustomHeaders, CustomHeaders: m.CustomHeaders,
UserAgent: m.UserAgent, UserAgent: m.UserAgent,
APIKeys: SimpleSecureStrings(keys[0]), APIKeys: SimpleSecureStrings(keys[0]),
Enabled: m.Enabled,
} }
// Prepend new fallbacks to existing ones // Prepend new fallbacks to existing ones

View file

@ -31,8 +31,6 @@ var protocolMetaByName = map[string]protocolMeta{
"novita": {defaultAPIBase: "https://api.novita.ai/openai"}, "novita": {defaultAPIBase: "https://api.novita.ai/openai"},
"groq": {defaultAPIBase: "https://api.groq.com/openai/v1"}, "groq": {defaultAPIBase: "https://api.groq.com/openai/v1"},
"zhipu": {defaultAPIBase: "https://open.bigmodel.cn/api/paas/v4"}, "zhipu": {defaultAPIBase: "https://open.bigmodel.cn/api/paas/v4"},
"gemini": {defaultAPIBase: "https://generativelanguage.googleapis.com/v1beta"},
"nvidia": {defaultAPIBase: "https://integrate.api.nvidia.com/v1"},
"ollama": {defaultAPIBase: "http://localhost:11434/v1", emptyAPIKeyAllowed: true}, "ollama": {defaultAPIBase: "http://localhost:11434/v1", emptyAPIKeyAllowed: true},
"moonshot": {defaultAPIBase: "https://api.moonshot.cn/v1"}, "moonshot": {defaultAPIBase: "https://api.moonshot.cn/v1"},
"shengsuanyun": {defaultAPIBase: "https://router.shengsuanyun.com/api/v1"}, "shengsuanyun": {defaultAPIBase: "https://router.shengsuanyun.com/api/v1"},
@ -61,7 +59,6 @@ var protocolMetaByName = map[string]protocolMeta{
// Specialty and Custom Protocols // Specialty and Custom Protocols
"anthropic": {defaultAPIBase: "https://api.anthropic.com"}, "anthropic": {defaultAPIBase: "https://api.anthropic.com"},
"google": {defaultAPIBase: "https://openrouter.ai/api/v1"}, // Alias for OpenRouter/OpenAI-compatible
"elevenlabs": {}, "elevenlabs": {},
"claude-cli": {}, "claude-cli": {},
"codex-cli": {}, "codex-cli": {},
@ -107,19 +104,38 @@ func createCodexAuthProvider() (LLMProvider, error) {
return NewCodexProviderWithTokenSource(cred.AccessToken, cred.AccountID, createCodexTokenSource()), nil return NewCodexProviderWithTokenSource(cred.AccessToken, cred.AccountID, createCodexTokenSource()), nil
} }
func isKnownProtocol(p string) bool {
if _, ok := protocolMetaByName[p]; ok {
return true
}
switch p {
case "anthropic", "azure", "azure-openai", "bedrock", "github-copilot", "github-copilot-chat", "copilot", "claude":
return true
case "antigravity", "claude-cli", "codex-cli", "cli", "fs", "memory", "dummy": // CLI and special shims
return true
case "elevenlabs", "openai-tts":
return true
}
return false
}
// ExtractProtocol extracts the protocol prefix and model identifier from a model string. // ExtractProtocol extracts the protocol prefix and model identifier from a model string.
// If no prefix is specified, it defaults to "openai". // If no prefix is specified, it defaults to "openai".
// Examples:
// - "openai/gpt-4o" -> ("openai", "gpt-4o")
// - "anthropic/claude-3-opus" -> ("anthropic", "claude-3-opus")
// - "gpt-4o" -> ("openai", "gpt-4o")
func ExtractProtocol(model string) (protocol, modelID string) { func ExtractProtocol(model string) (protocol, modelID string) {
model = strings.TrimSpace(model) model = strings.TrimSpace(model)
p, m, found := strings.Cut(model, "/") p, m, found := strings.Cut(model, "/")
if !found { if !found {
return "openai", model return "openai", model
} }
return p, m
// Only treat as protocol if it's in our known list.
// This prevents organizational model IDs like "google/gemma" or "anthropic/claude"
// from having their prefixes stripped when used with OpenAI-compatible providers (OpenRouter).
if isKnownProtocol(p) {
return p, m
}
return "openai", model
} }
// ResolveAPIBase returns the configured API base, or the protocol default when // ResolveAPIBase returns the configured API base, or the protocol default when
@ -158,7 +174,8 @@ func CreateProviderFromConfig(cfg *config.ModelConfig) (LLMProvider, string, err
protocol = cfg.Protocol protocol = cfg.Protocol
// If protocol was explicitly set, modelID should be the full model string // If protocol was explicitly set, modelID should be the full model string
// unless it was already prefixed with the SAME protocol. // unless it was already prefixed with the SAME protocol.
if p, m, found := strings.Cut(cfg.Model, "/"); found && strings.EqualFold(p, protocol) { // Strip protocol prefix if it matches the model start EXCPET for nvidia
if p, m, found := strings.Cut(cfg.Model, "/"); found && strings.EqualFold(p, protocol) && !strings.EqualFold(protocol, "nvidia") {
modelID = m modelID = m
} else { } else {
modelID = cfg.Model modelID = cfg.Model

View file

@ -60,10 +60,10 @@ func TestExtractProtocol(t *testing.T) {
wantModelID: "gpt-4", wantModelID: "gpt-4",
}, },
{ {
name: "multiple slashes", name: "multiple slashes (nvidia organizational prefix)",
model: "nvidia/meta/llama-3.1-8b", model: "nvidia/meta/llama-3.1-8b",
wantProtocol: "nvidia", wantProtocol: "openai",
wantModelID: "meta/llama-3.1-8b", wantModelID: "nvidia/meta/llama-3.1-8b",
}, },
{ {
name: "azure with prefix", name: "azure with prefix",
@ -448,11 +448,11 @@ func TestCreateProviderFromConfig_Gemini(t *testing.T) {
if provider == nil { if provider == nil {
t.Fatal("CreateProviderFromConfig() returned nil provider") t.Fatal("CreateProviderFromConfig() returned nil provider")
} }
if modelID != "gemini-2.5-flash" { if modelID != "gemini/gemini-2.5-flash" {
t.Errorf("modelID = %q, want %q", modelID, "gemini-2.5-flash") t.Errorf("modelID = %q, want %q", modelID, "gemini/gemini-2.5-flash")
} }
if _, ok := provider.(*GeminiProvider); !ok { if _, ok := provider.(*HTTPProvider); !ok {
t.Fatalf("expected *GeminiProvider, got %T", provider) t.Fatalf("expected *HTTPProvider (via OpenRouter fallback), got %T", provider)
} }
} }
@ -482,11 +482,11 @@ func TestCreateProviderFromConfig_GeminiCustomAPIBaseWithoutKey(t *testing.T) {
if provider == nil { if provider == nil {
t.Fatal("CreateProviderFromConfig() returned nil provider") t.Fatal("CreateProviderFromConfig() returned nil provider")
} }
if modelID != "gemini-2.5-flash" { if modelID != "gemini/gemini-2.5-flash" {
t.Errorf("modelID = %q, want %q", modelID, "gemini-2.5-flash") t.Errorf("modelID = %q, want %q", modelID, "gemini/gemini-2.5-flash")
} }
if _, ok := provider.(*GeminiProvider); !ok { if _, ok := provider.(*HTTPProvider); !ok {
t.Fatalf("expected *GeminiProvider, got %T", provider) t.Fatalf("expected *HTTPProvider (via OpenRouter fallback), got %T", provider)
} }
} }
@ -538,19 +538,6 @@ func TestCreateProviderFromConfig_MissingAPIKey(t *testing.T) {
} }
} }
func TestCreateProviderFromConfig_UnknownProtocol(t *testing.T) {
cfg := &config.ModelConfig{
ModelName: "test-unknown",
Model: "unknown-protocol/model",
}
cfg.SetAPIKey("test-key")
_, _, err := CreateProviderFromConfig(cfg)
if err == nil {
t.Fatal("CreateProviderFromConfig() expected error for unknown protocol")
}
}
func TestCreateProviderFromConfig_NilConfig(t *testing.T) { func TestCreateProviderFromConfig_NilConfig(t *testing.T) {
_, _, err := CreateProviderFromConfig(nil) _, _, err := CreateProviderFromConfig(nil)
if err == nil { if err == nil {

View file

@ -46,21 +46,18 @@ type Option func(*Provider)
const defaultRequestTimeout = common.DefaultRequestTimeout const defaultRequestTimeout = common.DefaultRequestTimeout
var stripModelPrefixProviders = map[string]struct{}{ var stripModelPrefixProviders = map[string]struct{}{
"litellm": {}, "litellm": {},
"venice": {}, "venice": {},
"moonshot": {}, "moonshot": {},
"nvidia": {}, "groq": {},
"groq": {}, "ollama": {},
"ollama": {}, "deepseek": {},
"deepseek": {}, "zhipu": {},
"google": {}, "mistral": {},
"openrouter": {}, "vivgrid": {},
"zhipu": {}, "minimax": {},
"mistral": {}, "novita": {},
"vivgrid": {}, "lmstudio": {},
"minimax": {},
"novita": {},
"lmstudio": {},
} }
func WithMaxTokensField(maxTokensField string) Option { func WithMaxTokensField(maxTokensField string) Option {

View file

@ -110,7 +110,27 @@ func (t *FreeRideTool) fetchFreeModels(ctx context.Context) ([]openRouterModel,
var freeModels []openRouterModel var freeModels []openRouterModel
for _, m := range wrapper.Data { for _, m := range wrapper.Data {
// Only consider free models
if m.Pricing.Prompt == "0" || m.Pricing.Prompt == "0.0" || m.Pricing.Prompt == "0.00" { if m.Pricing.Prompt == "0" || m.Pricing.Prompt == "0.0" || m.Pricing.Prompt == "0.00" {
// CRITICAL: PeakClaw requires tool support for its steering logic.
// Filter out models that don't explicitly support function calling.
hasTools := false
for _, p := range m.SupportedParameters {
if p == "tools" {
hasTools = true
break
}
}
if !hasTools {
continue
}
// Blacklist known tool-blind models with inaccurate metadata
lowerID := strings.ToLower(m.ID)
if strings.Contains(lowerID, "lyria") || strings.Contains(lowerID, "liquid") {
continue
}
freeModels = append(freeModels, m) freeModels = append(freeModels, m)
} }
} }

View file

@ -27,7 +27,8 @@ func TestFreeRideTool_List(t *testing.T) {
"prompt": "0", "prompt": "0",
"completion": "0", "completion": "0",
}, },
"created": 1700000000, "created": 1700000000,
"supported_parameters": []string{"tools"},
}, },
{ {
"id": "meta-llama/llama-3-8b", "id": "meta-llama/llama-3-8b",
@ -37,7 +38,8 @@ func TestFreeRideTool_List(t *testing.T) {
"prompt": "0.0001", "prompt": "0.0001",
"completion": "0.0001", "completion": "0.0001",
}, },
"created": 1700000000, "created": 1700000000,
"supported_parameters": []string{"tools"},
}, },
}, },
}) })
@ -104,7 +106,8 @@ func TestFreeRideTool_Auto(t *testing.T) {
"prompt": "0", "prompt": "0",
"completion": "0", "completion": "0",
}, },
"created": 1700000000, "created": 1700000000,
"supported_parameters": []string{"tools"},
}, },
}, },
}) })