This document covers how to configure each supported LLM provider with qcr.
| Provider | ID | Models |
|---|---|---|
| OpenAI | openai |
gpt-4o, gpt-4o-mini, o1, o3-mini, ... |
| Anthropic | anthropic |
claude-3-5-sonnet, claude-3-opus, ... |
| Google Gemini | gemini / google |
gemini-2.5-pro, gemini-2.5-flash, ... |
| DashScope (Alibaba) | dashscope / qwen |
qwen3-coder, qwen-max, qwen-turbo, qwen-plus, ... |
| Azure OpenAI Service | azure / azure_openai |
gpt-4o, gpt-4o-mini, o1, o3-mini, ... |
| GitHub Copilot | copilot / github_copilot |
gpt-4o, claude-3.5-sonnet, o3-mini, ... |
| MiniMax | openai (proxy) |
minimax-m2.5, minimax-m2.5-highspeed, minimax-m2.1, ... |
| OpenAI-compatible proxy | openai |
any |
export OPENAI_API_KEY=sk-...Or in ~/.qcr/settings.json:
{
"providers": {
"openai": {
"api_key": "sk-..."
}
}
}qcr --provider openai --model gpt-4o "refactor this function"export OPENAI_API_BASE=https://my-proxy.example.com/v1Or in settings:
{
"providers": {
"openai": {
"base_url": "https://my-proxy.example.com/v1",
"api_key": "..."
}
}
}export ANTHROPIC_API_KEY=sk-ant-...qcr --provider anthropic --model claude-3-5-sonnet-20241022 "explain this code"{
"model": {
"model_id": "claude-3-5-sonnet-20241022",
"provider": "anthropic"
},
"providers": {
"anthropic": {
"api_key": "sk-ant-..."
}
}
}export GEMINI_API_KEY=AIza...qcr --provider gemini --model gemini-2.5-pro "write tests for this module"The provider alias google is also accepted:
qcr --provider google --model gemini-2.5-flash "summarize"export DASHSCOPE_API_KEY=sk-...qcr --provider dashscope --model qwen-max "translate this"
# or
qcr --provider qwen --model qwen-turbo "translate this"
Both `dashscope` and `qwen` resolve to the DashScope endpoint (`https://dashscope.aliyuncs.com/compatible-mode/v1`).
---
## Azure OpenAI Service
Azure OpenAI requires a **resource endpoint** and a deployment-scoped API key.
The `--model` / `model_id` value is used as the **deployment name** in the URL.
### Environment variables
```bash
export AZURE_OPENAI_API_KEY=...
export AZURE_OPENAI_ENDPOINT=https://my-resource.openai.azure.com
# Optional: override the REST API version (default: 2024-12-01-preview)
export AZURE_OPENAI_API_VERSION=2024-12-01-previewqcr --provider azure --model gpt-4o "refactor this function"Both azure and azure_openai are accepted as the provider ID.
{
"model": {
"model_id": "gpt-4o",
"provider": "azure"
},
"providers": {
"azure": {
"base_url": "https://my-resource.openai.azure.com",
"api_key": "...",
"custom_headers": {
"api-version": "2024-12-01-preview"
}
}
}
}Note: The
api-versionentry incustom_headerscontrols the Azure REST API version query parameter. It is not sent as an HTTP header — qcr strips it before building the request and appends it to the URL automatically.
qcr constructs the request URL as:
https://{resource}.openai.azure.com/openai/deployments/{deployment}/chat/completions?api-version={version}
Azure uses the api-key HTTP header (not Authorization: Bearer). For
managed-identity / Entra ID scenarios, add "Authorization": "Bearer {token}"
via custom_headers and omit the api_key field.
Supported models depend on which deployments exist in your Azure resource. Common deployment names:
| Deployment name | Underlying model |
|---|---|
gpt-4o |
GPT-4o |
gpt-4o-mini |
GPT-4o mini |
o1 |
o1 reasoning model |
o3-mini |
o3-mini reasoning model |
gpt-4-turbo |
GPT-4 Turbo |
GitHub Copilot uses a two-step authentication flow: a GitHub OAuth token is exchanged for a short-lived Copilot API token, which is then used to call the OpenAI-compatible chat completions endpoint. qcr handles the token exchange and automatic refresh transparently.
qcr resolves the GitHub OAuth token from these sources (first match wins):
api_keyin provider config — explicit setting insettings.jsonor CLIGH_COPILOT_TOKENenvironment variable- Local Copilot config files —
~/.config/github-copilot/hosts.jsonorapps.json(written by VS Code Copilot extension and GitHub CLI)
The easiest way to get started is to sign in to GitHub Copilot in VS Code or GitHub CLI first — qcr will pick up the cached OAuth token automatically.
# Option 1: Set the token directly
export GH_COPILOT_TOKEN=gho_...
# Option 2: Already signed in via VS Code / GitHub CLI — nothing to do
qcr --provider copilot --model gpt-4o "refactor this function"qcr --provider copilot --model gpt-4o "explain this code"
qcr --provider copilot --model claude-3.5-sonnet "write tests"The provider aliases copilot, github_copilot, and github-copilot are all
accepted.
{
"model": {
"model_id": "gpt-4o",
"provider": "copilot"
},
"providers": {
"copilot": {
"api_key": "gho_..."
}
}
}Note: You usually do not need to set
api_keyexplicitly — qcr reads the token from~/.config/github-copilot/hosts.jsonautomatically.
For GitHub Enterprise (GHES) users, set base_url to your enterprise server URI.
qcr derives the token exchange URL from this domain:
{
"providers": {
"copilot": {
"base_url": "https://github.example.com"
}
}
}This causes token exchange to go to
https://api.github.example.com/copilot_internal/v2/token instead of the
default https://api.github.com/copilot_internal/v2/token.
The models available depend on your Copilot subscription plan. Common models:
| Model | Notes |
|---|---|
gpt-4o |
Default, fast and capable |
gpt-4o-mini |
Faster, lower cost |
claude-3.5-sonnet |
Anthropic model via Copilot |
o3-mini |
OpenAI reasoning model (no temperature support) |
o4-mini |
OpenAI reasoning model (no temperature support) |
gpt-5.2-codex |
Codex model, uses Responses API (no temperature support) |
- qcr sends your GitHub OAuth token to the Copilot token endpoint
- The response includes a short-lived API key, an expiration timestamp, and the API endpoint URL
- qcr caches the API token and automatically refreshes it 5 minutes before expiry
- All chat completions requests use the dynamically assigned endpoint and API key
"no GitHub OAuth token available" — qcr could not find an OAuth token.
Sign in to GitHub Copilot in VS Code, run gh auth login, or set
GH_COPILOT_TOKEN.
401 from token exchange — your OAuth token is expired or revoked. Re-sign in to VS Code Copilot or regenerate the token.
400 Bad Request during a tool-use turn — several causes have been fixed across recent versions. Upgrade to the latest version first:
cargo install --path crates/qcr-cli --force| Version | Root cause | Fix |
|---|---|---|
| ≤ 0.1.1 | Assistant message missing "content" field on tool-call-only turns |
Always emit "content": null |
| ≤ 0.1.2 | OpenAI-only fields (intent, stream_options, n, presence_penalty, frequency_penalty) sent to Claude models via Copilot — Anthropic backend rejects them |
Strip these fields for Claude models |
| ≤ 0.1.2 | Empty string tool result content ("content": "") — Claude rejects blank tool results |
Replace empty content with "(no output)" placeholder |
| ≤ 0.1.2 | Tool call arguments serialised as "\"\"" (JSON string) instead of "{}" (JSON object) when Claude returns empty args for no-parameter tools like CronList |
Normalise non-object arguments to {} |
| ≤ 0.1.15 | temperature/top_p/presence_penalty sent to o-series and codex models — these reasoning models reject unsupported parameters with 400 |
Auto-detect o1/o3/o4-mini/codex models and skip temperature params |
If you still see 400 errors after upgrading, enable debug logging to capture the full request/response bodies:
QCR_LOG=warn qcr --provider copilot --model claude-sonnet-4.6 "your prompt"
tail -f ~/.qcr/qcr.log403 / subscription error — your GitHub account may not have an active Copilot subscription, or the model you requested is not included in your plan.
MiniMax models are accessed through any OpenAI-compatible proxy that serves them (e.g. aiproxies.win). Use openai as the provider and set a custom base_url.
{
"model": {
"model_id": "minimax-m2.5",
"provider": "openai"
},
"providers": {
"openai": {
"name": "openai",
"base_url": "https://aiproxies.win/v1",
"api_key": "YOUR_PROXY_KEY"
}
}
}| Model ID | Notes |
|---|---|
minimax-m2.5 |
Latest flagship model |
minimax-m2.5-highspeed |
Faster, lower-latency variant |
minimax-m2.1 |
Previous generation |
minimax-m2.1-highspeed |
Fast variant of M2.1 |
minimax-m2 |
Baseline M2 generation |
qcr --provider openai --model minimax-m2.5 "refactor this module"List available models from the proxy:
curl -s https://aiproxies.win/v1/models \
-H "Authorization: Bearer YOUR_PROXY_KEY" \
| python3 -c "import sys,json; [print(m['id']) for m in json.load(sys.stdin)['data']]"Any service that speaks the OpenAI chat completions protocol works by setting OPENAI_API_BASE:
export OPENAI_API_BASE=https://openrouter.ai/api/v1
export OPENAI_API_KEY=sk-or-...
qcr --provider openai --model mistralai/mistral-7b-instruct "hello"Tested proxies:
- OpenRouter — access many models through one API
- Together AI — open-source model hosting
- Groq — fast inference
- aiproxies.win — multi-model proxy (MiniMax, Gemini, and more)
- Ollama — local models (use
http://localhost:11434/v1) - LM Studio — local models (use
http://localhost:1234/v1)
# Start Ollama with a model
ollama run llama3.2
# Point qcr at it
export OPENAI_API_BASE=http://localhost:11434/v1
export OPENAI_API_KEY=ollama # any non-empty string
qcr --provider openai --model llama3.2 "explain this file"Set a persistent default in ~/.qcr/settings.json:
{
"model": {
"model_id": "minimax-m2.5",
"provider": "openai"
},
"providers": {
"openai": {
"name": "openai",
"base_url": "https://aiproxies.win/v1",
"api_key": "YOUR_PROXY_KEY"
}
},
"approval_mode": "suggest"
}Switch to a different model for one session without changing settings:
qcr --model gemini-2.5-flash "summarize this PR"Or via environment:
export QCR_PROVIDER=openai
export QCR_MODEL=minimax-m2.5Command-line flags always take highest priority:
qcr --provider anthropic --model claude-3-5-sonnet-20241022 "..."When creating an API client, qcr resolves configuration in this order (highest wins):
- CLI flags:
--provider,--model - Environment variables:
QCR_PROVIDER,QCR_MODEL,OPENAI_API_KEY, etc. - Project settings:
.qcr/settings.jsonin current directory - Global settings:
~/.qcr/settings.json - Built-in defaults: provider=
dashscope, model=qwen3-coder
qcr supports prompt caching to reduce per-turn token costs. The static portion of the system prompt (base template) is marked as cacheable; dynamic content (memory, steering, memorix) is not cached.
| Provider | Cache Hints Sent | Cache Tokens Read | Mechanism |
|---|---|---|---|
| Anthropic | yes | read + write (streaming + non-streaming) | cache_control: {type: "ephemeral"} in system array, anthropic-beta: prompt-caching-2024-07-31 header |
| OpenAI | yes | read only (streaming + non-streaming) | Structured system message with cache_control, auto prefix caching |
| Azure | yes | read only (streaming + non-streaming) | Same as OpenAI |
| Copilot | yes | read only (streaming + non-streaming) | Same as OpenAI |
| Gemini | no | no | No caching API in responses |
Cache token usage is reported in the TokenUsage struct as cache_read_tokens and cache_write_tokens. These are visible in the TUI token counter when available.
No configuration is needed — prompt caching is automatic when SystemPromptBlocks are available (which they are in ACP mode). For TUI mode, blocks will be wired in a future release.
401 Unauthorized — API key is missing or wrong. Check echo $OPENAI_API_KEY.
approval_mode "prompt" is invalid — The valid values are always, never, and suggest. Using "prompt" causes a parse error and qcr silently falls back to defaults (no provider config → 401).
400 Bad Request after a tool call (any OpenAI-compatible provider) — qcr
≤ 0.1.1 omitted "content" entirely on assistant messages that contained only
tool calls and no text. The OpenAI wire format requires "content": null in this
case. Fixed in v0.1.2 — affects openai, azure, and copilot providers.
400 unknown provider for model — The proxy does not recognize the model name. List available models:
curl -s $OPENAI_API_BASE/models -H "Authorization: Bearer $OPENAI_API_KEY" \
| python3 -c "import sys,json; [print(m['id']) for m in json.load(sys.stdin)['data']]"Connection refused — OPENAI_API_BASE points to a server that is not running.
Rate limited — qcr respects 429 responses and surfaces a RateLimited error. Add retry logic or reduce request frequency.