OpenAI-Compatible Chat (9Router, OmniRoute &
|
| Provider / gateway | Default base URL | Auth | Notes |
|---|---|---|---|
| 9Router (built-in preset) | http://localhost:20128/v1 |
Optional (loopback) | Local gateway, zero-config on localhost. |
| OmniRoute (built-in preset) | http://localhost:20128/v1 |
Optional (loopback) | Shares the 9Router-compatible local API surface. |
| OpenRouter | https://openrouter.ai/api/v1 |
API key | Cloud, pay-as-you-go. |
| LM Studio | http://localhost:1234/v1 |
Optional (loopback) | Local model server. |
| Ollama (OpenAI-compat shim) | http://localhost:11434/v1 |
Optional (loopback) | Enable the OpenAI-compatible endpoint in Ollama settings. |
| Groq | https://api.groq.com/openai/v1 |
API key | Cloud, low-latency inference. |
| vLLM / LiteLLM / custom proxy | your own URL | Depends on upstream | Anything speaking GET /v1/models + POST /v1/chat/completions. |
Any other /v1/models provider |
Custom option in the wizard | Depends on provider | Just paste the base URL and (if needed) the API key. |
What "compatible" means: a provider is supported if it implements the three endpoints the extension uses —
GET /v1/models(model discovery),POST /v1/chat/completions(chat + streaming SSE + tool calls), and optionallyPOST /v1/completions(FIM inline completions). This is the de-facto OpenAI standard used by 9Router, OmniRoute, and most LLM gateways.
What it does
- Native chat: the configured models appear in the VS Code Chat model picker, powered by the public
LanguageModelChatProviderAPI. - Multi-provider support: works with 9Router, OmniRoute, and any gateway that exposes the standard
GET /v1/modelsendpoint — including OpenRouter, LM Studio, Ollama (with OpenAI shim), Groq, vLLM, LiteLLM, and custom proxies. - Streaming responses: SSE text streams into the chat as they arrive.
- Agent mode: tool calls round-trip between VS Code and your gateway (
tools→ OpenAItool_calls→ tool results). - Inline completions: ghost-text suggestions in the editor (non-streaming), using a hybrid FIM → chat-completion fallback prompt.
- Profile wizard: a guided command to set up a profile — preset → base URL → API key → model discovery → save.
- Model auto-discovery: models are fetched from the gateway's standard
GET /v1/models— no hard-coded list, just what the gateway advertises. - SecretStorage: API keys are stored in the OS keychain via VS Code's SecretStorage — never in
settings.jsonand never in logs.
Requirements
- VS Code ≥ 1.96 (stable).
- Node.js ≥ 18.19 (or ≥ 20) for development.
- A gateway that exposes the standard OpenAI-compatible
/v1API, e.g.:- 9Router at
http://localhost:20128/v1(built-in preset, no API key needed for localhost), or - OmniRoute at
http://localhost:20128/v1(built-in preset, 9Router-compatible surface, no API key needed for localhost), or - any other OpenAI-compatible endpoint —
http://on a loopback address (LM Studio, Ollama with its OpenAI shim, custom proxies) orhttps://on any host (e.g.https://openrouter.ai/api/v1,https://api.groq.com/openai/v1, or your own gateway athttps://your-gateway.example.com/v1). - See Supported providers for the full list and configuration notes.
- An API key for endpoints that require one.
- 9Router at
Install
- Package the extension:
npx @vscode/vsce package --no-dependencies - Install the resulting VSIX:
code --install-extension 9router-openai-chat-0.1.0.vsix - Reload the window.
Quick start
- Open the Command Palette (
Ctrl+Shift+P/⌘⇧P) and run 9Router: Manage Profiles. - Pick a preset:
- 9Router (localhost:20128/v1) — recommended for 9Router.
- OmniRoute (localhost:20128/v1) — recommended for OmniRoute (same local endpoint, 9Router-compatible).
- Custom OpenAI-compatible endpoint — for any other provider implementing the standard
GET /v1/models(OpenRouter, LM Studio, Ollama, Groq, vLLM, custom proxies, etc.).
- Enter the base URL. Accepted:
http://on loopback hosts (localhost,127.0.0.1,::1) orhttps://on any host, with a/v1path. The URL does not have to point at 9Router or OmniRoute — any provider exposing the OpenAI/v1standard works. - Enter your API key. It is saved to SecretStorage only (leave empty for loopback presets that don't require auth).
- The wizard lists models fetched from the gateway's standard
GET /v1/models— pick a default model. - Confirm and save. The profile (without the API key) is written to your user
settings.jsonand set as active. - Open the Chat view and select the model — it appears under the extension's vendor in the model picker, regardless of whether the underlying gateway is 9Router, OmniRoute, or any other
/v1/models-compatible provider.
Alternatively, configure a profile manually in your user settings.json:
{
"9routerOpenai.profiles": [
{
"id": "local-9router",
"name": "Local 9Router",
"baseUrl": "http://localhost:20128/v1",
},
],
"9routerOpenai.activeProfileId": "local-9router",
}
For endpoints that require a key, store it via the wizard (or via the SecretStorage key 9router-openai.<profileId>). Localhost endpoints without a stored key send no Authorization header.
Profiles & Secrets (BYOK)
- Multiple profiles can be stored; one active profile is used at a time (
9routerOpenai.activeProfileId). - Each profile is
{ id, name, baseUrl, modelOverride?, capabilitiesOverride? }and can point at any provider that exposes the standard OpenAI-compatible/v1API — 9Router, OmniRoute, OpenRouter, LM Studio, Ollama (OpenAI shim), Groq, vLLM, LiteLLM, custom proxies, etc. (see Supported providers). - API keys are never written to
settings.json. They live in VS Code SecretStorage (OS keychain) under9router-openai.<profileId>. - Bring-your-own-key: this extension is BYOK. Corporate policies for Copilot Business/Enterprise can hide third-party language model providers from the model picker — the extension is unaffected for personal/community use.
Models
- Models are discovered from the active profile's standard
GET /v1/modelsand cached (TTL) per profile. Because discovery is driven by the gateway itself, the extension works with any provider that follows the OpenAI/v1/modelsconvention — 9Router, OmniRoute, OpenRouter, LM Studio, Ollama (OpenAI shim), Groq, vLLM, custom proxies, etc. — without any per-provider code paths. - Use
modelOverride(comma-separated ids) on a profile to restrict the picker to specific models. - Model names in the picker are prefixed with the endpoint alias to disambiguate.
- Context window & capabilities now come from gateway metadata:
maxInputTokensis taken fromcontext_lengthandmaxOutputTokensfrommax_completion_tokensin the/v1/modelspayload;toolCalling/imageInputare derived fromcapabilities.tools/capabilities.vision. When the gateway does not expose those fields (e.g.kr/*or internal models) the extension falls back to262144(256K) /4096/falseso every model still appears in the picker. The fallback is configurable via9routerOpenai.chat.maxInputTokens(default262144, max1048576— supports 1M-context models) and9routerOpenai.chat.maxTokens(output, default2048, max128000). - capabilitiesOverride: a per-profile override
{ toolCalling?, imageInput? }in9routerOpenai.profileslets you force-enable (or suppress) capabilities on top of the mapped metadata — e.g. to enable tool calling for a gateway that reports it incorrectly. No contract change: this field already exists in profile config. - Model variants (9Router gateway): choosing
low/medium/highvariants in the picker selects the thinking level (reasoning effort) — the picker now shows a compactdetaillikeeffort: medium · thinking: gemini-level · ctx: 1Mand a hovertooltipwith context window, max output and capabilities (vision/audio/search/tools/reasoning/agentic). All 274 gateway models remain listed (no filtering); variant parsing recognizesextra-low/low/medium/high/xhigh/max(effort) andthinking/agentic/thinking-agentic/agent(mode). - No cost/price field — the gateway does not expose pricing and the VS Code LanguageModel API has no cost field; cost is out of scope.
- Token counting uses a character-based heuristic (≈1 token per 4 characters) — no tokenizer weights are available for arbitrary gateway models.
Inline completions
- Enabled by default (
9routerOpenai.inline.enabled). - Triggered after ~1.5 s of inactivity while typing, at a word boundary, in code files (excluded:
markdown,plaintext,json,jsonc,log,yaml,xml,csv). - FIM path: if the endpoint supports the OpenAI
/completions(FIM) API — probed once per model and cached — it uses a cheap prefix/suffix request. - Chat fallback: otherwise it uses a chat-completions prompt ("Complete the following code…"), returning only the completion.
- Non-streaming (platform limitation of the stable inline API).
Troubleshooting
- Model doesn't appear in the picker: confirm
activeProfileIdmatches a profile, and that a profile is configured (wizard orsettings.json). The provider returns no models when no key is stored for a key-required endpoint. - "Unauthorized (401): check the API key": the stored key is wrong or missing. Re-run 9Router: Manage Profiles for the profile.
- "Network error" / chat stops: the gateway is unreachable. Check it is running and reachable at the configured base URL.
- Inline completions don't show: check
9routerOpenai.inline.enabled, that the active profile has a stored key, and that the model id is set (9routerOpenai.chat.defaultModel). Completion only triggers at a word boundary after the debounce. - SSRF guard blocks a URL:
http://is only allowed for loopback hosts (localhost,127.0.0.1,::1); any non-loopback endpoint must usehttps://.
Security
- Loopback-only SSRF guard: HTTP base URLs are restricted to loopback hosts (
localhost,127.0.0.1,::1); anything else must usehttps://. This prevents server-side request forgery through the extension host. - API keys: SecretStorage only — never logged, never in
settings.json. Logs are redacted forsk-…patterns. - No telemetry: the extension collects and sends nothing.
- No remote code execution: gateway responses are inserted only as chat markdown or inline suggestions.
Development
npm install
npm run typecheck # tsc --noEmit
npm run lint # eslint
npm run build # esbuild bundle → out/
npm test # vitest unit tests
npm run package # vsce package --no-dependencies → .vsix
Architecture and design decisions: see docs/blueprint.md.
License
MIT — see LICENSE.