Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ docker run --rm -p 8080:8080 \
-e OPENAI_API_KEY="your-openai-key" \
-e ANTHROPIC_API_KEY="your-anthropic-key" \
-e GEMINI_API_KEY="your-gemini-key" \
-e VERTEX_PROJECT="your-gcp-project" \
-e VERTEX_LOCATION="us-central1" \
-e VERTEX_AUTH_TYPE="gcp_adc" \
-e DEEPSEEK_API_KEY="your-deepseek-key" \
-e GROQ_API_KEY="your-groq-key" \
-e OPENROUTER_API_KEY="your-openrouter-key" \
Expand Down Expand Up @@ -90,7 +93,8 @@ Example model identifiers are illustrative and subject to change; consult provid
| ------------- | ----------------------------------------------------------------- | ---------------------------------- | :--: | :----------: | :---: | :---: | :-----: | :------: |
| OpenAI | `OPENAI_API_KEY` | `gpt-5.5` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Anthropic | `ANTHROPIC_API_KEY` | `claude-sonnet-4-20250514` | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ |
| Google Gemini | `GEMINI_API_KEY` | `gemini-2.5-flash` | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| Google Gemini | `GEMINI_API_KEY` | `gemini-2.5-flash` | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| Vertex AI | `VERTEX_PROJECT` + `VERTEX_LOCATION` | `google/gemini-2.5-flash` | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ |
| DeepSeek | `DEEPSEEK_API_KEY` | `deepseek-v4-pro` | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
| Groq | `GROQ_API_KEY` | `llama-3.3-70b-versatile` | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| OpenRouter | `OPENROUTER_API_KEY` | `google/gemini-2.5-flash` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
Expand Down Expand Up @@ -120,6 +124,10 @@ To register multiple instances of the same provider type without `config.yaml`,
use suffixed env vars such as `OPENAI_EAST_API_KEY` and
`OPENAI_EAST_BASE_URL`; add `OPENAI_EAST_MODELS` to configure that instance's
model list. This registers provider `openai-east` with type `openai`.
Vertex AI uses the `VERTEX_*` prefix and registers provider `vertex`
with type `vertex`; suffixed variables such as `VERTEX_US_PROJECT` register
provider `vertex_us`. Vertex requires `VERTEX_PROJECT` and `VERTEX_LOCATION`;
`VERTEX_AUTH_TYPE` defaults to Application Default Credentials (`gcp_adc`).

---

Expand Down Expand Up @@ -250,7 +258,9 @@ Key settings:
| `ENABLE_PASSTHROUGH_ROUTES` | `true` | Enable provider-native passthrough routes under `/p/{provider}/...` |
| `ALLOW_PASSTHROUGH_V1_ALIAS` | `true` | Allow `/p/{provider}/v1/...` aliases while keeping `/p/{provider}/...` canonical |
| `ENABLED_PASSTHROUGH_PROVIDERS` | `openai,anthropic,openrouter,zai,vllm` | Comma-separated list of enabled passthrough providers |
| `USE_GOOGLE_GEMINI_NATIVE_API` | `true` | Use Gemini native `generateContent` for chat/responses; set `false` for Gemini's OpenAI-compatible API and image_url pass-through behavior |
| `GEMINI_API_MODE` | `native` | Gemini AI Studio upstream mode: `native` or `openai_compatible` |
| `VERTEX_API_MODE` | `native` | Vertex AI Gemini upstream mode: `native` or `openai_compatible` |
| `USE_GOOGLE_GEMINI_NATIVE_API` | `true` | Legacy global Gemini mode toggle used when per-provider `*_API_MODE` is unset |
| `STORAGE_TYPE` | `sqlite` | Storage backend (`sqlite`, `postgresql`, `mongodb`) |
| `METRICS_ENABLED` | `false` | Enable Prometheus metrics (experimental) |
| `LOGGING_ENABLED` | `false` | Enable audit logging |
Expand Down
2 changes: 2 additions & 0 deletions cmd/gomodel/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"gomodel/internal/providers/openai"
"gomodel/internal/providers/openrouter"
"gomodel/internal/providers/oracle"
"gomodel/internal/providers/vertex"
"gomodel/internal/providers/vllm"
"gomodel/internal/providers/xai"
"gomodel/internal/providers/zai"
Expand Down Expand Up @@ -122,6 +123,7 @@ func main() {
factory.Add(anthropic.Registration)
factory.Add(deepseek.Registration)
factory.Add(gemini.Registration)
factory.Add(vertex.Registration)
factory.Add(groq.Registration)
factory.Add(minimax.Registration)
factory.Add(ollama.Registration)
Expand Down
15 changes: 14 additions & 1 deletion config/config.example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -202,9 +202,22 @@ providers:
type: gemini
api_key: "..."
# Chat/responses use Gemini's native generateContent API by default.
# Set USE_GOOGLE_GEMINI_NATIVE_API=false to use Gemini's OpenAI-compatible API instead.
# Set GEMINI_API_MODE=openai_compatible or api_mode: openai_compatible to use
# Gemini's OpenAI-compatible API instead.
# Native mode accepts image data URLs but does not fetch remote image URLs yet.

vertex:
type: vertex
auth_type: gcp_adc # or gcp_service_account
vertex_project: "my-gcp-project"
vertex_location: "us-central1"
# service_account_file: "/var/run/secrets/google/service-account.json"
# service_account_json: "${VERTEX_SERVICE_ACCOUNT_JSON}"
# service_account_json_base64: "${VERTEX_SERVICE_ACCOUNT_JSON_BASE64}"
# api_mode: native # or openai_compatible
# models:
# - id: "google/gemini-2.5-flash"

xai:
type: xai
api_key: "..."
Expand Down
21 changes: 15 additions & 6 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,21 @@ type LoadResult struct {
// overrides, credential filtering, or resilience merging. Exported so the
// providers package can resolve it into a fully-configured ProviderConfig.
type RawProviderConfig struct {
Type string `yaml:"type"`
APIKey string `yaml:"api_key"`
BaseURL string `yaml:"base_url"`
APIVersion string `yaml:"api_version"`
Models []RawProviderModel `yaml:"models"`
Resilience *RawResilienceConfig `yaml:"resilience"`
Type string `yaml:"type"`
APIKey string `yaml:"api_key"`
BaseURL string `yaml:"base_url"`
APIVersion string `yaml:"api_version"`
Backend string `yaml:"backend"`
AuthType string `yaml:"auth_type"`
APIMode string `yaml:"api_mode"`
VertexProject string `yaml:"vertex_project"`
VertexLocation string `yaml:"vertex_location"`
ServiceAccountFile string `yaml:"service_account_file"`
ServiceAccountJSON string `yaml:"service_account_json"`
ServiceAccountJSONBase64 string `yaml:"service_account_json_base64"`
GCPScope string `yaml:"gcp_scope"`
Models []RawProviderModel `yaml:"models"`
Resilience *RawResilienceConfig `yaml:"resilience"`
}

// RawResilienceConfig holds optional per-provider resilience overrides from YAML.
Expand Down
5 changes: 5 additions & 0 deletions docs/advanced/config-yaml.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ For multiple provider instances, env vars support
`OLLAMA_A_BASE_URL` registers `ollama-a`. Azure also supports
`<PROVIDER>_<SUFFIX>_API_VERSION`.

Vertex AI is the exception to the provider-name shape: use `VERTEX_*`
env vars to create `type: vertex` providers named `vertex`, `vertex_us`,
`vertex_eu`, and so on. Vertex requires `VERTEX_PROJECT` and
`VERTEX_LOCATION`; `VERTEX_AUTH_TYPE` defaults to `gcp_adc`.

Configured provider model lists can stay in env via `<PROVIDER>_MODELS`, for
example `OPENROUTER_MODELS`, `ORACLE_MODELS`, `AZURE_MODELS`, or `VLLM_MODELS`.
Set `CONFIGURED_PROVIDER_MODELS_MODE=fallback` (default) to use those lists only
Expand Down
22 changes: 22 additions & 0 deletions docs/advanced/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,18 @@ The same pattern works for every registered provider type:
suffixed `BASE_URL` values because their endpoints are deployment- or
region-specific.

Vertex AI uses the dedicated `VERTEX_*` prefix and registers provider
`vertex` with type `vertex`. Suffixed Vertex env vars keep underscores in the
provider name:

```bash
export VERTEX_PROJECT="prod-ai"
export VERTEX_LOCATION="us-central1" # Registers "vertex", type "vertex"

export VERTEX_US_PROJECT="prod-ai"
export VERTEX_US_LOCATION="us-central1" # Registers "vertex_us", type "vertex"
```

### YAML Provider Blocks

For more control (custom names, per-provider resilience, or larger structured
Expand Down Expand Up @@ -404,6 +416,16 @@ providers:
models:
- gemini-2.0-flash
- gemini-1.5-pro

# Add Vertex AI. Project and location are required.
vertex:
type: vertex
auth_type: gcp_adc
vertex_project: "prod-ai"
vertex_location: "us-central1"
api_mode: native
models:
- google/gemini-2.5-flash
```

<Note>
Expand Down
98 changes: 85 additions & 13 deletions docs/guides/gemini.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ GoModel routes Gemini chat and Responses API requests through Gemini's native
Gemini's OpenAI-compatible API when you need compatibility behavior that the
native adapter does not implement yet.

## Configure Gemini
## Configure AI Studio

Env-only configuration is enough:
Use the `GEMINI_*` prefix for Gemini API keys from AI Studio:

```bash
export GEMINI_API_KEY="..."
export GEMINI_API_MODE="native"
```

Or in `config.yaml`:
Expand All @@ -24,21 +25,85 @@ providers:
gemini:
type: gemini
api_key: "${GEMINI_API_KEY}"
api_mode: native
```

## Configure Vertex AI

Use the `VERTEX_*` prefix for Gemini on Vertex AI. Vertex providers use the
dedicated `vertex` provider type:

```bash
export VERTEX_PROJECT="my-gcp-project"
export VERTEX_LOCATION="us-central1"
export VERTEX_AUTH_TYPE="gcp_adc"
export VERTEX_API_MODE="native"
```

This registers provider `vertex`, type `vertex`. For multiple Vertex regions
or accounts, use suffixes:

```bash
export VERTEX_US_PROJECT="prod-ai"
export VERTEX_US_LOCATION="us-central1"
export VERTEX_US_AUTH_TYPE="gcp_adc"

export VERTEX_EU_PROJECT="prod-ai"
export VERTEX_EU_LOCATION="europe-west4"
export VERTEX_EU_AUTH_TYPE="gcp_service_account"
export VERTEX_EU_SERVICE_ACCOUNT_FILE="/secrets/vertex-eu.json"
```

These register providers `vertex_us` and `vertex_eu`.

Or in `config.yaml`:

```yaml
providers:
vertex:
type: vertex
auth_type: gcp_adc
vertex_project: my-gcp-project
vertex_location: us-central1
api_mode: native
```

Vertex requires both project and location. `VERTEX_AUTH_TYPE` defaults to
Application Default Credentials (`gcp_adc`) when service-account fields are not
set. Use service-account JSON directly when ADC is not appropriate:

<Note>
Vertex authentication is enterprise-oriented and may become paid or licensed
in a future release.
</Note>

```bash
export VERTEX_AUTH_TYPE="gcp_service_account"
export VERTEX_SERVICE_ACCOUNT_FILE="/secrets/service-account.json"
# or VERTEX_SERVICE_ACCOUNT_JSON / VERTEX_SERVICE_ACCOUNT_JSON_BASE64
```

The default Vertex bases are derived from project and location:

- OpenAI-compatible: `https://aiplatform.googleapis.com/v1/projects/{project}/locations/{location}/endpoints/openapi`
- native Gemini: `https://aiplatform.googleapis.com/v1/projects/{project}/locations/{location}/publishers/google`

## Native versus OpenAI-compatible mode

Gemini native mode is enabled by default:

```bash
export USE_GOOGLE_GEMINI_NATIVE_API=true
export GEMINI_API_MODE="native"
export VERTEX_API_MODE="native"
```

Set it to `false` to route chat and Responses API requests through Gemini's
OpenAI-compatible `/chat/completions` endpoint:
Set the per-provider API mode to `openai_compatible` to route chat and
Responses API requests through the upstream OpenAI-compatible
`/chat/completions` endpoint:

```bash
export USE_GOOGLE_GEMINI_NATIVE_API=false
export GEMINI_API_MODE="openai_compatible"
export VERTEX_API_MODE="openai_compatible"
```

`GEMINI_BASE_URL` configures the Gemini base. GoModel keeps separate internal
Expand All @@ -50,15 +115,17 @@ bases for native Gemini and the OpenAI-compatible API:
When `GEMINI_BASE_URL` ends in `/openai`, GoModel uses that value for the
OpenAI-compatible client and derives the native base by stripping `/openai`.
Gemini embeddings, files, and batches still use the OpenAI-compatible surface.
Vertex embeddings use Vertex AI native prediction. Vertex does not expose
Gemini Files or OpenAI-compatible batch operations.

```bash
export GEMINI_BASE_URL="https://generativelanguage.googleapis.com/v1beta/openai"
```

<Note>
`USE_GOOGLE_GEMINI_NATIVE_API` decides whether chat and Responses API calls
use native Gemini or the OpenAI-compatible API. `GEMINI_BASE_URL` only
configures the upstream base URLs.
`USE_GOOGLE_GEMINI_NATIVE_API` is still supported as a legacy global toggle
when per-provider `*_API_MODE` is unset. Prefer `GEMINI_API_MODE` and
`VERTEX_API_MODE` for new deployments.
</Note>

## Image URL behavior
Expand All @@ -84,8 +151,9 @@ mode. Google's native Gemini API supports inline image data and Files API
references; for URL-hosted images, Google's examples fetch the URL first and
send the bytes to `generateContent`.

Set `USE_GOOGLE_GEMINI_NATIVE_API=false` when you need GoModel to pass the
OpenAI-compatible `image_url` request shape through to Gemini's
Set `GEMINI_API_MODE=openai_compatible` or
`VERTEX_API_MODE=openai_compatible` when you need GoModel to pass the
OpenAI-compatible `image_url` request shape through to Google's
OpenAI-compatible endpoint instead. Google documents image input for that
endpoint using the OpenAI `image_url` field.

Expand All @@ -95,17 +163,21 @@ Integrated:

- chat completions and streaming
- Responses API and streaming
- model listing through Gemini's native `/models`
- model listing through AI Studio `/models` and Vertex publisher models
- Vertex embeddings through native prediction
- usage metadata normalization for native responses
- tool calls and function-call results
- inline image data via `data:` URLs in native mode

Not integrated in native mode yet:
Not integrated yet:

- fetching remote `image_url` values
- uploading remote images through the Gemini Files API before a chat request
- Vertex Files and batch prediction APIs

References:

- [Gemini image understanding](https://ai.google.dev/gemini-api/docs/image-understanding)
- [Gemini OpenAI compatibility](https://ai.google.dev/gemini-api/docs/openai)
- [Vertex AI OpenAI compatibility](https://cloud.google.com/vertex-ai/generative-ai/docs/start/openai)
- [Gemini API in Vertex AI](https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/inference)
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ require (
github.com/swaggo/swag/v2 v2.0.0-rc5
github.com/tidwall/gjson v1.18.0
go.mongodb.org/mongo-driver/v2 v2.6.0
golang.org/x/oauth2 v0.36.0
golang.org/x/sync v0.20.0
golang.org/x/term v0.42.0
gopkg.in/yaml.v3 v3.0.1
modernc.org/sqlite v1.50.0
)

require (
cloud.google.com/go/compute/metadata v0.3.0 // indirect
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/andybalholm/brotli v1.2.1 h1:R+f5xP285VArJDRgowrfb9DqL18yVK0gKAW/F+eTWro=
Expand Down Expand Up @@ -161,6 +163,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
Expand Down
Loading
Loading