Models & credentials
How Gajae-Code loads models from ~/.gjc/agent/models.yml, applies provider presets, resolves model roles and thinking levels, builds canonical equivalence, discovers local runtimes, and resolves API keys.
Gajae-Code (gjc) loads every concrete provider model, then builds a canonical layer above them so you can pin an exact provider/model or select a canonical id that coalesces across providers. All of this is driven by one file — ~/.gjc/agent/models.yml — plus auth resolution and runtime discovery.
This page is the practical reference. It does not paste the full schema; it covers the shape, the presets, roles, equivalence, discovery, and auth order.
Config file location
The default config path is:
~/.gjc/agent/models.ymlLegacy behavior is still present:
- If
models.ymlis missing andmodels.jsonexists at the same location, it is migrated tomodels.yml. - Explicit
.json/.jsoncpaths are still supported when passed programmatically.
If models.yml fails schema or validation checks, the registry keeps operating with built-in models and surfaces the error in the UI.
models.yml shape
At a high level, models.yml has three top-level sections — providers, equivalence, and modelBindings:
providers:
<provider-id>:
# provider-level config (baseUrl, api, auth, models, ...)
equivalence:
overrides:
<provider-id>/<model-id>: <canonical-model-id>
exclude:
- <provider-id>/<model-id>
modelBindings:
modelRoles:
default: <provider/model-id or canonical-id>[:thinkingLevel]
agentModelOverrides:
executor: <provider/model-id>provider-id is the canonical provider key used across selection and auth lookup. equivalence is optional and configures canonical model grouping on top of concrete provider models.
models.yml is strict: unknown provider/model keys fail validation before provider dispatch, so remove stale keys rather than relying on them being ignored.
Provider-level fields
A full custom provider declares a transport and a list of models:
providers:
my-provider:
baseUrl: https://api.example.com/v1
apiKey: MY_PROVIDER_API_KEY
api: openai-completions
auth: apiKey
headers:
X-Team: platform
disableStrictTools: false # set true for Anthropic-compatible endpoints that reject `strict`
models:
- id: some-model-id
name: Some Model
reasoning: false
input: [text]
contextWindow: 128000
maxTokens: 16384Allowed provider/model api values:
openai-completions, openai-responses, openai-codex-responses, azure-openai-responses, bedrock-converse-stream, anthropic-messages, google-generative-ai, google-vertex, google-gemini-cli, ollama-chat, cursor-agent.
Allowed auth values: apiKey (default), none, or oauth. (For models.yml custom models, oauth is accepted by the schema but does not waive the apiKey requirement.)
Validation rules
- Full custom provider (
modelsnon-empty): requiresbaseUrl,apiKeyunlessauth: none, andapiat the provider level or each model. - Override-only provider (
modelsmissing/empty): must define at least one ofbaseUrl,headers,compat,requestTransform,disableStrictTools,modelOverrides, ordiscovery. - Model checks:
idis required;contextWindowandmaxTokensmust be positive if provided.
Provider presets
For common MiniMax and GLM/zAI setups, prefer the provider presets so the OpenAI-compatible API, base URL, env var, model id, and compatibility flags are written together.
From the CLI:
gjc setup provider --preset minimax
gjc setup provider --preset minimax-cn
gjc setup provider --preset glmThe same presets are available inside the TUI:
/provider add --preset minimax
/provider add --preset glm
/provider add zaiPresets only write models.yml entries that reference documented environment variable names (MINIMAX_CODE_API_KEY, MINIMAX_CODE_CN_API_KEY, or ZAI_API_KEY); they do not store or validate real credentials. The GLM aliases (glm, zai, z-ai) write an OpenAI-compatible custom provider named glm-proxy and do not replace the first-class zai provider.
Model roles and thinking levels
Model roles let you bind a model to a job instead of hard-coding ids everywhere. Supported roles:
default, smol, slow, vision, plan, designer, commit, taskRole values live under modelRoles (in settings / modelBindings). Each role value can be:
provider/modelId— pins a concrete provider variant, or- a canonical id such as
gpt-5.3-openai-code— allows provider coalescing.
Each role value can append a thinking selector suffix:
:off | :minimal | :low | :medium | :high | :xhighFor example default: layofflabs/gpt-5.5:high. If a role points at another role, the target model is inherited normally and any explicit suffix on the referring role wins for that role-specific use.
Related settings:
| Setting | Purpose |
|---|---|
modelRoles | Map roles to a provider/modelId or canonical id (record). |
enabledModels | Scoped pattern list of selectable models. |
modelProviderOrder | Global canonical-provider precedence. |
providers.kimiApiFormat | openai or anthropic request format. |
providers.openaiWebsockets | auto / off / on websocket preference for the OpenAI code transport. |
enabledModels and disabledProviders entries may also be scoped to a path prefix (using path, paths, pathPrefix, or pathPrefixes), so a project directory can pin a different set of models than your home directory.
Canonical equivalence
The registry keeps every concrete provider model and then builds a canonical layer above them. Canonical ids are official upstream ids only — for example anthropic-model-opus-4-6, anthropic-model-haiku-4-5, or gpt-5.3-openai-code.
Configure grouping with the equivalence block:
equivalence:
overrides:
zenmux/openai-code: gpt-5.3-openai-code
p-openai-code/openai-code: gpt-5.3-openai-code
exclude:
- demo/openai-code-previewBuild order for canonical grouping:
equivalence.overrides.anthropic/ or openai/ prefixes, normalizing 4.6 -> 4-6).Heuristics are intentionally narrow: ambiguous families or versions are never merged without a bundled match or an explicit override.
When multiple concrete variants share a canonical id, resolution uses availability + auth, then modelProviderOrder, then existing registry order. Disabled or unauthenticated providers are skipped. Session state and transcripts always record the concrete provider/model that actually executed the turn.
In /model and --list-models, a canonical view appears alongside provider tabs/rows. Selecting a canonical entry stores the canonical selector; selecting a provider row stores the explicit provider/modelId.
Runtime discovery
If you do not configure them explicitly, Gajae-Code adds implicit, keyless discoverable providers for local runtimes:
| Provider | Default base URL (env override) | API |
|---|---|---|
ollama | http://127.0.0.1:11434 (OLLAMA_BASE_URL) | openai-responses |
llama.cpp | http://127.0.0.1:8080 (LLAMA_CPP_BASE_URL) | openai-responses |
lm-studio | http://127.0.0.1:1234/v1 (LM_STUDIO_BASE_URL) | openai-completions |
All three behave as auth: none. Discovery calls the runtime's model endpoints and synthesizes model entries with local defaults. You can also configure discovery yourself:
providers:
ollama:
baseUrl: http://127.0.0.1:11434
api: openai-responses
auth: none
discovery:
type: ollamadiscovery.type accepts ollama, llama.cpp, or lm-studio, and requires a provider-level api.
Auth and API key resolution order
When requesting a key for a provider, the effective order is:
--api-key).agent.db.agent.db (with refresh).OPENAI_API_KEY, ANTHROPIC_API_KEY, etc.).apiKey from models.yml).The models.yml apiKey value is first treated as an environment variable name; if no such env var exists, the literal string is used as the token. If authHeader: true and a provider apiKey is set, models get an Authorization: Bearer <resolved-key> header injected. Providers marked auth: none are treated as available without credentials.
A model can exist in the registry (getAll()) but only be selectable once auth resolves (getAvailable()).
Broker mode
When GJC_AUTH_BROKER_URL (or auth.broker.url) is set, the local SQLite credential store is replaced by a remote broker store: stored API-key and OAuth credentials are served from a broker-supplied snapshot with redacted refresh tokens, and expiry triggers a refresh on the broker rather than locally.
Related pages
Treat every *_API_KEY, *_TOKEN, and cloud credential as a secret — never log or commit them.