diff --git a/.gitignore b/.gitignore index 6ad4d78d6..0c1aca1e3 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,7 @@ bin/ *.out /picoclaw /picoclaw-test - +/docs # Picoclaw specific .picoclaw/ config.json diff --git a/go.mod b/go.mod index f4c233ea8..55eb6b100 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,6 @@ require ( github.com/chzyer/readline v1.5.1 github.com/google/uuid v1.6.0 github.com/gorilla/websocket v1.5.3 - github.com/larksuite/oapi-sdk-go/v3 v3.5.3 github.com/mymmrac/telego v1.6.0 github.com/open-dingtalk/dingtalk-stream-sdk-go v0.9.1 github.com/openai/openai-go/v3 v3.21.0 diff --git a/pkg/channels/manager.go b/pkg/channels/manager.go index b0e1416b6..39cec942a 100644 --- a/pkg/channels/manager.go +++ b/pkg/channels/manager.go @@ -71,18 +71,6 @@ func (m *Manager) initChannels() error { } } - if m.config.Channels.Feishu.Enabled { - logger.DebugC("channels", "Attempting to initialize Feishu channel") - feishu, err := NewFeishuChannel(m.config.Channels.Feishu, m.bus) - if err != nil { - logger.ErrorCF("channels", "Failed to initialize Feishu channel", map[string]interface{}{ - "error": err.Error(), - }) - } else { - m.channels["feishu"] = feishu - logger.InfoC("channels", "Feishu channel enabled successfully") - } - } if m.config.Channels.Discord.Enabled && m.config.Channels.Discord.Token != "" { logger.DebugC("channels", "Attempting to initialize Discord channel") diff --git a/pkg/providers/http_provider.go b/pkg/providers/http_provider.go index e982e0988..54dbd53cc 100644 --- a/pkg/providers/http_provider.go +++ b/pkg/providers/http_provider.go @@ -63,6 +63,14 @@ func (p *HTTPProvider) Chat(ctx context.Context, messages []Message, tools []Too requestBody["temperature"] = temperature } + // Add additional options (like chat_template_kwargs for Nvidia) + for k, v := range options { + if k == "max_tokens" || k == "temperature" || k == "model" || k == "messages" { + continue + } + requestBody[k] = v + } + jsonData, err := json.Marshal(requestBody) if err != nil { return nil, fmt.Errorf("failed to marshal request: %w", err) @@ -263,6 +271,14 @@ func CreateProvider(cfg *config.Config) (LLMProvider, error) { apiKey = cfg.Providers.VLLM.APIKey apiBase = cfg.Providers.VLLM.APIBase } + case "nvidia": + if cfg.Providers.Nvidia.APIKey != "" { + apiKey = cfg.Providers.Nvidia.APIKey + apiBase = cfg.Providers.Nvidia.APIBase + if apiBase == "" { + apiBase = "https://integrate.api.nvidia.com/v1" + } + } } } @@ -321,6 +337,13 @@ func CreateProvider(cfg *config.Config) (LLMProvider, error) { apiKey = cfg.Providers.VLLM.APIKey apiBase = cfg.Providers.VLLM.APIBase + case cfg.Providers.Nvidia.APIKey != "": + apiKey = cfg.Providers.Nvidia.APIKey + apiBase = cfg.Providers.Nvidia.APIBase + if apiBase == "" { + apiBase = "https://integrate.api.nvidia.com/v1" + } + default: if cfg.Providers.OpenRouter.APIKey != "" { apiKey = cfg.Providers.OpenRouter.APIKey diff --git a/pkg/providers/http_provider_test.go b/pkg/providers/http_provider_test.go new file mode 100644 index 000000000..cb5ef60ef --- /dev/null +++ b/pkg/providers/http_provider_test.go @@ -0,0 +1,71 @@ +package providers + +import ( + "context" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" +) + +func TestHTTPProvider_NvidiaOptions(t *testing.T) { + var capturedBody map[string]interface{} + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + t.Errorf("Expected POST request, got %s", r.Method) + } + if r.Header.Get("Authorization") != "Bearer test-key" { + t.Errorf("Expected Authorization header, got %s", r.Header.Get("Authorization")) + } + + err := json.NewDecoder(r.Body).Decode(&capturedBody) + if err != nil { + t.Errorf("Failed to decode request body: %v", err) + } + + resp := map[string]interface{}{ + "choices": []map[string]interface{}{ + { + "message": map[string]interface{}{ + "content": "Hello from Nvidia!", + }, + "finish_reason": "stop", + }, + }, + "usage": map[string]interface{}{ + "prompt_tokens": 10, + "completion_tokens": 5, + "total_tokens": 15, + }, + } + json.NewEncoder(w).Encode(resp) + })) + defer server.Close() + + provider := NewHTTPProvider("test-key", server.URL) + ctx := context.Background() + messages := []Message{{Role: "user", Content: "Hi"}} + options := map[string]interface{}{ + "chat_template_kwargs": map[string]interface{}{ + "thinking": true, + }, + "top_p": 1.0, + } + + resp, err := provider.Chat(ctx, messages, nil, "nvidia/kimi", options) + if err != nil { + t.Fatalf("Chat failed: %v", err) + } + + if resp.Content != "Hello from Nvidia!" { + t.Errorf("Expected content 'Hello from Nvidia!', got %s", resp.Content) + } + + // Verify captured body contains the custom options + if kwargs, ok := capturedBody["chat_template_kwargs"].(map[string]interface{}); !ok || !kwargs["thinking"].(bool) { + t.Errorf("Missing or incorrect chat_template_kwargs in request body: %v", capturedBody) + } + if capturedBody["top_p"].(float64) != 1.0 { + t.Errorf("Missing or incorrect top_p in request body: %v", capturedBody) + } +}