mirror of
https://github.com/sipeed/picoclaw.git
synced 2026-06-12 18:08:54 +00:00
09a0d19119
1. Add VLLM default API base (http://localhost:8000/v1) - Previously returned empty string, causing provider creation to fail 2. Implement MaxTokensField configuration - Add maxTokensField field to HTTPProvider - Add NewHTTPProviderWithMaxTokensField constructor - Use configured field name for max_tokens parameter - Fallback to model-based detection for backward compatibility 3. Add tests for VLLM, deepseek, ollama default API bases Example config usage: { "model_name": "glm-4", "model": "openai/glm-4", "max_tokens_field": "max_completion_tokens" } Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
254 lines
6.4 KiB
Go
254 lines
6.4 KiB
Go
// PicoClaw - Ultra-lightweight personal AI agent
|
|
// License: MIT
|
|
//
|
|
// Copyright (c) 2026 PicoClaw contributors
|
|
|
|
package providers
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/sipeed/picoclaw/pkg/config"
|
|
)
|
|
|
|
func TestExtractProtocol(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
model string
|
|
wantProtocol string
|
|
wantModelID string
|
|
}{
|
|
{
|
|
name: "openai with prefix",
|
|
model: "openai/gpt-4o",
|
|
wantProtocol: "openai",
|
|
wantModelID: "gpt-4o",
|
|
},
|
|
{
|
|
name: "anthropic with prefix",
|
|
model: "anthropic/claude-3-sonnet",
|
|
wantProtocol: "anthropic",
|
|
wantModelID: "claude-3-sonnet",
|
|
},
|
|
{
|
|
name: "no prefix - defaults to openai",
|
|
model: "gpt-4o",
|
|
wantProtocol: "openai",
|
|
wantModelID: "gpt-4o",
|
|
},
|
|
{
|
|
name: "groq with prefix",
|
|
model: "groq/llama-3.1-70b",
|
|
wantProtocol: "groq",
|
|
wantModelID: "llama-3.1-70b",
|
|
},
|
|
{
|
|
name: "empty string",
|
|
model: "",
|
|
wantProtocol: "openai",
|
|
wantModelID: "",
|
|
},
|
|
{
|
|
name: "with whitespace",
|
|
model: " openai/gpt-4 ",
|
|
wantProtocol: "openai",
|
|
wantModelID: "gpt-4",
|
|
},
|
|
{
|
|
name: "multiple slashes",
|
|
model: "nvidia/meta/llama-3.1-8b",
|
|
wantProtocol: "nvidia",
|
|
wantModelID: "meta/llama-3.1-8b",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
protocol, modelID := ExtractProtocol(tt.model)
|
|
if protocol != tt.wantProtocol {
|
|
t.Errorf("ExtractProtocol(%q) protocol = %q, want %q", tt.model, protocol, tt.wantProtocol)
|
|
}
|
|
if modelID != tt.wantModelID {
|
|
t.Errorf("ExtractProtocol(%q) modelID = %q, want %q", tt.model, modelID, tt.wantModelID)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCreateProviderFromConfig_OpenAI(t *testing.T) {
|
|
cfg := &config.ModelConfig{
|
|
ModelName: "test-openai",
|
|
Model: "openai/gpt-4o",
|
|
APIKey: "test-key",
|
|
APIBase: "https://api.example.com/v1",
|
|
}
|
|
|
|
provider, modelID, err := CreateProviderFromConfig(cfg)
|
|
if err != nil {
|
|
t.Fatalf("CreateProviderFromConfig() error = %v", err)
|
|
}
|
|
if provider == nil {
|
|
t.Fatal("CreateProviderFromConfig() returned nil provider")
|
|
}
|
|
if modelID != "gpt-4o" {
|
|
t.Errorf("modelID = %q, want %q", modelID, "gpt-4o")
|
|
}
|
|
}
|
|
|
|
func TestCreateProviderFromConfig_DefaultAPIBase(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
protocol string
|
|
wantBase string
|
|
}{
|
|
{"openai", "openai", "https://api.openai.com/v1"},
|
|
{"groq", "groq", "https://api.groq.com/openai/v1"},
|
|
{"openrouter", "openrouter", "https://openrouter.ai/api/v1"},
|
|
{"cerebras", "cerebras", "https://api.cerebras.ai/v1"},
|
|
{"qwen", "qwen", "https://dashscope.aliyuncs.com/compatible-mode/v1"},
|
|
{"vllm", "vllm", "http://localhost:8000/v1"},
|
|
{"deepseek", "deepseek", "https://api.deepseek.com/v1"},
|
|
{"ollama", "ollama", "http://localhost:11434/v1"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
cfg := &config.ModelConfig{
|
|
ModelName: "test-" + tt.protocol,
|
|
Model: tt.protocol + "/test-model",
|
|
APIKey: "test-key",
|
|
}
|
|
|
|
provider, _, err := CreateProviderFromConfig(cfg)
|
|
if err != nil {
|
|
t.Fatalf("CreateProviderFromConfig() error = %v", err)
|
|
}
|
|
|
|
httpProvider, ok := provider.(*HTTPProvider)
|
|
if !ok {
|
|
t.Fatalf("expected *HTTPProvider, got %T", provider)
|
|
}
|
|
if httpProvider.apiBase != tt.wantBase {
|
|
t.Errorf("apiBase = %q, want %q", httpProvider.apiBase, tt.wantBase)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCreateProviderFromConfig_Anthropic(t *testing.T) {
|
|
cfg := &config.ModelConfig{
|
|
ModelName: "test-anthropic",
|
|
Model: "anthropic/claude-3-sonnet",
|
|
APIKey: "test-key",
|
|
}
|
|
|
|
provider, modelID, err := CreateProviderFromConfig(cfg)
|
|
if err != nil {
|
|
t.Fatalf("CreateProviderFromConfig() error = %v", err)
|
|
}
|
|
if provider == nil {
|
|
t.Fatal("CreateProviderFromConfig() returned nil provider")
|
|
}
|
|
if modelID != "claude-3-sonnet" {
|
|
t.Errorf("modelID = %q, want %q", modelID, "claude-3-sonnet")
|
|
}
|
|
}
|
|
|
|
func TestCreateProviderFromConfig_Antigravity(t *testing.T) {
|
|
cfg := &config.ModelConfig{
|
|
ModelName: "test-antigravity",
|
|
Model: "antigravity/gemini-2.0-flash",
|
|
}
|
|
|
|
provider, modelID, err := CreateProviderFromConfig(cfg)
|
|
if err != nil {
|
|
t.Fatalf("CreateProviderFromConfig() error = %v", err)
|
|
}
|
|
if provider == nil {
|
|
t.Fatal("CreateProviderFromConfig() returned nil provider")
|
|
}
|
|
if modelID != "gemini-2.0-flash" {
|
|
t.Errorf("modelID = %q, want %q", modelID, "gemini-2.0-flash")
|
|
}
|
|
}
|
|
|
|
func TestCreateProviderFromConfig_ClaudeCLI(t *testing.T) {
|
|
cfg := &config.ModelConfig{
|
|
ModelName: "test-claude-cli",
|
|
Model: "claude-cli/claude-sonnet-4-20250514",
|
|
}
|
|
|
|
provider, modelID, err := CreateProviderFromConfig(cfg)
|
|
if err != nil {
|
|
t.Fatalf("CreateProviderFromConfig() error = %v", err)
|
|
}
|
|
if provider == nil {
|
|
t.Fatal("CreateProviderFromConfig() returned nil provider")
|
|
}
|
|
if modelID != "claude-sonnet-4-20250514" {
|
|
t.Errorf("modelID = %q, want %q", modelID, "claude-sonnet-4-20250514")
|
|
}
|
|
}
|
|
|
|
func TestCreateProviderFromConfig_CodexCLI(t *testing.T) {
|
|
cfg := &config.ModelConfig{
|
|
ModelName: "test-codex-cli",
|
|
Model: "codex-cli/codex",
|
|
}
|
|
|
|
provider, modelID, err := CreateProviderFromConfig(cfg)
|
|
if err != nil {
|
|
t.Fatalf("CreateProviderFromConfig() error = %v", err)
|
|
}
|
|
if provider == nil {
|
|
t.Fatal("CreateProviderFromConfig() returned nil provider")
|
|
}
|
|
if modelID != "codex" {
|
|
t.Errorf("modelID = %q, want %q", modelID, "codex")
|
|
}
|
|
}
|
|
|
|
func TestCreateProviderFromConfig_MissingAPIKey(t *testing.T) {
|
|
cfg := &config.ModelConfig{
|
|
ModelName: "test-no-key",
|
|
Model: "openai/gpt-4o",
|
|
}
|
|
|
|
_, _, err := CreateProviderFromConfig(cfg)
|
|
if err == nil {
|
|
t.Fatal("CreateProviderFromConfig() expected error for missing API key")
|
|
}
|
|
}
|
|
|
|
func TestCreateProviderFromConfig_UnknownProtocol(t *testing.T) {
|
|
cfg := &config.ModelConfig{
|
|
ModelName: "test-unknown",
|
|
Model: "unknown-protocol/model",
|
|
APIKey: "test-key",
|
|
}
|
|
|
|
_, _, err := CreateProviderFromConfig(cfg)
|
|
if err == nil {
|
|
t.Fatal("CreateProviderFromConfig() expected error for unknown protocol")
|
|
}
|
|
}
|
|
|
|
func TestCreateProviderFromConfig_NilConfig(t *testing.T) {
|
|
_, _, err := CreateProviderFromConfig(nil)
|
|
if err == nil {
|
|
t.Fatal("CreateProviderFromConfig(nil) expected error")
|
|
}
|
|
}
|
|
|
|
func TestCreateProviderFromConfig_EmptyModel(t *testing.T) {
|
|
cfg := &config.ModelConfig{
|
|
ModelName: "test-empty",
|
|
Model: "",
|
|
}
|
|
|
|
_, _, err := CreateProviderFromConfig(cfg)
|
|
if err == nil {
|
|
t.Fatal("CreateProviderFromConfig() expected error for empty model")
|
|
}
|
|
}
|