From 2fa57d0fd350baf5e308026ba53caec74c251f9c Mon Sep 17 00:00:00 2001 From: Nirupam Bhowmick <48842933+heydryft@users.noreply.github.com> Date: Sat, 24 Oct 2026 22:24:50 +0100 Subject: [PATCH 1/4] feat(providers): add Runcrate to the free directory --- src/providers/free-directory.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/providers/free-directory.ts b/src/providers/free-directory.ts index 467c3e6b7d7..4294798b991 100644 --- a/src/providers/free-directory.ts +++ b/src/providers/free-directory.ts @@ -12,7 +12,7 @@ export const FREE_PROVIDER_ACCESS_GROUPS = { "cloudflare-ai", "cohere", "coze", "duckduckgo-web", "felo-web", "friendliai", "gemini", "github-models", "groq", "hackclub", "huggingchat", "huggingface", "iflytek", "inference-net", "kiro", "liquid", "llm7", "mistral", "morph", "muse-spark-web", "nara", "navy", "nlpcloud", "ollama-cloud", "opencode", "openrouter", - "ovhcloud", "pollinations", "puter", "qwen-web", "reka", "sambanova", "sparkdesk", "t3-web", "uncloseai", + "ovhcloud", "pollinations", "puter", "qwen-web", "reka", "runcrate", "sambanova", "sparkdesk", "t3-web", "uncloseai", ], "recurring-uncapped": [ "agnes", "ainative", "aion", "baidu", "glm", "glm-cn", "kilo-gateway", "opencode-zen", "requesty", @@ -119,6 +119,9 @@ const CONNECTABLE: Record = { ovhcloud: openAi("https://oai.endpoints.kepler.ai.cloud.ovh.net/v1", "https://console.ovhcloud.com", { keyOptional: true, modelsUrl: "https://oai.endpoints.kepler.ai.cloud.ovh.net/v1/models" }), pollinations: openAi("https://gen.pollinations.ai/v1", "https://pollinations.ai", { keyOptional: true }), reka: openAi("https://api.reka.ai/v1", "https://platform.reka.ai"), + // Verified 2026-10-24: GET /v1/models is public (no key) and returns the OpenAI-shaped catalog; + // a chat completion with deepseek-ai/DeepSeek-V4.1-Flash returned a standard chat.completion. + runcrate: openAi("https://api.runcrate.ai/v1", "https://www.runcrate.ai/dashboard/api-keys", { verification: "official", documentationUrl: "https://www.runcrate.ai/openapi.json", modelsUrl: "https://api.runcrate.ai/v1/models", lastVerified: "2026-10-24", note: "Open models are free with your GitHub account." }), sambanova: openAi("https://api.sambanova.ai/v1", "https://cloud.sambanova.ai/apis", { supportLevel: "supported", verification: "official", documentationUrl: "https://docs.sambanova.ai/docs/en/get-started/api-keys-urls", modelsUrl: "https://api.sambanova.ai/v1/models", lastVerified: "2026-08-02" }), sparkdesk: openAi("https://spark-api-open.xf-yun.com/v1", "https://console.xfyun.cn", { verification: "official", documentationUrl: "https://www.xfyun.cn/doc/spark/HTTP%E8%B0%83%E7%94%A8%E6%96%87%E6%A1%A3.html" }), agnes: openAi("https://apihub.agnes-ai.com/v1", "https://agnes-ai.com"), @@ -202,7 +205,7 @@ const LABELS: Record = { ai21: "AI21", baichuan: "Baichuan", crusoe: "Crusoe", deepinfra: "DeepInfra", deepseek: "DeepSeek", doubao: "Doubao", "freemodel-dev": "FreeModel.dev", sambanova: "SambaNova Cloud", nebius: "Nebius Token Factory", novita: "Novita", nscale: "Nscale", nvidia: "NVIDIA NIM", - publicai: "PublicAI", qoder: "Qoder", "qoder-cn": "Qoder CN", sensenova: "SenseNova", stepfun: "StepFun", vertex: "Google Vertex AI", + publicai: "PublicAI", qoder: "Qoder", "qoder-cn": "Qoder CN", runcrate: "Runcrate", sensenova: "SenseNova", stepfun: "StepFun", vertex: "Google Vertex AI", }; const referenceNote = "Reference entry only: no safe documented API integration is enabled. Configure it manually only with provider documentation; consumer-web cookies and anti-bot bypasses are intentionally unsupported."; From 904dd6af2912dee7a093d8de7a7e0788ad8d7ddb Mon Sep 17 00:00:00 2001 From: Nirupam Bhowmick <48842933+heydryft@users.noreply.github.com> Date: Mon, 26 Oct 2026 08:32:17 +0000 Subject: [PATCH 2/4] fix(providers): bound Runcrate free allowance in the directory row Only five named open models have a free allowance (GitHub account at least 30 days old, limited per session and per week, shared by the workspace). Seed those ids, keep live discovery of the full paid catalog, cite the free-allowance page, chat-completions docs and terms, and add a focused projection test for the row. --- src/providers/free-directory.ts | 11 ++++-- .../provider-registry-parity.test.ts | 39 +++++++++++++++++++ 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/providers/free-directory.ts b/src/providers/free-directory.ts index 4294798b991..815ca1075db 100644 --- a/src/providers/free-directory.ts +++ b/src/providers/free-directory.ts @@ -119,9 +119,14 @@ const CONNECTABLE: Record = { ovhcloud: openAi("https://oai.endpoints.kepler.ai.cloud.ovh.net/v1", "https://console.ovhcloud.com", { keyOptional: true, modelsUrl: "https://oai.endpoints.kepler.ai.cloud.ovh.net/v1/models" }), pollinations: openAi("https://gen.pollinations.ai/v1", "https://pollinations.ai", { keyOptional: true }), reka: openAi("https://api.reka.ai/v1", "https://platform.reka.ai"), - // Verified 2026-10-24: GET /v1/models is public (no key) and returns the OpenAI-shaped catalog; - // a chat completion with deepseek-ai/DeepSeek-V4.1-Flash returned a standard chat.completion. - runcrate: openAi("https://api.runcrate.ai/v1", "https://www.runcrate.ai/dashboard/api-keys", { verification: "official", documentationUrl: "https://www.runcrate.ai/openapi.json", modelsUrl: "https://api.runcrate.ai/v1/models", lastVerified: "2026-10-24", note: "Open models are free with your GitHub account." }), + // Checked 2026-10-26. Free terms: https://www.runcrate.ai/inference/free covers five named open + // models only, one allowance per GitHub account at least 30 days old, limited per session and per + // week and shared by the workspace; a workspace with credit continues as paid traffic once the + // allowance is used or free capacity is busy. Endpoint: https://www.runcrate.ai/docs/models/chat-completions. + // Operator: Aeonmind LLC (https://www.runcrate.ai/terms). GET /v1/models is public and returned + // 193 rows (text, image, video, audio, embeddings), all with paid list prices, including the five + // free ids. Discovery is hybrid: the five free ids are seeded, and live discovery still lists the full paid catalog. + runcrate: openAi("https://api.runcrate.ai/v1", "https://www.runcrate.ai/dashboard/api-keys", { verification: "official", documentationUrl: "https://www.runcrate.ai/docs/models/chat-completions", modelsUrl: "https://api.runcrate.ai/v1/models", lastVerified: "2026-10-26", discovery: "hybrid", models: ["deepseek-ai/DeepSeek-V4.1-Flash", "zai-org/GLM-5.3-Flash", "zai-org/GLM-5.3", "openai/gpt-oss-120b", "Qwen/Qwen3.8-Flash"], note: "Only the five listed open models have a free allowance: one per GitHub account at least 30 days old, limited per session and per week, and shared by the workspace. Every other discovered model is paid; chat inference uses published per-token rates. When the allowance is used up or free capacity is busy, a workspace with credit continues as paid traffic; without credit, requests stop until the allowance resets or capacity frees up. Terms: https://www.runcrate.ai/inference/free" }), sambanova: openAi("https://api.sambanova.ai/v1", "https://cloud.sambanova.ai/apis", { supportLevel: "supported", verification: "official", documentationUrl: "https://docs.sambanova.ai/docs/en/get-started/api-keys-urls", modelsUrl: "https://api.sambanova.ai/v1/models", lastVerified: "2026-08-02" }), sparkdesk: openAi("https://spark-api-open.xf-yun.com/v1", "https://console.xfyun.cn", { verification: "official", documentationUrl: "https://www.xfyun.cn/doc/spark/HTTP%E8%B0%83%E7%94%A8%E6%96%87%E6%A1%A3.html" }), agnes: openAi("https://apihub.agnes-ai.com/v1", "https://agnes-ai.com"), diff --git a/tests/providers/provider-registry-parity.test.ts b/tests/providers/provider-registry-parity.test.ts index 68787660838..3031ee0bbee 100644 --- a/tests/providers/provider-registry-parity.test.ts +++ b/tests/providers/provider-registry-parity.test.ts @@ -1903,6 +1903,45 @@ describe("free-provider directory isolation", () => { } }); + test("the Runcrate row bounds its free allowance and keeps the paid catalog discoverable", () => { + // The row accepts a key and discovers the full /v1/models catalog, which is mostly paid. Only + // five named models have a free allowance (runcrate.ai/inference/free, checked 2026-10-26), so + // the note must carry those bounds instead of advertising the whole catalog as free. + const row = FREE_PROVIDER_DIRECTORY.find(entry => entry.id === "runcrate"); + expect(row).toMatchObject({ + label: "Runcrate", + adapter: "openai-chat", + baseUrl: "https://api.runcrate.ai/v1", + authKind: "key", + accessGroups: ["recurring-or-keyless"], + supportLevel: "experimental", + verification: "official", + documentationUrl: "https://www.runcrate.ai/docs/models/chat-completions", + modelsUrl: "https://api.runcrate.ai/v1/models", + dashboardUrl: "https://www.runcrate.ai/dashboard/api-keys", + discovery: "hybrid", + liveModels: true, + lastVerified: "2026-10-26", + }); + expect(row?.keyOptional).toBeUndefined(); + expect(row?.models).toEqual([ + "deepseek-ai/DeepSeek-V4.1-Flash", + "zai-org/GLM-5.3-Flash", + "zai-org/GLM-5.3", + "openai/gpt-oss-120b", + "Qwen/Qwen3.8-Flash", + ]); + const note = row?.note ?? ""; + expect(note).toContain("Only the five listed open models have a free allowance"); + expect(note).toContain("GitHub account at least 30 days old"); + expect(note).toContain("per session and per week"); + expect(note).toContain("Every other discovered model is paid"); + expect(note).toContain("continues as paid traffic"); + expect(note).toContain("https://www.runcrate.ai/inference/free"); + // Directory-only: it must not become a canonical credential destination. + expect(PROVIDER_REGISTRY.some(entry => entry.id === "runcrate")).toBe(false); + }); + /* * #1057. The registry classifies DeepSeek V4 ids Flash-versus-Pro with a substring * test, which is correct for every id shipped today but is exactly the kind of From f98bf002d3657efee58f2d983ada499d4f21c1a1 Mon Sep 17 00:00:00 2001 From: JUN <243035832+lidge-jun@users.noreply.github.com> Date: Mon, 26 Oct 2026 23:33:29 +0900 Subject: [PATCH 3/4] test(providers): move the Runcrate row test to a sibling file provider-registry-parity.test.ts sits at the 2000-line file-size threshold; the new case pushed it to 2034 lines (NEW_OVERSIZED) once merged with dev. Move the case byte for byte into tests/providers/free-directory-runcrate.test.ts, registered in a new explicitShards map and the expected layout fixture. --- scripts/test-layout/layout-providers.json | 3 ++ scripts/test-layout/layout.json | 2 +- tests/fixtures/test-layout-expected.json | 2 +- .../providers/free-directory-runcrate.test.ts | 45 +++++++++++++++++++ .../provider-registry-parity.test.ts | 39 ---------------- 5 files changed, 50 insertions(+), 41 deletions(-) create mode 100644 scripts/test-layout/layout-providers.json create mode 100644 tests/providers/free-directory-runcrate.test.ts diff --git a/scripts/test-layout/layout-providers.json b/scripts/test-layout/layout-providers.json new file mode 100644 index 00000000000..318ce2a79d0 --- /dev/null +++ b/scripts/test-layout/layout-providers.json @@ -0,0 +1,3 @@ +{ + "free-directory-runcrate.test.ts": "providers" +} diff --git a/scripts/test-layout/layout.json b/scripts/test-layout/layout.json index bc228afaffb..7f9e4d274c1 100644 --- a/scripts/test-layout/layout.json +++ b/scripts/test-layout/layout.json @@ -1,7 +1,7 @@ { "version": 1, "root": "tests", - "explicitShards": ["layout-clients.json", "layout-codex-collision.json", "layout-agent-assertion.json", "layout-chat.json", "layout-replay.json", "layout-adapters.json", "layout-jev.json", "layout-workbuddy.json", "layout-combos.json"], + "explicitShards": ["layout-clients.json", "layout-codex-collision.json", "layout-agent-assertion.json", "layout-chat.json", "layout-replay.json", "layout-adapters.json", "layout-jev.json", "layout-workbuddy.json", "layout-combos.json", "layout-providers.json"], "explicit": { "combo-description.test.ts": "routing", "combo-description-catalog.test.ts": "codex-integration", "codex-provider-routing.test.ts": "codex-integration", "config-native-reasoning-retention.test.ts": "config", "server-native-reasoning-retention-merge.test.ts": "server", "server-native-reasoning-retention.test.ts": "server", "responses-retained-reasoning-stream-recovery.test.ts": "responses", "openai-native-hosted-search-history.test.ts": "adapters/openai", "responses-native-compact-search-history.test.ts": "responses", "config-hosted-search-projection.test.ts": "config", "gcp-credential-boundary.test.ts": "adapters/google", "gcp-sa-marker.test.ts": "adapters/google", "gcp-sa-adapter.test.ts": "adapters/google", "management-gcp-credential-input.test.ts": "server", "codex-preserve-signed-in.test.ts": "codex-integration", "cli-account-preservation.test.ts": "cli", "cli-provider-list-active.test.ts": "cli", "service-state-isolation.test.ts": "service", "compaction-history-framing.test.ts": "responses", "catalog-native-max-context.test.ts": "codex-integration", "protocol-argon-thought-boundary.test.ts": "responses", "service-log.test.ts": "service", "messages-native-empty-inline.test.ts": "claude-integration", "catalog-auto-review-override.test.ts": "codex-integration", "management-auto-review-settings.test.ts": "server", "management-auto-review-auth.test.ts": "server", "prefer-model-alias-ids.test.ts": "server", "responses-mimo-envelope.test.ts": "responses", "responses-call-id-linkage.test.ts": "responses", "bridge-terminal-refusal.test.ts": "adapters", "loop-terminal-refusal.test.ts": "images", "web-search-terminal-refusal.test.ts": "web-search", "combo-terminal-refusal.test.ts": "routing", "server-terminal-refusal-wiring.test.ts": "server", "incomplete-reason-normalization.test.ts": "responses", "advisor-retention.test.ts": "advisor", "responses-native-key-cooldown.test.ts": "responses", "responses-run-turn-key-readmission.test.ts": "responses", "qoder-spawn-readmission.test.ts": "adapters", "api-key-selection-cooldown.test.ts": "providers", "api-key-auto-pause-distribution.test.ts": "providers", "explicit-key-pause-oauth-boundary.test.ts": "providers", "server-chat-native-key-selection.test.ts": "server", "compact-key-selection.test.ts": "responses", "messages-native-key-selection.test.ts": "claude-integration", "coding-agent-windows-dispatch.test.ts": "adapters", "coding-agent-native-empty-argv.test.ts": "windows", "service-proxy-env.test.ts": "service", "openai-chat-standalone-bare-block.test.ts": "adapters/openai", "owner-registry-isolation.test.ts": "config", diff --git a/tests/fixtures/test-layout-expected.json b/tests/fixtures/test-layout-expected.json index c7858ea3657..507708a3de4 100644 --- a/tests/fixtures/test-layout-expected.json +++ b/tests/fixtures/test-layout-expected.json @@ -1040,7 +1040,7 @@ "provider-outbound.test.ts": "providers", "provider-runtime-fetch.test.ts": "providers", "provider-tls-profile.test.ts": "providers", "provider-payload.test.ts": "gui", "provider-quota-label-sanitize.test.ts": "providers", "provider-quota-observed-marker.test.ts": "providers", - "provider-quota-abliteration.test.ts": "providers", "provider-quota-ollama-cloud.test.ts": "providers", "provider-quota.test.ts": "providers", "provider-registry-parity.test.ts": "providers", "haiku-5-5-catalog.test.ts": "providers", + "provider-quota-abliteration.test.ts": "providers", "provider-quota-ollama-cloud.test.ts": "providers", "provider-quota.test.ts": "providers", "provider-registry-parity.test.ts": "providers", "free-directory-runcrate.test.ts": "providers", "haiku-5-5-catalog.test.ts": "providers", "provider-send-path-import.test.ts": "server", "provider-static-model-discovery.test.ts": "providers", "provider-subscription-cli.test.ts": "gui", "provider-workspace-auth.test.ts": "gui", "provider-workspace-data.test.ts": "gui", "provider-workspace-rail.test.ts": "gui", diff --git a/tests/providers/free-directory-runcrate.test.ts b/tests/providers/free-directory-runcrate.test.ts new file mode 100644 index 00000000000..97debdd43c4 --- /dev/null +++ b/tests/providers/free-directory-runcrate.test.ts @@ -0,0 +1,45 @@ +import { describe, expect, test } from "bun:test"; +import { FREE_PROVIDER_DIRECTORY } from "../../src/providers/free-directory"; +import { PROVIDER_REGISTRY } from "../../src/providers/registry"; + +// Sibling of provider-registry-parity.test.ts, which sits at the file-size ratchet threshold. +describe("free-provider directory: Runcrate", () => { + test("the Runcrate row bounds its free allowance and keeps the paid catalog discoverable", () => { + // The row accepts a key and discovers the full /v1/models catalog, which is mostly paid. Only + // five named models have a free allowance (runcrate.ai/inference/free, checked 2026-10-26), so + // the note must carry those bounds instead of advertising the whole catalog as free. + const row = FREE_PROVIDER_DIRECTORY.find(entry => entry.id === "runcrate"); + expect(row).toMatchObject({ + label: "Runcrate", + adapter: "openai-chat", + baseUrl: "https://api.runcrate.ai/v1", + authKind: "key", + accessGroups: ["recurring-or-keyless"], + supportLevel: "experimental", + verification: "official", + documentationUrl: "https://www.runcrate.ai/docs/models/chat-completions", + modelsUrl: "https://api.runcrate.ai/v1/models", + dashboardUrl: "https://www.runcrate.ai/dashboard/api-keys", + discovery: "hybrid", + liveModels: true, + lastVerified: "2026-10-26", + }); + expect(row?.keyOptional).toBeUndefined(); + expect(row?.models).toEqual([ + "deepseek-ai/DeepSeek-V4.1-Flash", + "zai-org/GLM-5.3-Flash", + "zai-org/GLM-5.3", + "openai/gpt-oss-120b", + "Qwen/Qwen3.8-Flash", + ]); + const note = row?.note ?? ""; + expect(note).toContain("Only the five listed open models have a free allowance"); + expect(note).toContain("GitHub account at least 30 days old"); + expect(note).toContain("per session and per week"); + expect(note).toContain("Every other discovered model is paid"); + expect(note).toContain("continues as paid traffic"); + expect(note).toContain("https://www.runcrate.ai/inference/free"); + // Directory-only: it must not become a canonical credential destination. + expect(PROVIDER_REGISTRY.some(entry => entry.id === "runcrate")).toBe(false); + }); +}); diff --git a/tests/providers/provider-registry-parity.test.ts b/tests/providers/provider-registry-parity.test.ts index 4014498347e..cf40389c886 100644 --- a/tests/providers/provider-registry-parity.test.ts +++ b/tests/providers/provider-registry-parity.test.ts @@ -1900,45 +1900,6 @@ describe("free-provider directory isolation", () => { } }); - test("the Runcrate row bounds its free allowance and keeps the paid catalog discoverable", () => { - // The row accepts a key and discovers the full /v1/models catalog, which is mostly paid. Only - // five named models have a free allowance (runcrate.ai/inference/free, checked 2026-10-26), so - // the note must carry those bounds instead of advertising the whole catalog as free. - const row = FREE_PROVIDER_DIRECTORY.find(entry => entry.id === "runcrate"); - expect(row).toMatchObject({ - label: "Runcrate", - adapter: "openai-chat", - baseUrl: "https://api.runcrate.ai/v1", - authKind: "key", - accessGroups: ["recurring-or-keyless"], - supportLevel: "experimental", - verification: "official", - documentationUrl: "https://www.runcrate.ai/docs/models/chat-completions", - modelsUrl: "https://api.runcrate.ai/v1/models", - dashboardUrl: "https://www.runcrate.ai/dashboard/api-keys", - discovery: "hybrid", - liveModels: true, - lastVerified: "2026-10-26", - }); - expect(row?.keyOptional).toBeUndefined(); - expect(row?.models).toEqual([ - "deepseek-ai/DeepSeek-V4.1-Flash", - "zai-org/GLM-5.3-Flash", - "zai-org/GLM-5.3", - "openai/gpt-oss-120b", - "Qwen/Qwen3.8-Flash", - ]); - const note = row?.note ?? ""; - expect(note).toContain("Only the five listed open models have a free allowance"); - expect(note).toContain("GitHub account at least 30 days old"); - expect(note).toContain("per session and per week"); - expect(note).toContain("Every other discovered model is paid"); - expect(note).toContain("continues as paid traffic"); - expect(note).toContain("https://www.runcrate.ai/inference/free"); - // Directory-only: it must not become a canonical credential destination. - expect(PROVIDER_REGISTRY.some(entry => entry.id === "runcrate")).toBe(false); - }); - /* * #1057. The registry classifies DeepSeek V4 ids Flash-versus-Pro with a substring * test, which is correct for every id shipped today but is exactly the kind of From 8b560faf9a20d4bf80018005f83e0581bf1d656b Mon Sep 17 00:00:00 2001 From: JUN <243035832+lidge-jun@users.noreply.github.com> Date: Tue, 27 Oct 2026 17:14:40 +0900 Subject: [PATCH 4/4] fix(providers): list RunCrate's sixth free model, DeepSeek V4.1 Flash 1017 RunCrate's free page and /v1/models (free_tier: true) include deepseek-ai/DeepSeek-V4.1-Flash-1017, which the entry omitted while saying every other model is paid. Checked 2026-10-27. --- src/providers/free-directory.ts | 8 ++++---- tests/providers/free-directory-runcrate.test.ts | 9 +++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/providers/free-directory.ts b/src/providers/free-directory.ts index 815ca1075db..9653fe27694 100644 --- a/src/providers/free-directory.ts +++ b/src/providers/free-directory.ts @@ -119,14 +119,14 @@ const CONNECTABLE: Record = { ovhcloud: openAi("https://oai.endpoints.kepler.ai.cloud.ovh.net/v1", "https://console.ovhcloud.com", { keyOptional: true, modelsUrl: "https://oai.endpoints.kepler.ai.cloud.ovh.net/v1/models" }), pollinations: openAi("https://gen.pollinations.ai/v1", "https://pollinations.ai", { keyOptional: true }), reka: openAi("https://api.reka.ai/v1", "https://platform.reka.ai"), - // Checked 2026-10-26. Free terms: https://www.runcrate.ai/inference/free covers five named open + // Checked 2026-10-27. Free terms: https://www.runcrate.ai/inference/free covers six named open // models only, one allowance per GitHub account at least 30 days old, limited per session and per // week and shared by the workspace; a workspace with credit continues as paid traffic once the // allowance is used or free capacity is busy. Endpoint: https://www.runcrate.ai/docs/models/chat-completions. // Operator: Aeonmind LLC (https://www.runcrate.ai/terms). GET /v1/models is public and returned - // 193 rows (text, image, video, audio, embeddings), all with paid list prices, including the five - // free ids. Discovery is hybrid: the five free ids are seeded, and live discovery still lists the full paid catalog. - runcrate: openAi("https://api.runcrate.ai/v1", "https://www.runcrate.ai/dashboard/api-keys", { verification: "official", documentationUrl: "https://www.runcrate.ai/docs/models/chat-completions", modelsUrl: "https://api.runcrate.ai/v1/models", lastVerified: "2026-10-26", discovery: "hybrid", models: ["deepseek-ai/DeepSeek-V4.1-Flash", "zai-org/GLM-5.3-Flash", "zai-org/GLM-5.3", "openai/gpt-oss-120b", "Qwen/Qwen3.8-Flash"], note: "Only the five listed open models have a free allowance: one per GitHub account at least 30 days old, limited per session and per week, and shared by the workspace. Every other discovered model is paid; chat inference uses published per-token rates. When the allowance is used up or free capacity is busy, a workspace with credit continues as paid traffic; without credit, requests stop until the allowance resets or capacity frees up. Terms: https://www.runcrate.ai/inference/free" }), + // 193 rows (text, image, video, audio, embeddings), all with paid list prices; six carry free_tier: true. + // Discovery is hybrid: the six free ids are seeded, and live discovery still lists the full paid catalog. + runcrate: openAi("https://api.runcrate.ai/v1", "https://www.runcrate.ai/dashboard/api-keys", { verification: "official", documentationUrl: "https://www.runcrate.ai/docs/models/chat-completions", modelsUrl: "https://api.runcrate.ai/v1/models", lastVerified: "2026-10-27", discovery: "hybrid", models: ["deepseek-ai/DeepSeek-V4.1-Flash", "deepseek-ai/DeepSeek-V4.1-Flash-1017", "zai-org/GLM-5.3-Flash", "zai-org/GLM-5.3", "openai/gpt-oss-120b", "Qwen/Qwen3.8-Flash"], note: "Only the six listed open models have a free allowance: one per GitHub account at least 30 days old, limited per session and per week, and shared by the workspace. Every other discovered model is paid; chat inference uses published per-token rates. When the allowance is used up or free capacity is busy, a workspace with credit continues as paid traffic; without credit, requests stop until the allowance resets or capacity frees up. Terms: https://www.runcrate.ai/inference/free" }), sambanova: openAi("https://api.sambanova.ai/v1", "https://cloud.sambanova.ai/apis", { supportLevel: "supported", verification: "official", documentationUrl: "https://docs.sambanova.ai/docs/en/get-started/api-keys-urls", modelsUrl: "https://api.sambanova.ai/v1/models", lastVerified: "2026-08-02" }), sparkdesk: openAi("https://spark-api-open.xf-yun.com/v1", "https://console.xfyun.cn", { verification: "official", documentationUrl: "https://www.xfyun.cn/doc/spark/HTTP%E8%B0%83%E7%94%A8%E6%96%87%E6%A1%A3.html" }), agnes: openAi("https://apihub.agnes-ai.com/v1", "https://agnes-ai.com"), diff --git a/tests/providers/free-directory-runcrate.test.ts b/tests/providers/free-directory-runcrate.test.ts index 97debdd43c4..bde88a96f09 100644 --- a/tests/providers/free-directory-runcrate.test.ts +++ b/tests/providers/free-directory-runcrate.test.ts @@ -6,8 +6,8 @@ import { PROVIDER_REGISTRY } from "../../src/providers/registry"; describe("free-provider directory: Runcrate", () => { test("the Runcrate row bounds its free allowance and keeps the paid catalog discoverable", () => { // The row accepts a key and discovers the full /v1/models catalog, which is mostly paid. Only - // five named models have a free allowance (runcrate.ai/inference/free, checked 2026-10-26), so - // the note must carry those bounds instead of advertising the whole catalog as free. + // six named models have a free allowance (runcrate.ai/inference/free and free_tier in /v1/models, + // checked 2026-10-27), so the note must carry those bounds instead of advertising the whole catalog as free. const row = FREE_PROVIDER_DIRECTORY.find(entry => entry.id === "runcrate"); expect(row).toMatchObject({ label: "Runcrate", @@ -22,18 +22,19 @@ describe("free-provider directory: Runcrate", () => { dashboardUrl: "https://www.runcrate.ai/dashboard/api-keys", discovery: "hybrid", liveModels: true, - lastVerified: "2026-10-26", + lastVerified: "2026-10-27", }); expect(row?.keyOptional).toBeUndefined(); expect(row?.models).toEqual([ "deepseek-ai/DeepSeek-V4.1-Flash", + "deepseek-ai/DeepSeek-V4.1-Flash-1017", "zai-org/GLM-5.3-Flash", "zai-org/GLM-5.3", "openai/gpt-oss-120b", "Qwen/Qwen3.8-Flash", ]); const note = row?.note ?? ""; - expect(note).toContain("Only the five listed open models have a free allowance"); + expect(note).toContain("Only the six listed open models have a free allowance"); expect(note).toContain("GitHub account at least 30 days old"); expect(note).toContain("per session and per week"); expect(note).toContain("Every other discovered model is paid");