mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-16 12:12:23 +03:00
fix(providers): repoint freeaiapikey to its live API host and resync its catalog (#10233)
* fix(providers): point freeaiapikey at the api. host it moved to Every /v1 route on the freeaiapikey.com apex host answers HTTP 410 with type "endpoint_moved", and the body names its own replacement: "This API endpoint has moved. Please update your base_url to https://api.freeaiapikey.com/v1 - the old endpoint on freeaiapikey.com no longer works." Probed 2026-08-13 with paired controls so a network fault could not be read as an upstream verdict: GET https://freeaiapikey.com/v1/models -> 410 GET https://freeaiapikey.com/v1/chat/completions -> 410 GET https://api.freeaiapikey.com/v1/models -> 200 GET https://api.freeaiapikey.com/v1/chat/completions -> 405 (POST-only) GET https://api.openai.com/v1/models -> 401 (control: reachable) GET https://<nonexistent-domain>/v1/models -> 000 (control: unreachable) Every request through this provider therefore fails today. Repoint baseUrl and modelsUrl at the host upstream names. * fix(providers): resync the freeaiapikey catalog with its live model list GET https://api.freeaiapikey.com/v1/models (200, probed 2026-08-13) serves 10 models. The registry declared 7, four of which upstream does not serve at all: openai/gpt-5, openai/gpt-5.2-codex, Alibaba/qwen3.5, Alibaba/qwen3-vl:235b. Seven live models were missing: openai/gpt-5.4, openai/gpt-5.5, openai/gpt-5.6-sol, anthropic/claude-opus-4.7, anthropic/claude-opus-4.8, anthropic/claude-sonnet-5, anthropic/claude-opus-5. The four phantom ids are selectable in the dashboard and can only ever fail upstream; the seven real ones are unreachable through the static catalog. On context windows: the /v1/models response carries only id/object/created/ owned_by, so upstream publishes no window at all. The models added here therefore declare no contextLength and inherit the entry's existing defaultContextLength (128000) instead of a fabricated number. The two pre-existing contextLength values are left untouched for the same reason - this sweep neither confirms nor refutes them, and rewriting them would be guesswork in the other direction. * chore(changelog): name the fragment after the real PR number * chore(changelog): substitute the PRNUM placeholder in the fragment body --------- Co-authored-by: adevwithpurpose <adevwithpurpose@users.noreply.github.com>
This commit is contained in:
1
changelog.d/fixes/10233-freeaiapikey-endpoint-moved.md
Normal file
1
changelog.d/fixes/10233-freeaiapikey-endpoint-moved.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(providers):** FreeAIAPIKey now targets `api.freeaiapikey.com`, the host upstream names in its `410 endpoint_moved` response — every request through the provider was failing — and its catalog is resynced to the 10 models the live `/v1/models` actually serves ([#10233](https://github.com/diegosouzapw/OmniRoute/pull/10233))
|
||||
@@ -5,34 +5,39 @@ export const freeaiapikeyProvider: RegistryEntry = {
|
||||
alias: "faik",
|
||||
format: "openai",
|
||||
executor: "default",
|
||||
baseUrl: "https://freeaiapikey.com/v1/chat/completions",
|
||||
modelsUrl: "https://freeaiapikey.com/v1/models",
|
||||
// 2026-08-13: the apex host answers 410 `endpoint_moved` on every /v1 route and
|
||||
// names its own replacement — "Please update your base_url to
|
||||
// https://api.freeaiapikey.com/v1". The api. host serves /v1/models (200) and
|
||||
// /v1/chat/completions (405 on GET, i.e. POST-only as expected).
|
||||
baseUrl: "https://api.freeaiapikey.com/v1/chat/completions",
|
||||
modelsUrl: "https://api.freeaiapikey.com/v1/models",
|
||||
authType: "apikey",
|
||||
authHeader: "bearer",
|
||||
defaultContextLength: 128000,
|
||||
// Catalog synced 2026-08-13 against GET https://api.freeaiapikey.com/v1/models (200).
|
||||
// That response carries only id/object/created/owned_by — upstream publishes no
|
||||
// context window — so models added from it declare no contextLength and inherit
|
||||
// `defaultContextLength` above rather than an invented figure. The two pre-existing
|
||||
// contextLength values are left exactly as they were: nothing in this sweep confirms
|
||||
// or refutes them, and rewriting them would be the same guesswork in reverse.
|
||||
models: [
|
||||
{ id: "openai/gpt-5", name: "GPT-5 (via FreeAIAPIKey)", contextLength: 400000 },
|
||||
{ id: "openai/gpt-4o", name: "GPT-4o (via FreeAIAPIKey)" },
|
||||
{ id: "openai/gpt-5.2-codex", name: "GPT-5.2 Codex (via FreeAIAPIKey)" },
|
||||
{ id: "openai/gpt-5.4", name: "GPT-5.4 (via FreeAIAPIKey)" },
|
||||
{ id: "openai/gpt-5.5", name: "GPT-5.5 (via FreeAIAPIKey)" },
|
||||
{ id: "openai/gpt-5.6-sol", name: "GPT-5.6 Sol (via FreeAIAPIKey)" },
|
||||
{
|
||||
id: "anthropic/claude-opus-4.6",
|
||||
name: "Claude Opus 4.6 (via FreeAIAPIKey)",
|
||||
contextLength: 1000000,
|
||||
},
|
||||
{ id: "anthropic/claude-opus-4.7", name: "Claude Opus 4.7 (via FreeAIAPIKey)" },
|
||||
{ id: "anthropic/claude-opus-4.8", name: "Claude Opus 4.8 (via FreeAIAPIKey)" },
|
||||
{ id: "anthropic/claude-opus-5", name: "Claude Opus 5 (via FreeAIAPIKey)" },
|
||||
{
|
||||
id: "anthropic/claude-sonnet-4.6",
|
||||
name: "Claude Sonnet 4.6 (via FreeAIAPIKey)",
|
||||
contextLength: 1000000,
|
||||
},
|
||||
{
|
||||
id: "Alibaba/qwen3.5",
|
||||
name: "Qwen 3.5 (via FreeAIAPIKey)",
|
||||
contextLength: 128000,
|
||||
},
|
||||
{
|
||||
id: "Alibaba/qwen3-vl:235b",
|
||||
name: "Qwen 3 VL 235B (via FreeAIAPIKey)",
|
||||
contextLength: 128000,
|
||||
},
|
||||
{ id: "anthropic/claude-sonnet-5", name: "Claude Sonnet 5 (via FreeAIAPIKey)" },
|
||||
],
|
||||
};
|
||||
|
||||
@@ -2082,8 +2082,8 @@
|
||||
}
|
||||
},
|
||||
"url": {
|
||||
"nonStream": "https://freeaiapikey.com/v1/chat/completions",
|
||||
"stream": "https://freeaiapikey.com/v1/chat/completions"
|
||||
"nonStream": "https://api.freeaiapikey.com/v1/chat/completions",
|
||||
"stream": "https://api.freeaiapikey.com/v1/chat/completions"
|
||||
}
|
||||
},
|
||||
"freeinference": {
|
||||
|
||||
104
tests/unit/freeaiapikey-endpoint-moved.test.ts
Normal file
104
tests/unit/freeaiapikey-endpoint-moved.test.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { test } from "node:test";
|
||||
import { freeaiapikeyProvider } from "../../open-sse/config/providers/registry/freeaiapikey/index.ts";
|
||||
|
||||
/**
|
||||
* FreeAIAPIKey retired its apex-host API and moved it to a dedicated `api.` host.
|
||||
*
|
||||
* Live probe (2026-08-13), each paired with a control call so a network fault
|
||||
* cannot be mistaken for an upstream verdict:
|
||||
*
|
||||
* GET https://freeaiapikey.com/v1/models → 410
|
||||
* GET https://freeaiapikey.com/v1/chat/completions → 410
|
||||
* GET https://api.freeaiapikey.com/v1/models → 200
|
||||
* GET https://api.freeaiapikey.com/v1/chat/completions → 405 (POST-only endpoint)
|
||||
* GET https://api.openai.com/v1/models → 401 (control: reachable)
|
||||
* GET https://<nonexistent-domain>/v1/models → 000 (control: unreachable)
|
||||
*
|
||||
* The 410 body names its own replacement, so the target host is upstream's own
|
||||
* instruction rather than an inference:
|
||||
*
|
||||
* {"error":{"message":"This API endpoint has moved. Please update your base_url
|
||||
* to https://api.freeaiapikey.com/v1 — the old endpoint on freeaiapikey.com no
|
||||
* longer works.","type":"endpoint_moved","code":"endpoint_moved"}}
|
||||
*
|
||||
* Provider entry added in #2708.
|
||||
*/
|
||||
const LIVE_API_BASE = "https://api.freeaiapikey.com/v1";
|
||||
|
||||
/**
|
||||
* Every model id returned by GET https://api.freeaiapikey.com/v1/models on 2026-08-13.
|
||||
* The response carries only id/object/created/owned_by — upstream publishes no context
|
||||
* window, so models catalogued from it declare no contextLength and inherit the entry's
|
||||
* defaultContextLength rather than an invented number.
|
||||
*/
|
||||
const LIVE_MODEL_IDS = [
|
||||
"openai/gpt-4o",
|
||||
"openai/gpt-5.4",
|
||||
"openai/gpt-5.5",
|
||||
"openai/gpt-5.6-sol",
|
||||
"anthropic/claude-opus-4.6",
|
||||
"anthropic/claude-opus-4.7",
|
||||
"anthropic/claude-opus-4.8",
|
||||
"anthropic/claude-sonnet-4.6",
|
||||
"anthropic/claude-sonnet-5",
|
||||
"anthropic/claude-opus-5",
|
||||
];
|
||||
|
||||
test("freeaiapikey targets the live api. host (upstream 410 endpoint_moved)", () => {
|
||||
assert.equal(
|
||||
freeaiapikeyProvider.baseUrl,
|
||||
`${LIVE_API_BASE}/chat/completions`,
|
||||
"baseUrl must point at the host named in upstream's 410 endpoint_moved body"
|
||||
);
|
||||
assert.equal(
|
||||
freeaiapikeyProvider.modelsUrl,
|
||||
`${LIVE_API_BASE}/models`,
|
||||
"modelsUrl must point at the host named in upstream's 410 endpoint_moved body"
|
||||
);
|
||||
});
|
||||
|
||||
test("freeaiapikey keeps no endpoint on the retired freeaiapikey.com apex host", () => {
|
||||
for (const [field, url] of [
|
||||
["baseUrl", freeaiapikeyProvider.baseUrl],
|
||||
["modelsUrl", freeaiapikeyProvider.modelsUrl],
|
||||
] as const) {
|
||||
assert.ok(url, `${field} must be set`);
|
||||
assert.doesNotMatch(
|
||||
url,
|
||||
/^https:\/\/freeaiapikey\.com\//,
|
||||
`${field} still targets the apex host, which answers 410 endpoint_moved`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test("freeaiapikey catalogs exactly the models upstream serves", () => {
|
||||
const declared = freeaiapikeyProvider.models.map((model) => model.id);
|
||||
assert.deepEqual(
|
||||
[...declared].sort(),
|
||||
[...LIVE_MODEL_IDS].sort(),
|
||||
"registry catalog must match the ids returned by the live /v1/models"
|
||||
);
|
||||
});
|
||||
|
||||
test("freeaiapikey declares no duplicate model ids", () => {
|
||||
const declared = freeaiapikeyProvider.models.map((model) => model.id);
|
||||
assert.equal(new Set(declared).size, declared.length, "model ids must be unique");
|
||||
});
|
||||
|
||||
test("freeaiapikey gives every catalogued model a display name", () => {
|
||||
for (const model of freeaiapikeyProvider.models) {
|
||||
assert.equal(typeof model.name, "string", `${model.id} must declare a name`);
|
||||
assert.ok(model.name.length > 0, `${model.id} must declare a non-empty name`);
|
||||
}
|
||||
});
|
||||
|
||||
test("freeaiapikey keeps a provider-wide default for unpublished context windows", () => {
|
||||
// Upstream reports no context windows, so the models added from its catalog carry
|
||||
// no contextLength of their own; this default is what they fall back to.
|
||||
assert.equal(
|
||||
typeof freeaiapikeyProvider.defaultContextLength,
|
||||
"number",
|
||||
"entry must keep a defaultContextLength for models with no upstream-published window"
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user