mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-14 02:42:24 +03:00
docs(providers): register providers removed at their operator's request and guard against reintroduction (#12478)
Adds docs/reference/REMOVED_PROVIDERS.md (policy + register: puter #10210, the keyless provider removed in #12440), links it from the docs index and AGENTS.md's provider checklist, and adds a regression test that fails if any registered id, alias or domain shows up again in the provider catalogs, the executor map or the registry/executor sources. Co-authored-by: Markus Hartung <diegosouzapw@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
84b345d9c0
commit
bf0d902dfc
@@ -343,6 +343,7 @@ Documentation must describe verified behavior, not plausible behavior.
|
||||
|
||||
### Adding a New Provider
|
||||
|
||||
0. Check `docs/reference/REMOVED_PROVIDERS.md` first — providers removed at their operator's request must never be reintroduced (guarded by `tests/unit/removed-providers-blocklist.test.ts`)
|
||||
1. Register in `src/shared/constants/providers.ts` (Zod-validated at load)
|
||||
2. Add executor in `open-sse/executors/` if custom logic needed (extend `BaseExecutor`)
|
||||
3. Add translator in `open-sse/translator/` if non-OpenAI format
|
||||
|
||||
@@ -79,6 +79,7 @@ Lookup material — API surface, environment variables, CLI flags, provider cata
|
||||
|
||||
- [API_REFERENCE.md](reference/API_REFERENCE.md) — REST API endpoints and shapes.
|
||||
- [PROVIDER_REFERENCE.md](reference/PROVIDER_REFERENCE.md) — auto-generated provider catalog (do not edit by hand).
|
||||
- [REMOVED_PROVIDERS.md](reference/REMOVED_PROVIDERS.md) — providers removed at their operator's request; never reintroduce without written permission.
|
||||
- [PROVIDER_PLUGIN_MANIFEST.md](reference/PROVIDER_PLUGIN_MANIFEST.md) — sidecar-safe provider plugin contract for Bifrost and CLIProxyAPI migration.
|
||||
- [openapi.yaml](openapi.yaml) — OpenAPI spec for the public API.
|
||||
- [ENVIRONMENT.md](reference/ENVIRONMENT.md) — environment variables reference.
|
||||
|
||||
60
docs/reference/REMOVED_PROVIDERS.md
Normal file
60
docs/reference/REMOVED_PROVIDERS.md
Normal file
@@ -0,0 +1,60 @@
|
||||
# Providers removed at their operator's request
|
||||
|
||||
Some services were integrated into OmniRoute and later removed because the people who run
|
||||
them asked for it. This page is the durable record of those removals. Its only purpose is to
|
||||
keep them from coming back by accident: a contributor who finds an old fork, a cached npm
|
||||
tarball, an archived issue or a "restore provider X" request needs one place that says **do
|
||||
not reintroduce**.
|
||||
|
||||
This page is **not** a list of dead or discontinued services. Those are tracked in
|
||||
[`FREE_TIERS.md`](FREE_TIERS.md) ("Removed / no free tier") and can come back if the service
|
||||
does. The entries below can only come back with written permission from the operator named in
|
||||
the request, and that permission must be linked from the entry.
|
||||
|
||||
## Policy
|
||||
|
||||
1. **A takedown request from a service operator is honored, not negotiated.** OmniRoute is
|
||||
not affiliated with any upstream service. When the operator of a service asks for the
|
||||
integration to go, it goes, whether the integration used an official API or not.
|
||||
2. **"Removed" means every surface OmniRoute controls.** Executor, registry entry, provider
|
||||
id and alias, model list, endpoints, environment variables, icon, dashboard cards, the
|
||||
generated provider reference, `FREE_TIERS.md`, the environment reference, README counts,
|
||||
`llm.txt` mirrors, dedicated tests and golden snapshots, code comments, CHANGELOG bullets
|
||||
(with a ledgered reconciliation, see `config/release/changelog-reconciliations.json`),
|
||||
GitHub Releases notes, the wiki, and the GitHub issues, discussions and pull requests whose
|
||||
subject was that provider (issues and discussions deleted; pull requests retitled, their
|
||||
description replaced and the thread locked, because GitHub cannot delete pull requests).
|
||||
3. **Never reintroduce an entry on this page without written permission.** That includes
|
||||
adding the id or alias back to any provider catalog, adding the domains to an executor,
|
||||
accepting a contributor PR that "restores" it, adding it to the free-model catalog, or
|
||||
documenting a manual way to reach it through OmniRoute. Close such PRs and issues with a
|
||||
link to this page.
|
||||
4. **Keep the entry minimal.** Record only what a reviewer needs to recognize a
|
||||
reintroduction: identifiers, domains, dates and the pull request that did the removal.
|
||||
Do not describe how the integration worked.
|
||||
5. **The regression guard is `tests/unit/removed-providers-blocklist.test.ts`.** It fails when
|
||||
any identifier or domain below shows up again in the provider catalogs, the executor map or
|
||||
the provider registry sources. Add the new identifiers to that test in the same PR that
|
||||
adds a row here.
|
||||
|
||||
## Register
|
||||
|
||||
| Removed on | Provider id | Alias | Domains | Requested by | Removal PR | Notes |
|
||||
| ---------- | ----------- | ------ | --------------------------------------- | ------------------------------------ | -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
|
||||
| 2026-08-12 | `puter` | `pu` | `puter.com` | Puter's owner (Nariman Jelveh) | [#10210](https://github.com/diegosouzapw/OmniRoute/pull/10210) | API-key provider. Migration `152_remove_puter_provider.sql` cleans stored config. |
|
||||
| 2026-09-02 | `theoldllm` | `tllm` | `theoldllm.com`, `theoldllm.vercel.app` | The service operator (support email) | [#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440) | Keyless provider. Written request received 2026-08-30. Dedicated issues and discussion deleted, PRs retitled. |
|
||||
|
||||
## Adding an entry
|
||||
|
||||
When a new takedown request arrives:
|
||||
|
||||
1. Confirm the request comes from the operator of the service (their support address or a
|
||||
domain they control), and keep the message privately.
|
||||
2. Remove the integration following the checklist in policy item 2. Use
|
||||
[#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440) as the reference for a
|
||||
keyless provider and [#10210](https://github.com/diegosouzapw/OmniRoute/pull/10210) for an
|
||||
API-key provider with stored connections (add a migration).
|
||||
3. Add one row to the table above and the identifiers to
|
||||
`tests/unit/removed-providers-blocklist.test.ts`, in the same PR.
|
||||
4. Reply to the operator once the PR is merged, listing what was removed and what OmniRoute
|
||||
cannot change (already-published npm and Docker versions, git history, third-party forks).
|
||||
@@ -8,6 +8,7 @@
|
||||
"FREE_TIERS",
|
||||
"FREE_PROXIES_API",
|
||||
"PROVIDER_REFERENCE",
|
||||
"REMOVED_PROVIDERS",
|
||||
"PROVIDER_PLUGIN_MANIFEST",
|
||||
"RELAY_BACKEND_STRATEGY",
|
||||
"RELAY_TROUBLESHOOTING"
|
||||
|
||||
107
tests/unit/removed-providers-blocklist.test.ts
Normal file
107
tests/unit/removed-providers-blocklist.test.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* Regression guard for docs/reference/REMOVED_PROVIDERS.md.
|
||||
*
|
||||
* Providers removed at their operator's request must never come back: not in the
|
||||
* provider catalogs, not in the executor map, not in the registry sources and not
|
||||
* as an upstream domain in any executor. Keep this list in sync with the table in
|
||||
* the doc; add the identifiers of a new takedown here in the same PR.
|
||||
*/
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
const { REGISTRY } = await import("../../open-sse/config/providerRegistry.ts");
|
||||
const { getProviderById, getProviderByAlias } =
|
||||
await import("../../src/shared/constants/providers.ts");
|
||||
const { hasSpecializedExecutor } = await import("../../open-sse/executors/index.ts");
|
||||
const { FREE_MODEL_BUDGETS } = await import("../../open-sse/config/freeModelCatalog.data.ts");
|
||||
|
||||
interface RemovedProvider {
|
||||
id: string;
|
||||
alias: string;
|
||||
domains: string[];
|
||||
removalPr: number;
|
||||
}
|
||||
|
||||
export const REMOVED_PROVIDERS: readonly RemovedProvider[] = [
|
||||
{ id: "puter", alias: "pu", domains: ["puter.com"], removalPr: 10210 },
|
||||
{
|
||||
id: "theoldllm",
|
||||
alias: "tllm",
|
||||
domains: ["theoldllm.com", "theoldllm.vercel.app"],
|
||||
removalPr: 12440,
|
||||
},
|
||||
];
|
||||
|
||||
// Source trees where a reintroduction would land. Scanned for ids, aliases and domains.
|
||||
const SCANNED_DIRS = [
|
||||
"open-sse/config/providers",
|
||||
"open-sse/executors",
|
||||
"src/shared/constants/providers",
|
||||
];
|
||||
|
||||
function walk(dir: string, out: string[] = []): string[] {
|
||||
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||
const full = path.join(dir, entry.name);
|
||||
if (entry.isDirectory()) walk(full, out);
|
||||
else if (/\.(ts|tsx|mts|js|mjs|json)$/.test(entry.name)) out.push(full);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
const ROOT = process.cwd();
|
||||
const scannedFiles = SCANNED_DIRS.flatMap((dir) => walk(path.join(ROOT, dir)));
|
||||
|
||||
for (const removed of REMOVED_PROVIDERS) {
|
||||
test(`removed provider "${removed.id}" (PR #${removed.removalPr}) stays out of the chat registry`, () => {
|
||||
assert.equal(REGISTRY[removed.id], undefined, `${removed.id} must not be in REGISTRY`);
|
||||
assert.equal(REGISTRY[removed.alias], undefined, `${removed.alias} must not be in REGISTRY`);
|
||||
});
|
||||
|
||||
test(`removed provider "${removed.id}" stays out of the provider catalogs`, () => {
|
||||
assert.equal(getProviderById(removed.id), undefined, `${removed.id} must not be a provider`);
|
||||
assert.equal(
|
||||
getProviderByAlias(removed.alias),
|
||||
null,
|
||||
`alias ${removed.alias} must not be reused by any provider`
|
||||
);
|
||||
});
|
||||
|
||||
test(`removed provider "${removed.id}" has no executor (id or alias)`, () => {
|
||||
assert.equal(hasSpecializedExecutor(removed.id), false);
|
||||
assert.equal(hasSpecializedExecutor(removed.alias), false);
|
||||
});
|
||||
|
||||
test(`removed provider "${removed.id}" has no free-model catalog entries`, () => {
|
||||
assert.deepEqual(
|
||||
FREE_MODEL_BUDGETS.filter((b) => b.provider === removed.id),
|
||||
[],
|
||||
`${removed.id} must not appear in FREE_MODEL_BUDGETS`
|
||||
);
|
||||
});
|
||||
|
||||
test(`removed provider "${removed.id}" identifiers and domains are absent from registry/executor sources`, () => {
|
||||
const needles = [`"${removed.id}"`, `"${removed.alias}"`, ...removed.domains];
|
||||
const offenders: string[] = [];
|
||||
for (const file of scannedFiles) {
|
||||
const text = fs.readFileSync(file, "utf8");
|
||||
for (const needle of needles) {
|
||||
if (text.includes(needle)) offenders.push(`${path.relative(ROOT, file)} :: ${needle}`);
|
||||
}
|
||||
}
|
||||
assert.deepEqual(
|
||||
offenders,
|
||||
[],
|
||||
`reintroduction of "${removed.id}" detected — see docs/reference/REMOVED_PROVIDERS.md`
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
test("the REMOVED_PROVIDERS doc lists every guarded id", () => {
|
||||
const doc = fs.readFileSync(path.join(ROOT, "docs/reference/REMOVED_PROVIDERS.md"), "utf8");
|
||||
for (const removed of REMOVED_PROVIDERS) {
|
||||
assert.ok(doc.includes(`\`${removed.id}\``), `${removed.id} must have a row in the doc`);
|
||||
assert.ok(doc.includes(`#${removed.removalPr}`), `PR #${removed.removalPr} must be linked`);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user