From 98f22691efc07be0f3d402809bc94aaed9b41c27 Mon Sep 17 00:00:00 2001 From: Krish Sharma Date: Tue, 26 May 2026 20:13:19 +0530 Subject: [PATCH] feat: add Cerebras provider configuration to backend and UI --- cmd/test/final_test.go | 3 +++ cmd/test/test_line_comment.go | 3 +++ cmd/test/verify_line_comment.go | 3 +++ debug/debug_json_repair.go | 3 +++ debug/debug_langchain.go | 3 +++ debug/debug_livereview_replica.go | 3 +++ debug/debug_raw_http.go | 3 +++ debug/test_ollama.go | 3 +++ internal/ai/langchain/provider.go | 7 ++++++ internal/aiconnectors/baseurl_defaults.go | 4 ++++ internal/aiconnectors/connector.go | 26 +++++++++++------------ scripts/create_live_plans.go | 3 +++ scripts/create_test_plans.go | 3 +++ tests/examples/final_line_comment.go | 3 +++ tests/examples/fix_gitlab_comments.go | 3 +++ tests/gitlab_comment_test.go | 2 +- tests/test_gitlab_note_webhook.go | 3 +++ tests/verify_line_comment.go | 3 +++ ui/src/pages/AIProviders/AIProviders.tsx | 11 ++++++++++ 19 files changed, 77 insertions(+), 15 deletions(-) diff --git a/cmd/test/final_test.go b/cmd/test/final_test.go index ff98f3e0..e6d93edb 100644 --- a/cmd/test/final_test.go +++ b/cmd/test/final_test.go @@ -1,3 +1,5 @@ +//go:build ignore + package main import ( @@ -54,3 +56,4 @@ func main_final_test() { fmt.Println("Comment posted successfully! Check GitLab to verify it's attached to the correct line.") } + diff --git a/cmd/test/test_line_comment.go b/cmd/test/test_line_comment.go index 72758112..cb16d5d5 100644 --- a/cmd/test/test_line_comment.go +++ b/cmd/test/test_line_comment.go @@ -1,3 +1,5 @@ +//go:build ignore + package main import ( @@ -55,3 +57,4 @@ func main_test_line_comment() { fmt.Println("Comment posted successfully! Check GitLab to verify it's attached to the correct line.") } + diff --git a/cmd/test/verify_line_comment.go b/cmd/test/verify_line_comment.go index 61d29a10..33386151 100644 --- a/cmd/test/verify_line_comment.go +++ b/cmd/test/verify_line_comment.go @@ -1,3 +1,5 @@ +//go:build ignore + package main import ( @@ -54,3 +56,4 @@ func main_verify_line_comment() { fmt.Println("Comment posted successfully! Check GitLab to verify it's attached to the correct line.") } + diff --git a/debug/debug_json_repair.go b/debug/debug_json_repair.go index 9518d0c1..35201dd7 100644 --- a/debug/debug_json_repair.go +++ b/debug/debug_json_repair.go @@ -1,3 +1,5 @@ +//go:build ignore + package main import ( @@ -32,3 +34,4 @@ func main() { valid := json.Valid([]byte(repaired)) fmt.Printf("Parse valid: %v\n", valid) } + diff --git a/debug/debug_langchain.go b/debug/debug_langchain.go index ca09ad0d..e9b4caf9 100644 --- a/debug/debug_langchain.go +++ b/debug/debug_langchain.go @@ -1,3 +1,5 @@ +//go:build ignore + package main import ( @@ -191,3 +193,4 @@ func testChat(ctx context.Context, llm llms.Model) { fmt.Printf("Total chunks received: %d\n", chunkCount) fmt.Printf("Full response: %s\n", fullResponse) } + diff --git a/debug/debug_livereview_replica.go b/debug/debug_livereview_replica.go index d07c0ebf..71c6759c 100644 --- a/debug/debug_livereview_replica.go +++ b/debug/debug_livereview_replica.go @@ -1,3 +1,5 @@ +//go:build ignore + package main import ( @@ -150,3 +152,4 @@ func main() { fmt.Printf("ERROR: %v\n", err) } } + diff --git a/debug/debug_raw_http.go b/debug/debug_raw_http.go index 1f6c32b4..c381bbd6 100644 --- a/debug/debug_raw_http.go +++ b/debug/debug_raw_http.go @@ -1,3 +1,5 @@ +//go:build ignore + package main import ( @@ -102,3 +104,4 @@ func min(a, b int) int { } return b } + diff --git a/debug/test_ollama.go b/debug/test_ollama.go index de9f843a..242dca71 100644 --- a/debug/test_ollama.go +++ b/debug/test_ollama.go @@ -1,3 +1,5 @@ +//go:build ignore + package main import ( @@ -124,3 +126,4 @@ func testChat(ctx context.Context, llm llms.Model) { fmt.Printf("Total chunks received: %d\n", chunkCount) fmt.Printf("Full response: %s\n", fullResponse) } + diff --git a/internal/ai/langchain/provider.go b/internal/ai/langchain/provider.go index 11785267..37b4aff4 100644 --- a/internal/ai/langchain/provider.go +++ b/internal/ai/langchain/provider.go @@ -330,6 +330,8 @@ func (p *LangchainProvider) MaxTokensPerBatch() int { return 16000 // DeepSeek chat/reasoner models case "openrouter": return 8000 // OpenRouter models commonly cap around 8k; stay conservative + case "cerebras": + return 8000 // Safe default token limit for Cerebras case "anthropic": return 20000 // Claude models default: @@ -372,6 +374,9 @@ func (p *LangchainProvider) initializeLLM() error { case "openrouter": p.baseURL = aiconnectors.ResolveBaseURLForProviderName(p.providerType, p.baseURL) return p.initializeOpenAILLM() + case "cerebras": + p.baseURL = aiconnectors.ResolveBaseURLForProviderName(p.providerType, p.baseURL) + return p.initializeOpenAILLM() case "anthropic", "claude": return p.initializeAnthropicLLM() default: @@ -604,6 +609,8 @@ func (p *LangchainProvider) getModelName() string { return "deepseek-chat" case "openrouter": return "deepseek/deepseek-r1-0528:free" + case "cerebras": + return "llama3.1-8b" case "anthropic", "claude": return "claude-haiku-4-5-20251001" case "ollama": diff --git a/internal/aiconnectors/baseurl_defaults.go b/internal/aiconnectors/baseurl_defaults.go index 24d158f6..63175213 100644 --- a/internal/aiconnectors/baseurl_defaults.go +++ b/internal/aiconnectors/baseurl_defaults.go @@ -9,6 +9,8 @@ func DefaultBaseURL(provider Provider) string { return "https://openrouter.ai/api/v1" case ProviderDeepSeek: return "https://api.deepseek.com/v1" + case ProviderCerebras: + return "https://api.cerebras.ai/v1" default: return "" } @@ -21,6 +23,8 @@ func DefaultBaseURLForProviderName(providerName string) string { return DefaultBaseURL(ProviderOpenRouter) case string(ProviderDeepSeek): return DefaultBaseURL(ProviderDeepSeek) + case string(ProviderCerebras): + return DefaultBaseURL(ProviderCerebras) default: return "" } diff --git a/internal/aiconnectors/connector.go b/internal/aiconnectors/connector.go index b6e289c6..fc6d99fb 100644 --- a/internal/aiconnectors/connector.go +++ b/internal/aiconnectors/connector.go @@ -33,6 +33,7 @@ const ( ProviderCohere Provider = "cohere" ProviderOllama Provider = "ollama" ProviderOpenRouter Provider = "openrouter" + ProviderCerebras Provider = "cerebras" ProviderLocalModel Provider = "local" ) @@ -86,6 +87,8 @@ func NewConnector(ctx context.Context, options ConnectorOptions) (*Connector, er model, err = createOllamaModel(ctx, options) case ProviderOpenRouter: model, err = createOpenRouterModel(ctx, options) + case ProviderCerebras: + model, err = createOpenAIModel(ctx, options) default: return nil, fmt.Errorf("unsupported provider: %s", options.Provider) } @@ -162,6 +165,8 @@ func ValidateAPIKey(ctx context.Context, provider Provider, apiKey string, baseU options.ModelConfig.Model = "llama3" case ProviderOpenRouter: options.ModelConfig.Model = "deepseek/deepseek-r1-0528:free" + case ProviderCerebras: + options.ModelConfig.Model = "llama3.1-8b" default: log.Error().Str("provider", string(provider)).Msg("Unsupported provider") return false, fmt.Errorf("unsupported provider: %s", provider) @@ -294,7 +299,7 @@ func trimTrailingSlash(s string) string { // Helper functions to create models for specific providers -func createOpenAIModel(ctx context.Context, options ConnectorOptions) (llms.Model, error) { +func createOpenAIModel(_ context.Context, options ConnectorOptions) (llms.Model, error) { // The OpenAI library doesn't have all the options we want to set directly as constructor options // We'll just use the basic options available opts := []openai.Option{ @@ -310,7 +315,7 @@ func createOpenAIModel(ctx context.Context, options ConnectorOptions) (llms.Mode return openai.New(opts...) } -func createDeepSeekModel(ctx context.Context, options ConnectorOptions) (llms.Model, error) { +func createDeepSeekModel(_ context.Context, options ConnectorOptions) (llms.Model, error) { baseURL := ResolveBaseURL(ProviderDeepSeek, options.BaseURL) opts := []openai.Option{ @@ -352,7 +357,7 @@ func createGeminiModel(ctx context.Context, options ConnectorOptions) (llms.Mode return model, nil } -func createAnthropicModel(ctx context.Context, options ConnectorOptions) (llms.Model, error) { +func createAnthropicModel(_ context.Context, options ConnectorOptions) (llms.Model, error) { opts := []anthropic.Option{ anthropic.WithToken(options.APIKey), anthropic.WithModel(options.ModelConfig.Model), @@ -361,7 +366,7 @@ func createAnthropicModel(ctx context.Context, options ConnectorOptions) (llms.M return anthropic.New(opts...) } -func createCohereModel(ctx context.Context, options ConnectorOptions) (llms.Model, error) { +func createCohereModel(_ context.Context, options ConnectorOptions) (llms.Model, error) { opts := []cohere.Option{ cohere.WithToken(options.APIKey), cohere.WithModel(options.ModelConfig.Model), @@ -375,7 +380,7 @@ func createCohereModel(ctx context.Context, options ConnectorOptions) (llms.Mode return cohere.New(opts...) } -func createOllamaModel(ctx context.Context, options ConnectorOptions) (llms.Model, error) { +func createOllamaModel(_ context.Context, options ConnectorOptions) (llms.Model, error) { // Set default server URL if not provided if options.BaseURL == "" { options.BaseURL = "http://localhost:11434" @@ -392,7 +397,7 @@ func createOllamaModel(ctx context.Context, options ConnectorOptions) (llms.Mode return ollama.New(opts...) } -func createOpenRouterModel(ctx context.Context, options ConnectorOptions) (llms.Model, error) { +func createOpenRouterModel(_ context.Context, options ConnectorOptions) (llms.Model, error) { baseURL := ResolveBaseURL(ProviderOpenRouter, options.BaseURL) httpClient := networkaiconnectors.NewHTTPClient(5 * time.Minute) @@ -503,7 +508,7 @@ func (c *Connector) Call(ctx context.Context, input string, options ...llms.Call func isCloudProviderProvider(provider Provider) bool { switch provider { - case ProviderOpenAI, ProviderDeepSeek, ProviderGemini, ProviderClaude, ProviderOpenRouter: + case ProviderOpenAI, ProviderDeepSeek, ProviderGemini, ProviderClaude, ProviderOpenRouter, ProviderCerebras: return true default: return false @@ -520,13 +525,6 @@ func (c *Connector) GetModel() string { return c.options.ModelConfig.Model } -// Helper function to get minimum of two integers -func min(a, b int) int { - if a < b { - return a - } - return b -} // truncateString limits string length for safe logging. func truncateString(s string, maxLen int) string { diff --git a/scripts/create_live_plans.go b/scripts/create_live_plans.go index 997262fa..dfa0ab2c 100644 --- a/scripts/create_live_plans.go +++ b/scripts/create_live_plans.go @@ -1,3 +1,5 @@ +//go:build ignore + package main import ( @@ -59,3 +61,4 @@ func main() { fmt.Printf("TeamMonthlyPlanID = \"%s\"\n", monthlyLive.ID) fmt.Printf("TeamYearlyPlanID = \"%s\"\n", yearlyLive.ID) } + diff --git a/scripts/create_test_plans.go b/scripts/create_test_plans.go index ea560a5f..c4ee5945 100644 --- a/scripts/create_test_plans.go +++ b/scripts/create_test_plans.go @@ -1,3 +1,5 @@ +//go:build ignore + package main import ( @@ -28,3 +30,4 @@ func main() { fmt.Printf(" TeamMonthlyPlanIDTest = \"%s\"\n", monthlyPlan.ID) fmt.Printf(" TeamYearlyPlanIDTest = \"%s\"\n", yearlyPlan.ID) } + diff --git a/tests/examples/final_line_comment.go b/tests/examples/final_line_comment.go index e7916fcc..7f56b624 100644 --- a/tests/examples/final_line_comment.go +++ b/tests/examples/final_line_comment.go @@ -1,3 +1,5 @@ +//go:build ignore + package main import ( @@ -54,3 +56,4 @@ func main() { fmt.Println("Comment posted successfully! Check GitLab to verify it's attached to the correct line.") } + diff --git a/tests/examples/fix_gitlab_comments.go b/tests/examples/fix_gitlab_comments.go index 7134167e..fbb7f20b 100644 --- a/tests/examples/fix_gitlab_comments.go +++ b/tests/examples/fix_gitlab_comments.go @@ -1,3 +1,5 @@ +//go:build ignore + package main import ( @@ -49,3 +51,4 @@ func main() { fmt.Println("\nAfter running the tests, implement the specific fix in http_client.go") fmt.Println("based on which approach was most successful in posting line comments.") } + diff --git a/tests/gitlab_comment_test.go b/tests/gitlab_comment_test.go index 3cc23bc0..f791c5d4 100644 --- a/tests/gitlab_comment_test.go +++ b/tests/gitlab_comment_test.go @@ -1,4 +1,4 @@ -package livereview +package livereview_test import ( "context" diff --git a/tests/test_gitlab_note_webhook.go b/tests/test_gitlab_note_webhook.go index f9e2debd..f91068be 100644 --- a/tests/test_gitlab_note_webhook.go +++ b/tests/test_gitlab_note_webhook.go @@ -1,3 +1,5 @@ +//go:build ignore + package main import ( @@ -315,3 +317,4 @@ func sendTestWebhook(payload TestGitLabNotePayload, testName string) { fmt.Printf(" Response: %s\n", responseBody.String()) } } + diff --git a/tests/verify_line_comment.go b/tests/verify_line_comment.go index c60d4b54..109ff572 100644 --- a/tests/verify_line_comment.go +++ b/tests/verify_line_comment.go @@ -1,3 +1,5 @@ +//go:build ignore + package main import ( @@ -101,3 +103,4 @@ func main() { fmt.Println("Comment posted successfully! Check GitLab to verify it's attached to the correct line.") } + diff --git a/ui/src/pages/AIProviders/AIProviders.tsx b/ui/src/pages/AIProviders/AIProviders.tsx index b3131cd5..8c2e9bf7 100644 --- a/ui/src/pages/AIProviders/AIProviders.tsx +++ b/ui/src/pages/AIProviders/AIProviders.tsx @@ -67,6 +67,17 @@ const popularAIProviders: AIProvider[] = [ defaultModel: 'deepseek/deepseek-r1-0528:free', baseURLPlaceholder: 'https://openrouter.ai/api/v1' }, + { + id: 'cerebras', + name: 'Cerebras', + url: 'https://cerebras.ai/', + description: 'Lightning fast AI inference with Llama models.', + icon: , + apiKeyPlaceholder: 'csk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', + models: ['llama3.1-8b', 'llama3.3-70b'], + defaultModel: 'llama3.1-8b', + baseURLPlaceholder: 'https://api.cerebras.ai/v1' + }, { id: 'ollama', name: 'Ollama',