Skip to content
Closed
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
11 changes: 10 additions & 1 deletion packages/app/src/hooks/use-providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,16 @@ import { useParams } from "@solidjs/router"
import { createMemo } from "solid-js"

// kilocode_change start - Preferred providers list (order determines display priority)
export const popularProviders = ["kilo", "anthropic", "github-copilot", "openai", "google", "openrouter", "vercel"]
export const popularProviders = [
"kilo",
"anthropic",
"github-copilot",
"openai",
"google",
"openrouter",
"fastrouter",
"vercel",
]
// kilocode_change end
const popularProviderSet = new Set(popularProviders)

Expand Down
1 change: 1 addition & 0 deletions packages/kilo-docs/lib/nav/ai-providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export const AiProvidersNav: NavSection[] = [
title: "AI Gateways",
links: [
{ href: "/ai-providers/openrouter", children: "OpenRouter" },
{ href: "/ai-providers/fastrouter", children: "FastRouter" },
{ href: "/ai-providers/glama", children: "Glama" },
{ href: "/ai-providers/requesty", children: "Requesty" },
{ href: "/ai-providers/unbound", children: "Unbound" },
Expand Down
1 change: 1 addition & 0 deletions packages/kilo-docs/lychee.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ exclude = [
'^https?://vercel\.link/',
# API base URL, returns 404 when fetched directly
'^https?://api\.apertis\.ai/v1/?$',
'^https?://go\.fastrouter\.ai/api/v1/?$',
# Redirects to authenticated Google Cloud console
'^https?://console\.cloud\.google\.com',
# Consistently times out in CI
Expand Down
50 changes: 50 additions & 0 deletions packages/kilo-docs/pages/ai-providers/fastrouter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
sidebar_label: FastRouter
---

# Using FastRouter With Kilo Code

FastRouter is an LLM gateway that provides access to 150+ language models from providers like Anthropic, OpenAI, Google, and more — all through a single OpenAI-compatible API. It adds smart routing, observability, cost tracking, and fallback handling on top of the underlying providers.

**Website:** [https://fastrouter.ai/](https://fastrouter.ai/)

## Getting an API Key

1. **Sign Up/Sign In:** Go to the [FastRouter website](https://fastrouter.ai/) and create an account.
2. **Get an API Key:** Navigate to your dashboard and generate a new API key.
3. **Copy the Key:** Copy the displayed API key.

## Supported Models

Kilo Code automatically fetches the full list of available models from the FastRouter API. FastRouter provides access to models from:

- **Anthropic:** Claude 4, Claude 3.5 Sonnet, Claude Haiku, and more
- **OpenAI:** GPT-4o, GPT-4.1, o3, o4-mini, and more
- **Google:** Gemini 2.5 Pro, Gemini 2.5 Flash, and more
- **Meta, Mistral, DeepSeek, and many other open-source and proprietary models**

Refer to the [FastRouter models page](https://go.fastrouter.ai/api/v1/models) for the full up-to-date list.

## Configuration in Kilo Code

1. **Open Kilo Code Settings:** Click the gear icon ({% codicon name="gear" /%}) in the Kilo Code panel.
2. **Select Provider:** Choose "FastRouter" from the "API Provider" dropdown.
3. **Enter API Key:** Paste your FastRouter API key into the "FastRouter API Key" field.
4. **Select Model:** Choose your desired model from the "Model" dropdown. Models are grouped by the underlying provider.

Alternatively, set the `FASTROUTER_API_KEY` environment variable before launching Kilo Code and the provider will be configured automatically.

## Smart Routing

FastRouter routes each request to the best available inference backend based on availability, latency, and cost. This means:

- **Automatic failover** — if one backend is down, FastRouter retries on another
- **Lower latency** — requests are routed to the fastest available provider for the chosen model
- **Cost optimization** — routing can be tuned to prefer lower-cost backends

## Tips and Notes

- **Model IDs:** FastRouter uses the format `provider/model-name` (e.g., `anthropic/claude-haiku-4.5`, `openai/gpt-4o`), the same convention as OpenRouter.
- **Pricing:** Pricing follows the underlying model's cost. See the [FastRouter documentation](https://docs.fastrouter.ai) for details.
- **Multimodal support:** Many FastRouter models support image, audio, and video inputs in addition to text. Model capabilities are detected automatically.
- **Streaming:** FastRouter fully supports streaming responses, which Kilo Code uses by default.
1 change: 1 addition & 0 deletions packages/kilo-docs/pages/ai-providers/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Run models on your own hardware for privacy and offline use:
Route requests through unified APIs with additional features:

- **[OpenRouter](/docs/ai-providers/openrouter)** - Access multiple providers through one API
- **[FastRouter](/docs/ai-providers/fastrouter)** - LLM gateway with smart routing and observability
- **[Glama](/docs/ai-providers/glama)** - Enterprise AI gateway
- **[Requesty](/docs/ai-providers/requesty)** - Smart routing and fallbacks

Expand Down
6 changes: 5 additions & 1 deletion packages/kilo-docs/source-links.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Source Code Links

<!-- Auto-generated by script/extract-source-links.ts — DO NOT EDIT -->
<!-- 77 unique URLs extracted from extension and CLI source -->
<!-- 79 unique URLs extracted from extension and CLI source -->

- <https://api.apertis.ai/v1>
<!-- packages/opencode/src/provider/model-cache.ts -->
Expand Down Expand Up @@ -65,6 +65,10 @@
<!-- packages/opencode/src/server/server.ts -->
- <https://gitlab.com>
<!-- packages/opencode/src/provider/provider.ts -->
- <https://go.fastrouter.ai/api/v1/>
<!-- packages/opencode/src/provider/models.ts -->
- <https://go.fastrouter.ai/api/v1/models>
<!-- packages/opencode/src/provider/model-cache.ts -->
- <https://julialang.org/downloads/>
<!-- packages/opencode/src/lsp/server.ts -->
- <https://kilo.ai>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ export const PROVIDER_PRIORITY: Record<string, number> = {
"github-copilot": 1,
openai: 2,
google: 3,
openrouter: 4,
fastrouter: 5,
}

// ---------------------------------------------------------------------------
Expand Down
19 changes: 19 additions & 0 deletions packages/opencode/src/kilocode/provider/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,24 @@ export function kiloCustomLoaders(dep: CustomDep): Record<string, CustomLoader>
options: {},
}),

fastrouter: Effect.fnUntraced(function* (input: any) {
const env = Env.all()
const apiKey = yield* Effect.gen(function* () {
const envKey = input.env.map((e: string) => env[e]).find(Boolean)
if (envKey) return envKey
const auth = yield* dep.auth(input.id)
if (auth?.type === "api") return auth.key
return (yield* dep.config()).provider?.["fastrouter"]?.options?.apiKey
})
return {
autoload: !!apiKey && Object.keys(input.models).length > 0,
options: {
...(apiKey ? { apiKey } : {}),
headers: DEFAULT_HEADERS,
},
}
}),

kilo: Effect.fnUntraced(function* (input: any) {
const env = Env.all()
const hasKey = yield* Effect.gen(function* () {
Expand Down Expand Up @@ -182,6 +200,7 @@ export function patchCustomLoaderResult(providerID: string, result: { options?:
break
}
case "openrouter":
case "fastrouter":
case "vercel":
case "zenmux":
result.options.headers = { ...result.options.headers, ...DEFAULT_HEADERS }
Expand Down
49 changes: 49 additions & 0 deletions packages/opencode/src/provider/model-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,9 @@ export namespace ModelCache {
if (providerID === "apertis") {
return fetchApertisModels(options)
}
if (providerID === "fastrouter") {
return fetchFastRouterModels()
}
// kilocode_change end

// Other providers not implemented yet
Expand Down Expand Up @@ -225,6 +228,52 @@ export namespace ModelCache {

return models
}

async function fetchFastRouterModels(): Promise<Record<string, any>> {
const resp = await globalThis
.fetch("https://go.fastrouter.ai/api/v1/models", {
signal: AbortSignal.timeout(10 * 1000),
})
.catch((err) => {
log.error("fastrouter models fetch failed", { err })
return undefined
})
if (!resp) return {}
if (!resp.ok) {
log.error("fastrouter models fetch failed", { status: resp.status })
return {}
}
const data = await resp.json()
const models: Record<string, any> = {}
for (const m of data.data ?? []) {
if (!m.id) continue
const modality: string = m.architecture?.modality ?? "text->text"
const [rawInput = "text", rawOutput = "text"] = modality.split("->")
const allowed = ["text", "image", "audio", "video", "pdf"]
const inputs = rawInput.split("+").filter((p: string) => allowed.includes(p))
const outputs = rawOutput.split("+").filter((p: string) => allowed.includes(p))
models[m.id] = {
id: m.id,
name: m.name || m.id,
release_date: "",
attachment: inputs.includes("image"),
reasoning: false,
temperature: true,
tool_call: true,
cost: {
input: parseFloat(m.pricing?.prompt || "0") * 1_000_000,
output: parseFloat(m.pricing?.completion || "0") * 1_000_000,
},
limit: {
context: m.context_length || 128_000,
output: m.max_completion_tokens || 16_384,
},
modalities: { input: inputs, output: outputs },
options: {},
}
}
return models
}
// kilocode_change end

/**
Expand Down
23 changes: 23 additions & 0 deletions packages/opencode/src/provider/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,29 @@ export namespace ModelsDev {
}
}

// kilocode_change start
const frAllowed = (!enabled || enabled.has("fastrouter")) && !disabled.has("fastrouter")
if (frAllowed) {
const priorFr = providers["fastrouter"]
const snapshotModels = priorFr?.models && Object.keys(priorFr.models).length > 0 ? priorFr.models : {}
let frModels = await ModelCache.fetch("fastrouter").catch(() => ({}))
if (Object.keys(frModels).length === 0) {
frModels = await ModelCache.refresh("fastrouter").catch(() => ({}))
}
const frModelsFinal = Object.keys(frModels).length > 0 ? frModels : snapshotModels
providers["fastrouter"] = {
id: "fastrouter",
name: "FastRouter",
env: ["FASTROUTER_API_KEY"],
api: "https://go.fastrouter.ai/api/v1/",
npm: "@openrouter/ai-sdk-provider",
models: frModelsFinal,
}
} else if (providers["fastrouter"]) {
delete providers["fastrouter"]
}
// kilocode_change end

return providers
// kilocode_change end
}
Expand Down
10 changes: 7 additions & 3 deletions packages/opencode/src/provider/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -921,14 +921,18 @@ export namespace ProviderTransform {
}
return { thinkingConfig: { thinkingBudget: 0 } }
}
if (model.providerID === "openrouter" || model.api.npm === "@kilocode/kilo-gateway") {
// kilocode_change - add Kilo Gateway support
// kilocode_change start
if (
model.providerID === "openrouter" ||
model.providerID === "fastrouter" ||
model.api.npm === "@kilocode/kilo-gateway"
) {
if (model.api.id.includes("google")) {
return { reasoning: { enabled: false } }
}
// Other models use reasoningEffort (AI SDK format)
return { reasoningEffort: "minimal" }
}
// kilocode_change end

if (model.providerID === "venice") {
return { veniceParameters: { disableThinking: true } }
Expand Down