mirror of
https://github.com/sst/opencode.git
synced 2025-07-07 16:14:59 +00:00
feat: support VertexAI provider (#153)
* support: vertexai fix fix set default for vertexai added comment fix fix * create schema * fix README.md * fix order * added pupularity * set tools if tools is exists restore commentout * fix comment * set summarizer model
This commit is contained in:
parent
5f5f9dad87
commit
87237b6462
9 changed files with 272 additions and 111 deletions
|
@ -74,6 +74,8 @@ You can configure OpenCode using environment variables:
|
|||
| `ANTHROPIC_API_KEY` | For Claude models |
|
||||
| `OPENAI_API_KEY` | For OpenAI models |
|
||||
| `GEMINI_API_KEY` | For Google Gemini models |
|
||||
| `VERTEXAI_PROJECT` | For Google Cloud VertexAI (Gemini) |
|
||||
| `VERTEXAI_LOCATION` | For Google Cloud VertexAI (Gemini) |
|
||||
| `GROQ_API_KEY` | For Groq models |
|
||||
| `AWS_ACCESS_KEY_ID` | For AWS Bedrock (Claude) |
|
||||
| `AWS_SECRET_ACCESS_KEY` | For AWS Bedrock (Claude) |
|
||||
|
@ -189,6 +191,11 @@ OpenCode supports a variety of AI models from different providers:
|
|||
- O3 family (o3, o3-mini)
|
||||
- O4 Mini
|
||||
|
||||
### Google Cloud VertexAI
|
||||
|
||||
- Gemini 2.5
|
||||
- Gemini 2.5 Flash
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
|
|
|
@ -227,6 +227,7 @@ func generateSchema() map[string]any {
|
|||
string(models.ProviderOpenRouter),
|
||||
string(models.ProviderBedrock),
|
||||
string(models.ProviderAzure),
|
||||
string(models.ProviderVertexAI),
|
||||
}
|
||||
|
||||
providerSchema["additionalProperties"].(map[string]any)["properties"].(map[string]any)["provider"] = map[string]any{
|
||||
|
|
|
@ -235,6 +235,7 @@ func setProviderDefaults() {
|
|||
// 5. OpenRouter
|
||||
// 6. AWS Bedrock
|
||||
// 7. Azure
|
||||
// 8. Google Cloud VertexAI
|
||||
|
||||
// Anthropic configuration
|
||||
if key := viper.GetString("providers.anthropic.apiKey"); strings.TrimSpace(key) != "" {
|
||||
|
@ -299,6 +300,15 @@ func setProviderDefaults() {
|
|||
viper.SetDefault("agents.title.model", models.AzureGPT41Mini)
|
||||
return
|
||||
}
|
||||
|
||||
// Google Cloud VertexAI configuration
|
||||
if hasVertexAICredentials() {
|
||||
viper.SetDefault("agents.coder.model", models.VertexAIGemini25)
|
||||
viper.SetDefault("agents.summarizer.model", models.VertexAIGemini25)
|
||||
viper.SetDefault("agents.task.model", models.VertexAIGemini25Flash)
|
||||
viper.SetDefault("agents.title.model", models.VertexAIGemini25Flash)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// hasAWSCredentials checks if AWS credentials are available in the environment.
|
||||
|
@ -327,6 +337,19 @@ func hasAWSCredentials() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// hasVertexAICredentials checks if VertexAI credentials are available in the environment.
|
||||
func hasVertexAICredentials() bool {
|
||||
// Check for explicit VertexAI parameters
|
||||
if os.Getenv("VERTEXAI_PROJECT") != "" && os.Getenv("VERTEXAI_LOCATION") != "" {
|
||||
return true
|
||||
}
|
||||
// Check for Google Cloud project and location
|
||||
if os.Getenv("GOOGLE_CLOUD_PROJECT") != "" && (os.Getenv("GOOGLE_CLOUD_REGION") != "" || os.Getenv("GOOGLE_CLOUD_LOCATION") != "") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// readConfig handles the result of reading a configuration file.
|
||||
func readConfig(err error) error {
|
||||
if err == nil {
|
||||
|
@ -549,6 +572,10 @@ func getProviderAPIKey(provider models.ModelProvider) string {
|
|||
if hasAWSCredentials() {
|
||||
return "aws-credentials-available"
|
||||
}
|
||||
case models.ProviderVertexAI:
|
||||
if hasVertexAICredentials() {
|
||||
return "vertex-ai-credentials-available"
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
@ -669,6 +696,24 @@ func setDefaultModelForAgent(agent AgentName) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
if hasVertexAICredentials() {
|
||||
var model models.ModelID
|
||||
maxTokens := int64(5000)
|
||||
|
||||
if agent == AgentTitle {
|
||||
model = models.VertexAIGemini25Flash
|
||||
maxTokens = 80
|
||||
} else {
|
||||
model = models.VertexAIGemini25
|
||||
}
|
||||
|
||||
cfg.Agents[agent] = Agent{
|
||||
Model: model,
|
||||
MaxTokens: maxTokens,
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@ var ProviderPopularity = map[ModelProvider]int{
|
|||
ProviderOpenRouter: 5,
|
||||
ProviderBedrock: 6,
|
||||
ProviderAzure: 7,
|
||||
ProviderVertexAI: 8,
|
||||
}
|
||||
|
||||
var SupportedModels = map[ModelID]Model{
|
||||
|
@ -95,4 +96,5 @@ func init() {
|
|||
maps.Copy(SupportedModels, AzureModels)
|
||||
maps.Copy(SupportedModels, OpenRouterModels)
|
||||
maps.Copy(SupportedModels, XAIModels)
|
||||
maps.Copy(SupportedModels, VertexAIGeminiModels)
|
||||
}
|
||||
|
|
38
internal/llm/models/vertexai.go
Normal file
38
internal/llm/models/vertexai.go
Normal file
|
@ -0,0 +1,38 @@
|
|||
package models
|
||||
|
||||
const (
|
||||
ProviderVertexAI ModelProvider = "vertexai"
|
||||
|
||||
// Models
|
||||
VertexAIGemini25Flash ModelID = "vertexai.gemini-2.5-flash"
|
||||
VertexAIGemini25 ModelID = "vertexai.gemini-2.5"
|
||||
)
|
||||
|
||||
var VertexAIGeminiModels = map[ModelID]Model{
|
||||
VertexAIGemini25Flash: {
|
||||
ID: VertexAIGemini25Flash,
|
||||
Name: "VertexAI: Gemini 2.5 Flash",
|
||||
Provider: ProviderVertexAI,
|
||||
APIModel: "gemini-2.5-flash-preview-04-17",
|
||||
CostPer1MIn: GeminiModels[Gemini25Flash].CostPer1MIn,
|
||||
CostPer1MInCached: GeminiModels[Gemini25Flash].CostPer1MInCached,
|
||||
CostPer1MOut: GeminiModels[Gemini25Flash].CostPer1MOut,
|
||||
CostPer1MOutCached: GeminiModels[Gemini25Flash].CostPer1MOutCached,
|
||||
ContextWindow: GeminiModels[Gemini25Flash].ContextWindow,
|
||||
DefaultMaxTokens: GeminiModels[Gemini25Flash].DefaultMaxTokens,
|
||||
SupportsAttachments: true,
|
||||
},
|
||||
VertexAIGemini25: {
|
||||
ID: VertexAIGemini25,
|
||||
Name: "VertexAI: Gemini 2.5 Pro",
|
||||
Provider: ProviderVertexAI,
|
||||
APIModel: "gemini-2.5-pro-preview-03-25",
|
||||
CostPer1MIn: GeminiModels[Gemini25].CostPer1MIn,
|
||||
CostPer1MInCached: GeminiModels[Gemini25].CostPer1MInCached,
|
||||
CostPer1MOut: GeminiModels[Gemini25].CostPer1MOut,
|
||||
CostPer1MOutCached: GeminiModels[Gemini25].CostPer1MOutCached,
|
||||
ContextWindow: GeminiModels[Gemini25].ContextWindow,
|
||||
DefaultMaxTokens: GeminiModels[Gemini25].DefaultMaxTokens,
|
||||
SupportsAttachments: true,
|
||||
},
|
||||
}
|
|
@ -176,13 +176,16 @@ func (g *geminiClient) send(ctx context.Context, messages []message.Message, too
|
|||
|
||||
history := geminiMessages[:len(geminiMessages)-1] // All but last message
|
||||
lastMsg := geminiMessages[len(geminiMessages)-1]
|
||||
chat, _ := g.client.Chats.Create(ctx, g.providerOptions.model.APIModel, &genai.GenerateContentConfig{
|
||||
config := &genai.GenerateContentConfig{
|
||||
MaxOutputTokens: int32(g.providerOptions.maxTokens),
|
||||
SystemInstruction: &genai.Content{
|
||||
Parts: []*genai.Part{{Text: g.providerOptions.systemMessage}},
|
||||
},
|
||||
Tools: g.convertTools(tools),
|
||||
}, history)
|
||||
}
|
||||
if len(tools) > 0 {
|
||||
config.Tools = g.convertTools(tools)
|
||||
}
|
||||
chat, _ := g.client.Chats.Create(ctx, g.providerOptions.model.APIModel, config, history)
|
||||
|
||||
attempts := 0
|
||||
for {
|
||||
|
@ -262,13 +265,16 @@ func (g *geminiClient) stream(ctx context.Context, messages []message.Message, t
|
|||
|
||||
history := geminiMessages[:len(geminiMessages)-1] // All but last message
|
||||
lastMsg := geminiMessages[len(geminiMessages)-1]
|
||||
chat, _ := g.client.Chats.Create(ctx, g.providerOptions.model.APIModel, &genai.GenerateContentConfig{
|
||||
config := &genai.GenerateContentConfig{
|
||||
MaxOutputTokens: int32(g.providerOptions.maxTokens),
|
||||
SystemInstruction: &genai.Content{
|
||||
Parts: []*genai.Part{{Text: g.providerOptions.systemMessage}},
|
||||
},
|
||||
Tools: g.convertTools(tools),
|
||||
}, history)
|
||||
}
|
||||
if len(tools) > 0 {
|
||||
config.Tools = g.convertTools(tools)
|
||||
}
|
||||
chat, _ := g.client.Chats.Create(ctx, g.providerOptions.model.APIModel, config, history)
|
||||
|
||||
attempts := 0
|
||||
eventChan := make(chan ProviderEvent)
|
||||
|
|
|
@ -123,6 +123,11 @@ func NewProvider(providerName models.ModelProvider, opts ...ProviderClientOption
|
|||
options: clientOptions,
|
||||
client: newAzureClient(clientOptions),
|
||||
}, nil
|
||||
case models.ProviderVertexAI:
|
||||
return &baseProvider[VertexAIClient]{
|
||||
options: clientOptions,
|
||||
client: newVertexAIClient(clientOptions),
|
||||
}, nil
|
||||
case models.ProviderOpenRouter:
|
||||
clientOptions.openaiOptions = append(clientOptions.openaiOptions,
|
||||
WithOpenAIBaseURL("https://openrouter.ai/api/v1"),
|
||||
|
|
34
internal/llm/provider/vertexai.go
Normal file
34
internal/llm/provider/vertexai.go
Normal file
|
@ -0,0 +1,34 @@
|
|||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"github.com/opencode-ai/opencode/internal/logging"
|
||||
"google.golang.org/genai"
|
||||
)
|
||||
|
||||
type VertexAIClient ProviderClient
|
||||
|
||||
func newVertexAIClient(opts providerClientOptions) VertexAIClient {
|
||||
geminiOpts := geminiOptions{}
|
||||
for _, o := range opts.geminiOptions {
|
||||
o(&geminiOpts)
|
||||
}
|
||||
|
||||
client, err := genai.NewClient(context.Background(), &genai.ClientConfig{
|
||||
Project: os.Getenv("VERTEXAI_PROJECT"),
|
||||
Location: os.Getenv("VERTEXAI_LOCATION"),
|
||||
Backend: genai.BackendVertexAI,
|
||||
})
|
||||
if err != nil {
|
||||
logging.Error("Failed to create VertexAI client", "error", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return &geminiClient{
|
||||
providerOptions: opts,
|
||||
options: geminiOpts,
|
||||
client: client,
|
||||
}
|
||||
}
|
|
@ -12,63 +12,74 @@
|
|||
"model": {
|
||||
"description": "Model ID for the agent",
|
||||
"enum": [
|
||||
"azure.o1-mini",
|
||||
"openrouter.gemini-2.5-flash",
|
||||
"claude-3-haiku",
|
||||
"o1-mini",
|
||||
"qwen-qwq",
|
||||
"llama-3.3-70b-versatile",
|
||||
"openrouter.claude-3.5-sonnet",
|
||||
"o3-mini",
|
||||
"o4-mini",
|
||||
"gpt-4.1",
|
||||
"azure.o3-mini",
|
||||
"openrouter.gpt-4.1-nano",
|
||||
"openrouter.gpt-4o",
|
||||
"gemini-2.5",
|
||||
"azure.gpt-4o",
|
||||
"azure.gpt-4o-mini",
|
||||
"claude-3.7-sonnet",
|
||||
"azure.gpt-4.1-nano",
|
||||
"openrouter.o1",
|
||||
"openrouter.claude-3-haiku",
|
||||
"bedrock.claude-3.7-sonnet",
|
||||
"gemini-2.5-flash",
|
||||
"azure.o3",
|
||||
"openrouter.gemini-2.5",
|
||||
"openrouter.o3",
|
||||
"openrouter.o3-mini",
|
||||
"openrouter.gpt-4.1-mini",
|
||||
"openrouter.gpt-4.5-preview",
|
||||
"openrouter.gpt-4o-mini",
|
||||
"gpt-4.1-mini",
|
||||
"meta-llama/llama-4-scout-17b-16e-instruct",
|
||||
"openrouter.o1-mini",
|
||||
"gpt-4.5-preview",
|
||||
"o3",
|
||||
"openrouter.claude-3.5-haiku",
|
||||
"claude-3-opus",
|
||||
"o1-pro",
|
||||
"gemini-2.0-flash",
|
||||
"azure.o4-mini",
|
||||
"openrouter.o4-mini",
|
||||
"claude-3.5-sonnet",
|
||||
"meta-llama/llama-4-maverick-17b-128e-instruct",
|
||||
"azure.o1",
|
||||
"openrouter.gpt-4.1",
|
||||
"openrouter.o1-pro",
|
||||
"gpt-4.1-nano",
|
||||
"azure.gpt-4.5-preview",
|
||||
"openrouter.claude-3-opus",
|
||||
"gpt-4o-mini",
|
||||
"meta-llama/llama-4-scout-17b-16e-instruct",
|
||||
"openrouter.gpt-4o",
|
||||
"o1-pro",
|
||||
"claude-3-haiku",
|
||||
"o1",
|
||||
"deepseek-r1-distill-llama-70b",
|
||||
"azure.gpt-4.1",
|
||||
"gpt-4o",
|
||||
"azure.gpt-4.1-mini",
|
||||
"openrouter.claude-3.7-sonnet",
|
||||
"gemini-2.5-flash",
|
||||
"vertexai.gemini-2.5-flash",
|
||||
"claude-3.5-haiku",
|
||||
"gemini-2.0-flash-lite"
|
||||
"gpt-4o-mini",
|
||||
"o3-mini",
|
||||
"gpt-4.5-preview",
|
||||
"azure.gpt-4o",
|
||||
"azure.o4-mini",
|
||||
"openrouter.claude-3.5-sonnet",
|
||||
"gpt-4o",
|
||||
"o3",
|
||||
"gpt-4.1-mini",
|
||||
"llama-3.3-70b-versatile",
|
||||
"azure.gpt-4o-mini",
|
||||
"gpt-4.1-nano",
|
||||
"o4-mini",
|
||||
"qwen-qwq",
|
||||
"openrouter.claude-3.5-haiku",
|
||||
"openrouter.qwen-3-14b",
|
||||
"vertexai.gemini-2.5",
|
||||
"gemini-2.5",
|
||||
"azure.gpt-4.1-nano",
|
||||
"openrouter.o1-mini",
|
||||
"openrouter.qwen-3-30b",
|
||||
"claude-3.7-sonnet",
|
||||
"claude-3.5-sonnet",
|
||||
"gemini-2.0-flash",
|
||||
"meta-llama/llama-4-maverick-17b-128e-instruct",
|
||||
"openrouter.o3-mini",
|
||||
"openrouter.o4-mini",
|
||||
"openrouter.gpt-4.1-mini",
|
||||
"openrouter.o1",
|
||||
"o1-mini",
|
||||
"azure.gpt-4.1-mini",
|
||||
"openrouter.o1-pro",
|
||||
"grok-3-beta",
|
||||
"grok-3-mini-fast-beta",
|
||||
"openrouter.claude-3.7-sonnet",
|
||||
"openrouter.claude-3-opus",
|
||||
"openrouter.qwen-3-235b",
|
||||
"openrouter.gpt-4.1-nano",
|
||||
"bedrock.claude-3.7-sonnet",
|
||||
"openrouter.qwen-3-8b",
|
||||
"claude-3-opus",
|
||||
"azure.o1-mini",
|
||||
"deepseek-r1-distill-llama-70b",
|
||||
"gemini-2.0-flash-lite",
|
||||
"openrouter.qwen-3-32b",
|
||||
"openrouter.gpt-4.5-preview",
|
||||
"grok-3-mini-beta",
|
||||
"grok-3-fast-beta",
|
||||
"azure.o3-mini",
|
||||
"openrouter.claude-3-haiku",
|
||||
"azure.gpt-4.1",
|
||||
"azure.o1",
|
||||
"azure.o3",
|
||||
"azure.gpt-4.5-preview",
|
||||
"openrouter.gemini-2.5-flash",
|
||||
"openrouter.gpt-4o-mini",
|
||||
"openrouter.gemini-2.5"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
|
@ -102,63 +113,74 @@
|
|||
"model": {
|
||||
"description": "Model ID for the agent",
|
||||
"enum": [
|
||||
"azure.o1-mini",
|
||||
"openrouter.gemini-2.5-flash",
|
||||
"claude-3-haiku",
|
||||
"o1-mini",
|
||||
"qwen-qwq",
|
||||
"llama-3.3-70b-versatile",
|
||||
"openrouter.claude-3.5-sonnet",
|
||||
"o3-mini",
|
||||
"o4-mini",
|
||||
"gpt-4.1",
|
||||
"azure.o3-mini",
|
||||
"openrouter.gpt-4.1-nano",
|
||||
"openrouter.gpt-4o",
|
||||
"gemini-2.5",
|
||||
"azure.gpt-4o",
|
||||
"azure.gpt-4o-mini",
|
||||
"claude-3.7-sonnet",
|
||||
"azure.gpt-4.1-nano",
|
||||
"openrouter.o1",
|
||||
"openrouter.claude-3-haiku",
|
||||
"bedrock.claude-3.7-sonnet",
|
||||
"gemini-2.5-flash",
|
||||
"azure.o3",
|
||||
"openrouter.gemini-2.5",
|
||||
"openrouter.o3",
|
||||
"openrouter.o3-mini",
|
||||
"openrouter.gpt-4.1-mini",
|
||||
"openrouter.gpt-4.5-preview",
|
||||
"openrouter.gpt-4o-mini",
|
||||
"gpt-4.1-mini",
|
||||
"meta-llama/llama-4-scout-17b-16e-instruct",
|
||||
"openrouter.o1-mini",
|
||||
"gpt-4.5-preview",
|
||||
"o3",
|
||||
"openrouter.claude-3.5-haiku",
|
||||
"claude-3-opus",
|
||||
"o1-pro",
|
||||
"gemini-2.0-flash",
|
||||
"azure.o4-mini",
|
||||
"openrouter.o4-mini",
|
||||
"claude-3.5-sonnet",
|
||||
"meta-llama/llama-4-maverick-17b-128e-instruct",
|
||||
"azure.o1",
|
||||
"openrouter.gpt-4.1",
|
||||
"openrouter.o1-pro",
|
||||
"gpt-4.1-nano",
|
||||
"azure.gpt-4.5-preview",
|
||||
"openrouter.claude-3-opus",
|
||||
"gpt-4o-mini",
|
||||
"meta-llama/llama-4-scout-17b-16e-instruct",
|
||||
"openrouter.gpt-4o",
|
||||
"o1-pro",
|
||||
"claude-3-haiku",
|
||||
"o1",
|
||||
"deepseek-r1-distill-llama-70b",
|
||||
"azure.gpt-4.1",
|
||||
"gpt-4o",
|
||||
"azure.gpt-4.1-mini",
|
||||
"openrouter.claude-3.7-sonnet",
|
||||
"gemini-2.5-flash",
|
||||
"vertexai.gemini-2.5-flash",
|
||||
"claude-3.5-haiku",
|
||||
"gemini-2.0-flash-lite"
|
||||
"gpt-4o-mini",
|
||||
"o3-mini",
|
||||
"gpt-4.5-preview",
|
||||
"azure.gpt-4o",
|
||||
"azure.o4-mini",
|
||||
"openrouter.claude-3.5-sonnet",
|
||||
"gpt-4o",
|
||||
"o3",
|
||||
"gpt-4.1-mini",
|
||||
"llama-3.3-70b-versatile",
|
||||
"azure.gpt-4o-mini",
|
||||
"gpt-4.1-nano",
|
||||
"o4-mini",
|
||||
"qwen-qwq",
|
||||
"openrouter.claude-3.5-haiku",
|
||||
"openrouter.qwen-3-14b",
|
||||
"vertexai.gemini-2.5",
|
||||
"gemini-2.5",
|
||||
"azure.gpt-4.1-nano",
|
||||
"openrouter.o1-mini",
|
||||
"openrouter.qwen-3-30b",
|
||||
"claude-3.7-sonnet",
|
||||
"claude-3.5-sonnet",
|
||||
"gemini-2.0-flash",
|
||||
"meta-llama/llama-4-maverick-17b-128e-instruct",
|
||||
"openrouter.o3-mini",
|
||||
"openrouter.o4-mini",
|
||||
"openrouter.gpt-4.1-mini",
|
||||
"openrouter.o1",
|
||||
"o1-mini",
|
||||
"azure.gpt-4.1-mini",
|
||||
"openrouter.o1-pro",
|
||||
"grok-3-beta",
|
||||
"grok-3-mini-fast-beta",
|
||||
"openrouter.claude-3.7-sonnet",
|
||||
"openrouter.claude-3-opus",
|
||||
"openrouter.qwen-3-235b",
|
||||
"openrouter.gpt-4.1-nano",
|
||||
"bedrock.claude-3.7-sonnet",
|
||||
"openrouter.qwen-3-8b",
|
||||
"claude-3-opus",
|
||||
"azure.o1-mini",
|
||||
"deepseek-r1-distill-llama-70b",
|
||||
"gemini-2.0-flash-lite",
|
||||
"openrouter.qwen-3-32b",
|
||||
"openrouter.gpt-4.5-preview",
|
||||
"grok-3-mini-beta",
|
||||
"grok-3-fast-beta",
|
||||
"azure.o3-mini",
|
||||
"openrouter.claude-3-haiku",
|
||||
"azure.gpt-4.1",
|
||||
"azure.o1",
|
||||
"azure.o3",
|
||||
"azure.gpt-4.5-preview",
|
||||
"openrouter.gemini-2.5-flash",
|
||||
"openrouter.gpt-4o-mini",
|
||||
"openrouter.gemini-2.5"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
|
@ -341,7 +363,8 @@
|
|||
"groq",
|
||||
"openrouter",
|
||||
"bedrock",
|
||||
"azure"
|
||||
"azure",
|
||||
"vertexai"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue