mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-02 21:02:14 +03:00
Compare commits
7 Commits
fix/v3851-
...
docs/remov
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
abf8938fc2 | ||
|
|
53b037051b | ||
|
|
c420a51df6 | ||
|
|
d6412532c4 | ||
|
|
a25ac4d979 | ||
|
|
089e70cbc5 | ||
|
|
7c119dd7ed |
@@ -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
|
||||
|
||||
1
changelog.d/fixes/provider-assets-provenance-final.md
Normal file
1
changelog.d/fixes/provider-assets-provenance-final.md
Normal file
@@ -0,0 +1 @@
|
||||
- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally.
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"_rebaseline_2026_09_02_12412_grok_web_prettier": "PR #12412 (repository Prettier style applied to tests/unit/grok-web.test.ts): the reformat expands the file +277 lines (2436 -> 2713) with an identical parsed AST — no production code, no assertion changes. Cap set to 2985 rather than the exact 2713 on the operator's instruction (2026-09-02): ~10% headroom so routine additions to this suite do not re-trip the gate on formatting alone. Previous cap 2437. This is a deliberate exception to the down-only ratchet for one reformatted test file; every other entry keeps the #12411 tightening.",
|
||||
"_rebaseline_2026_09_02_v3851_merged_growth_basereds": "Base-red drain: the 2026-09-02 merge waves (#12359-#12404, #11461, #11513, #12423) each grew a frozen file at an existing chokepoint, but the rebaseline was computed in the throwaway combined validation worktree and never reached any PR branch, so the growth landed while the caps did not and check-file-size went red on the release tip. Recorded here against the merged state: src/app/api/providers/[id]/models/route.ts 2429->2432 (#12389 gemini-business listing on top of #11461's 2429); src/app/api/v1/models/catalog.ts 2066->2075 (#12381 self-aliased canonical rows + #12403 NUL escape); src/lib/db/core.ts 1740->1745 (#12394 busy_timeout ordering + probe classification); src/sse/handlers/chat.ts 2375->2384 (#12360 breaker result classification + #12365 shadowed-node error); src/sse/services/auth.ts 3420->3427 (#12375 backoffLevel tie-break); open-sse/handlers/imageGeneration.ts 3255->3259 (#11513 uc-image branch + #12423 uc-image id scoping); open-sse/utils/proxyFetch.ts 1261->1271 (#12380 hasAmbientProxyContext()); tests/unit/image-generation-handler.test.ts 2110->2133 (#12362 regression coverage); tests/unit/sse-auth.test.ts 1697->1729 (#12375 regression coverage). No cap is raised beyond the merged LOC; every other entry is untouched.",
|
||||
"_rebaseline_2026_09_02_11513_uc_provider": "PR #11513 (arminanton, feat/uc-native-standalone) own growth: open-sse/handlers/imageGeneration.ts 3243->3255 (+12) — the uc-image format branch for the UC persona provider's image surface. Additive at the existing per-format chokepoint, same rationale as _rebaseline_2026_09_02_11461_maxai_tls_profile.",
|
||||
"_rebaseline_2026_09_02_11461_maxai_tls_profile": "PR #11461 (arminanton, feat/maxai-provider) own growth, three files at existing per-provider chokepoints: open-sse/utils/proxyFetch.ts 1241->1261 (+20, the TLS_PROVIDER_PROFILE map giving MaxAI a Windows/firefox_150 impersonation profile instead of the tlsClient chrome_124/macos default); open-sse/handlers/imageGeneration.ts 3231->3243 (+12, the maxai-image format branch); src/app/api/providers/[id]/models/route.ts 2381->2429 (+48, live model listing via maxaiModels). Additive data, same no-split rationale as _rebaseline_2026_08_20_10531_freebuff_provider.",
|
||||
@@ -210,7 +211,7 @@
|
||||
"tests/unit/db-migration-runner.test.ts": 1509,
|
||||
"tests/unit/executor-codex.test.ts": 1465,
|
||||
"tests/unit/executor-default-base.test.ts": 1632,
|
||||
"tests/unit/grok-web.test.ts": 2437,
|
||||
"tests/unit/grok-web.test.ts": 2985,
|
||||
"tests/unit/image-generation-handler.test.ts": 2133,
|
||||
"tests/unit/models-catalog-route.test.ts": 1652,
|
||||
"tests/unit/perplexity-web.test.ts": 1384,
|
||||
|
||||
@@ -1,9 +1 @@
|
||||
{
|
||||
"src/lib/guardrails/videoBridgeHelpers.ts": {
|
||||
"TS2488": 2,
|
||||
"TS2365": 3,
|
||||
"TS2322": 2,
|
||||
"TS2345": 2
|
||||
},
|
||||
"_relax_velocity_2026_08_30": "per-file TS diagnostic counts raised by 20% (5 → 9); velocity phase, see quality-baseline.json _policy."
|
||||
}
|
||||
{}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{"recordType": "manifest", "schemaVersion": 1, "expectedAssetCount": 142, "auditedCommit": "7d57d9f4a15931aa33a9ab968e4e5d76a205e27c", "auditedAt": "2026-08-28", "scope": "Every regular file directly under public/providers at the audited commit.", "statusSemantics": {"proven": "Immutable source plus byte-exact or SVG path-data match.", "probable": "Repository evidence suggests provenance, but no immutable upstream match is proven.", "unresolved": "No sufficient immutable provenance evidence is recorded."}, "enforcement": "All physical files, hashes, magic MIME values, statuses, and duplicate aliases are blocking. Probable and unresolved statuses are recorded but non-blocking in schema version 1.", "legalScope": "Provenance records source matching only; it does not establish copyright or trademark clearance."}
|
||||
{"recordType": "manifest", "schemaVersion": 1, "expectedAssetCount": 141, "auditedCommit": "ccb024cfa9a7612fa65b1f1795740572369d58f5", "auditedAt": "2026-09-02", "scope": "Every regular file directly under public/providers at the audited commit.", "statusSemantics": {"proven": "Immutable source plus byte-exact or SVG path-data match.", "probable": "Repository evidence suggests provenance, but no immutable upstream match is proven.", "unresolved": "No sufficient immutable provenance evidence is recorded."}, "enforcement": "All physical files, hashes, magic MIME values, statuses, and duplicate aliases are blocking. Probable and unresolved statuses are recorded but non-blocking in schema version 1.", "legalScope": "Provenance records source matching only; it does not establish copyright or trademark clearance."}
|
||||
{"recordType": "asset", "path": "public/providers/360ai.svg", "mediaType": "image/svg+xml", "sha256": "59366fe04a4336518b8277b430f4a464a91e7ec944c9cf6f40a945c74002386d", "provenanceStatus": "proven", "source": {"kind": "npm", "url": "https://registry.npmjs.org/@lobehub/icons/-/icons-5.10.0.tgz", "ref": "5.10.0", "path": "package/es/Ai360/components/Color.js", "integrity": "sha512-CIpjkISCLRK7haDtSugGFd0o3odaJts8ewJOkUiEFtns3xvsqbl8i24eowBnjw+yMDQVQyNONlhqTD58YC6Ljg==", "packageShasum": "add1baced073a60157d39c7820b8d5c1928a1054", "match": "svg-path-data", "matchDetail": "All 5/5 local SVG path d values match the pinned Color component."}, "upstreamLicenseClaim": {"value": "MIT", "assertedBy": "@lobehub/icons@5.10.0 package/LICENSE", "evidence": "https://registry.npmjs.org/@lobehub/icons/-/icons-5.10.0.tgz#package/LICENSE", "independentlyVerified": true, "scope": "Pinned package distribution only; no trademark clearance."}, "trademarkClearance": null, "evidenceNote": "All local SVG path data matches the pinned LobeHub Color component. This proves source provenance only, not trademark clearance."}
|
||||
{"recordType": "asset", "path": "public/providers/alibaba.svg", "mediaType": "image/svg+xml", "sha256": "1cd1e7be5108d1e847508dc9e40591fb6eb29aac20bbf7f4c56c6f082359b323", "provenanceStatus": "proven", "source": {"kind": "git", "url": "https://github.com/GLINCKER/thesvg", "ref": "7870bc1c5f657d9accbb7f96cc457b8dd3363ee8", "path": "public/icons/alibaba/default.svg", "integrity": "sha256:1cd1e7be5108d1e847508dc9e40591fb6eb29aac20bbf7f4c56c6f082359b323", "match": "byte-exact"}, "upstreamLicenseClaim": {"value": "MIT", "assertedBy": "theSVG pinned registry", "evidence": "https://github.com/GLINCKER/thesvg/blob/7870bc1c5f657d9accbb7f96cc457b8dd3363ee8/src/data/icons.json", "independentlyVerified": false, "scope": "Upstream SVG copyright claim only; no trademark clearance.", "upstreamUrl": "https://alibaba.com"}, "trademarkClearance": null, "evidenceNote": "Local bytes are byte-identical to the pinned theSVG source. This proves source provenance only, not copyright or trademark clearance."}
|
||||
{"recordType": "asset", "path": "public/providers/anthropic.svg", "mediaType": "image/svg+xml", "sha256": "7fea3100bfc2a9480e181fc615d4791cab014f54674b83953785abb86dc293f0", "provenanceStatus": "proven", "source": {"kind": "git", "url": "https://github.com/GLINCKER/thesvg", "ref": "7870bc1c5f657d9accbb7f96cc457b8dd3363ee8", "path": "public/icons/anthropic/default.svg", "integrity": "sha256:7fea3100bfc2a9480e181fc615d4791cab014f54674b83953785abb86dc293f0", "match": "byte-exact"}, "upstreamLicenseClaim": {"value": "CC0-1.0", "assertedBy": "theSVG pinned registry", "evidence": "https://github.com/GLINCKER/thesvg/blob/7870bc1c5f657d9accbb7f96cc457b8dd3363ee8/src/data/icons.json", "independentlyVerified": false, "scope": "Upstream SVG copyright claim only; no trademark clearance.", "upstreamUrl": "https://www.anthropic.com/"}, "trademarkClearance": null, "evidenceNote": "Local bytes are byte-identical to the pinned theSVG source. This proves source provenance only, not copyright or trademark clearance."}
|
||||
@@ -80,7 +80,6 @@
|
||||
{"recordType": "asset", "path": "public/providers/moonshot.svg", "mediaType": "image/svg+xml", "sha256": "a6ac95d972fdb044cd4155b0f75d9c1c816348868c810f63c7e5c8840c9e3e12", "provenanceStatus": "proven", "source": {"kind": "git", "url": "https://github.com/GLINCKER/thesvg", "ref": "7870bc1c5f657d9accbb7f96cc457b8dd3363ee8", "path": "public/icons/moonshot/default.svg", "integrity": "sha256:a6ac95d972fdb044cd4155b0f75d9c1c816348868c810f63c7e5c8840c9e3e12", "match": "byte-exact"}, "upstreamLicenseClaim": {"value": "MIT", "assertedBy": "theSVG pinned registry", "evidence": "https://github.com/GLINCKER/thesvg/blob/7870bc1c5f657d9accbb7f96cc457b8dd3363ee8/src/data/icons.json", "independentlyVerified": false, "scope": "Upstream SVG copyright claim only; no trademark clearance.", "upstreamUrl": "https://moonshot.cn"}, "trademarkClearance": null, "evidenceNote": "Local bytes are byte-identical to the pinned theSVG source. This proves source provenance only, not copyright or trademark clearance."}
|
||||
{"recordType": "asset", "path": "public/providers/morph.svg", "mediaType": "image/svg+xml", "sha256": "0fdb479e13c5d5de15aa89d1f87c8d55f8f8a56d33fc5dee8c566d2e0dbd5b96", "provenanceStatus": "proven", "source": {"kind": "git", "url": "https://github.com/GLINCKER/thesvg", "ref": "7870bc1c5f657d9accbb7f96cc457b8dd3363ee8", "path": "public/icons/morph/default.svg", "integrity": "sha256:0fdb479e13c5d5de15aa89d1f87c8d55f8f8a56d33fc5dee8c566d2e0dbd5b96", "match": "byte-exact"}, "upstreamLicenseClaim": {"value": "MIT", "assertedBy": "theSVG pinned registry", "evidence": "https://github.com/GLINCKER/thesvg/blob/7870bc1c5f657d9accbb7f96cc457b8dd3363ee8/src/data/icons.json", "independentlyVerified": false, "scope": "Upstream SVG copyright claim only; no trademark clearance.", "upstreamUrl": "https://morphllm.com"}, "trademarkClearance": null, "evidenceNote": "Local bytes are byte-identical to the pinned theSVG source. This proves source provenance only, not copyright or trademark clearance."}
|
||||
{"recordType": "asset", "path": "public/providers/nebius.svg", "mediaType": "image/svg+xml", "sha256": "fb190b4efb1d143442ef6c5eb0258801fc27b7316e88216c59a0f4b58b8b0281", "provenanceStatus": "proven", "source": {"kind": "git", "url": "https://github.com/GLINCKER/thesvg", "ref": "7870bc1c5f657d9accbb7f96cc457b8dd3363ee8", "path": "public/icons/nebius/default.svg", "integrity": "sha256:fb190b4efb1d143442ef6c5eb0258801fc27b7316e88216c59a0f4b58b8b0281", "match": "byte-exact"}, "upstreamLicenseClaim": {"value": "MIT", "assertedBy": "theSVG pinned registry", "evidence": "https://github.com/GLINCKER/thesvg/blob/7870bc1c5f657d9accbb7f96cc457b8dd3363ee8/src/data/icons.json", "independentlyVerified": false, "scope": "Upstream SVG copyright claim only; no trademark clearance.", "upstreamUrl": "https://nebius.com"}, "trademarkClearance": null, "evidenceNote": "Local bytes are byte-identical to the pinned theSVG source. This proves source provenance only, not copyright or trademark clearance."}
|
||||
{"recordType": "asset", "path": "public/providers/nimble-search.svg", "mediaType": "image/svg+xml", "sha256": "c22d214880d1cbf48aa08617fbc245b96f7372fe5602ef1382978eee522c6575", "provenanceStatus": "unresolved", "source": null, "upstreamLicenseClaim": null, "trademarkClearance": null, "evidenceNote": "Added by an unrelated already-merged provider PR (#11620/#11629); no provenance research recorded yet. Flagged unresolved pending review, per schema v1 (non-blocking)."}
|
||||
{"recordType": "asset", "path": "public/providers/nomic.svg", "mediaType": "image/svg+xml", "sha256": "73cc513c9d5f460ec8f00a097f3fabaa54c0e1f824944412e9a4461c0620fba6", "provenanceStatus": "probable", "source": null, "upstreamLicenseClaim": null, "trademarkClearance": null, "evidenceNote": "Repository history shows a shared generic initial-badge pattern, but no immutable authorship or license evidence is recorded."}
|
||||
{"recordType": "asset", "path": "public/providers/novita.svg", "mediaType": "image/svg+xml", "sha256": "ab99ef3113a12e64ef8b44eda132094017359ede5bdb33e830ed855b69520612", "provenanceStatus": "proven", "source": {"kind": "git", "url": "https://github.com/GLINCKER/thesvg", "ref": "7870bc1c5f657d9accbb7f96cc457b8dd3363ee8", "path": "public/icons/novita/default.svg", "integrity": "sha256:ab99ef3113a12e64ef8b44eda132094017359ede5bdb33e830ed855b69520612", "match": "byte-exact"}, "upstreamLicenseClaim": {"value": "MIT", "assertedBy": "theSVG pinned registry", "evidence": "https://github.com/GLINCKER/thesvg/blob/7870bc1c5f657d9accbb7f96cc457b8dd3363ee8/src/data/icons.json", "independentlyVerified": false, "scope": "Upstream SVG copyright claim only; no trademark clearance.", "upstreamUrl": "https://novita.ai/"}, "trademarkClearance": null, "evidenceNote": "Local bytes are byte-identical to the pinned theSVG source. This proves source provenance only, not copyright or trademark clearance."}
|
||||
{"recordType": "asset", "path": "public/providers/nube.svg", "mediaType": "image/svg+xml", "sha256": "e5eff793cbc8a917e499c18365979f001010b229dd53b0802d5f29d9dfb963e1", "provenanceStatus": "probable", "source": null, "upstreamLicenseClaim": null, "trademarkClearance": null, "evidenceNote": "Repository PR #6926 describes this family as letter-in-circle placeholders, but original authorship and license were not independently proven."}
|
||||
@@ -90,7 +89,7 @@
|
||||
{"recordType": "asset", "path": "public/providers/openai.svg", "mediaType": "image/svg+xml", "sha256": "db81a8225166f02f773304ba4d8f0141343da5f43870d8b41f10bf6bc59840c8", "provenanceStatus": "proven", "source": {"kind": "git", "url": "https://github.com/GLINCKER/thesvg", "ref": "7870bc1c5f657d9accbb7f96cc457b8dd3363ee8", "path": "public/icons/openai/default.svg", "integrity": "sha256:db81a8225166f02f773304ba4d8f0141343da5f43870d8b41f10bf6bc59840c8", "match": "byte-exact"}, "upstreamLicenseClaim": {"value": "MIT", "assertedBy": "theSVG pinned registry", "evidence": "https://github.com/GLINCKER/thesvg/blob/7870bc1c5f657d9accbb7f96cc457b8dd3363ee8/src/data/icons.json", "independentlyVerified": false, "scope": "Upstream SVG copyright claim only; no trademark clearance.", "upstreamUrl": "https://openai.com/"}, "trademarkClearance": null, "evidenceNote": "Local bytes are byte-identical to the pinned theSVG source. This proves source provenance only, not copyright or trademark clearance."}
|
||||
{"recordType": "asset", "path": "public/providers/openclaw.svg", "mediaType": "image/svg+xml", "sha256": "4123c0c75dda5b28e3e0d38075514085bf546178a620776344813c08fa41277c", "provenanceStatus": "proven", "source": {"kind": "npm", "url": "https://registry.npmjs.org/@lobehub/icons/-/icons-5.10.0.tgz", "ref": "5.10.0", "path": "package/es/OpenClaw/components/Color.js", "integrity": "sha512-CIpjkISCLRK7haDtSugGFd0o3odaJts8ewJOkUiEFtns3xvsqbl8i24eowBnjw+yMDQVQyNONlhqTD58YC6Ljg==", "packageShasum": "add1baced073a60157d39c7820b8d5c1928a1054", "match": "svg-path-data", "matchDetail": "All 6/6 local SVG path d values match the pinned Color component."}, "upstreamLicenseClaim": {"value": "MIT", "assertedBy": "@lobehub/icons@5.10.0 package/LICENSE", "evidence": "https://registry.npmjs.org/@lobehub/icons/-/icons-5.10.0.tgz#package/LICENSE", "independentlyVerified": true, "scope": "Pinned package distribution only; no trademark clearance."}, "trademarkClearance": null, "evidenceNote": "All local SVG path data matches the pinned LobeHub Color component. This proves source provenance only, not trademark clearance."}
|
||||
{"recordType": "asset", "path": "public/providers/openrouter.svg", "mediaType": "image/svg+xml", "sha256": "d05021526e72fddf3426eabc066924aca83da0cd66a699a3de3bac58ed2fe0a2", "provenanceStatus": "proven", "source": {"kind": "git", "url": "https://github.com/GLINCKER/thesvg", "ref": "7870bc1c5f657d9accbb7f96cc457b8dd3363ee8", "path": "public/icons/openrouter/default.svg", "integrity": "sha256:d05021526e72fddf3426eabc066924aca83da0cd66a699a3de3bac58ed2fe0a2", "match": "byte-exact"}, "upstreamLicenseClaim": {"value": "CC0-1.0", "assertedBy": "theSVG pinned registry", "evidence": "https://github.com/GLINCKER/thesvg/blob/7870bc1c5f657d9accbb7f96cc457b8dd3363ee8/src/data/icons.json", "independentlyVerified": false, "scope": "Upstream SVG copyright claim only; no trademark clearance.", "upstreamUrl": "https://openrouter.ai/"}, "trademarkClearance": null, "evidenceNote": "Local bytes are byte-identical to the pinned theSVG source. This proves source provenance only, not copyright or trademark clearance."}
|
||||
{"recordType": "asset", "path": "public/providers/opper.svg", "mediaType": "image/svg+xml", "sha256": "e45d0409e7746946f204903ad6e7da267d805b7d7534fa684eeb7b8ac5717791", "provenanceStatus": "unresolved", "source": null, "upstreamLicenseClaim": null, "trademarkClearance": null, "evidenceNote": "Added by an unrelated already-merged provider PR (#11620/#11629); no provenance research recorded yet. Flagged unresolved pending review, per schema v1 (non-blocking)."}
|
||||
{"recordType": "asset", "path": "public/providers/opper.svg", "mediaType": "image/svg+xml", "sha256": "e45d0409e7746946f204903ad6e7da267d805b7d7534fa684eeb7b8ac5717791", "provenanceStatus": "proven", "source": {"kind": "git", "url": "https://github.com/opper-ai/provider-omniroute", "ref": "9aacef7d6ae68d8d79f5aee042a25e9b646d2338", "path": "public/providers/opper.svg", "integrity": "sha256:e45d0409e7746946f204903ad6e7da267d805b7d7534fa684eeb7b8ac5717791", "match": "byte-exact"}, "upstreamLicenseClaim": {"value": "MIT", "assertedBy": "opper-ai/provider-omniroute LICENSE at the pinned commit", "evidence": "https://github.com/opper-ai/provider-omniroute/blob/9aacef7d6ae68d8d79f5aee042a25e9b646d2338/LICENSE", "independentlyVerified": true, "scope": "Pinned repository distribution only; no trademark clearance."}, "trademarkClearance": null, "evidenceNote": "Local bytes are byte-identical to the pinned official-organization repository source. The same commit carries an MIT license. This proves source and repository-license provenance only, not copyright or trademark clearance."}
|
||||
{"recordType": "asset", "path": "public/providers/orcarouter.svg", "mediaType": "image/svg+xml", "sha256": "06b36d030492901cada4c1e757b613c3ada340727d74f601fe407cfad7b529cf", "provenanceStatus": "probable", "source": null, "upstreamLicenseClaim": null, "trademarkClearance": null, "evidenceNote": "Repository PR #6926 describes this family as letter-in-circle placeholders, but original authorship and license were not independently proven."}
|
||||
{"recordType": "asset", "path": "public/providers/ovhcloud.svg", "mediaType": "image/svg+xml", "sha256": "ab65efec83d5106fa649e1f3ec5db98beb20ec6708158362844c912c34e1d31a", "provenanceStatus": "proven", "source": {"kind": "git", "url": "https://github.com/GLINCKER/thesvg", "ref": "7870bc1c5f657d9accbb7f96cc457b8dd3363ee8", "path": "public/icons/ovhcloud/default.svg", "integrity": "sha256:ab65efec83d5106fa649e1f3ec5db98beb20ec6708158362844c912c34e1d31a", "match": "byte-exact"}, "upstreamLicenseClaim": {"value": "brand-use", "assertedBy": "theSVG pinned registry", "evidence": "https://github.com/GLINCKER/thesvg/blob/7870bc1c5f657d9accbb7f96cc457b8dd3363ee8/src/data/icons.json", "independentlyVerified": false, "scope": "Upstream SVG copyright claim only; no trademark clearance.", "upstreamUrl": "https://ovhcloud.com"}, "trademarkClearance": null, "evidenceNote": "Local bytes are byte-identical to the pinned theSVG source. This proves source provenance only, not copyright or trademark clearance."}
|
||||
{"recordType": "asset", "path": "public/providers/perplexity.svg", "mediaType": "image/svg+xml", "sha256": "c7a4c847b6b3c0e8a10868d35b0b4a89727c03f8db3394060dcdb33c4b21c83b", "provenanceStatus": "probable", "source": null, "upstreamLicenseClaim": null, "trademarkClearance": null, "evidenceNote": "Repository PR #6317 and the asset structure indicate a likely source family, but no immutable upstream source or hash was proven."}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -20,6 +20,14 @@ interface SystemPromptConfig {
|
||||
prompt: string;
|
||||
}
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return value !== null && typeof value === "object" && !Array.isArray(value);
|
||||
}
|
||||
|
||||
function isSystemMessage(value: unknown): value is Record<string, unknown> {
|
||||
return isRecord(value) && (value.role === "system" || value.role === "developer");
|
||||
}
|
||||
|
||||
// Typed accessor for globalThis storage — avoids `as any` casts (#2470)
|
||||
const _store = globalThis as unknown as Record<string, SystemPromptConfig | undefined>;
|
||||
|
||||
@@ -74,45 +82,50 @@ export function getSystemPromptConfig() {
|
||||
* suffixPrompt is appended after existing system content.
|
||||
* This ensures: prefix → agent instructions → suffix (#2468).
|
||||
*
|
||||
* @param {object} body - Request body
|
||||
* @returns {object} Modified body
|
||||
* @param body - Request body
|
||||
* @returns Modified body
|
||||
*/
|
||||
export function injectSystemPrompt(body) {
|
||||
export function injectSystemPrompt<T>(body: T): T {
|
||||
const cfg = getConfig();
|
||||
if (!cfg.enabled) return body;
|
||||
const prefix = cfg.prefixPrompt || "";
|
||||
const suffix = cfg.suffixPrompt || "";
|
||||
if (!prefix && !suffix) return body;
|
||||
if (!body || typeof body !== "object") return body;
|
||||
if (!isRecord(body)) return body;
|
||||
if (body._skipSystemPrompt) return body;
|
||||
|
||||
const result = { ...body };
|
||||
const result: Record<string, unknown> = { ...body };
|
||||
|
||||
// OpenAI/Claude format (messages[])
|
||||
if (result.messages && Array.isArray(result.messages)) {
|
||||
const sysIdx = result.messages.findIndex((m) => m.role === "system" || m.role === "developer");
|
||||
result.messages = [...result.messages];
|
||||
const messages: unknown[] = result.messages;
|
||||
const sysIdx = messages.findIndex(isSystemMessage);
|
||||
const nextMessages = [...messages];
|
||||
if (sysIdx >= 0) {
|
||||
const msg = { ...result.messages[sysIdx] };
|
||||
if (Array.isArray(msg.content)) {
|
||||
const content = [...msg.content];
|
||||
if (prefix) content.unshift({ type: "text", text: prefix });
|
||||
if (suffix) content.push({ type: "text", text: suffix });
|
||||
msg.content = content;
|
||||
} else {
|
||||
let content = msg.content || "";
|
||||
if (prefix) content = prefix + "\n\n" + content;
|
||||
if (suffix) content = content + "\n\n" + suffix;
|
||||
msg.content = content;
|
||||
const existingMessage = nextMessages[sysIdx];
|
||||
if (isRecord(existingMessage)) {
|
||||
const msg = { ...existingMessage };
|
||||
if (Array.isArray(msg.content)) {
|
||||
const content: unknown[] = [...msg.content];
|
||||
if (prefix) content.unshift({ type: "text", text: prefix });
|
||||
if (suffix) content.push({ type: "text", text: suffix });
|
||||
msg.content = content;
|
||||
} else {
|
||||
let content = String(msg.content || "");
|
||||
if (prefix) content = prefix + "\n\n" + content;
|
||||
if (suffix) content = content + "\n\n" + suffix;
|
||||
msg.content = content;
|
||||
}
|
||||
nextMessages[sysIdx] = msg;
|
||||
}
|
||||
result.messages[sysIdx] = msg;
|
||||
} else {
|
||||
// No existing system message — combine both into one
|
||||
const combined = [prefix, suffix].filter(Boolean).join("\n\n");
|
||||
if (combined) {
|
||||
result.messages = [{ role: "system", content: combined }, ...result.messages];
|
||||
nextMessages.unshift({ role: "system", content: combined });
|
||||
}
|
||||
}
|
||||
result.messages = nextMessages;
|
||||
}
|
||||
|
||||
// Claude format (system field)
|
||||
@@ -123,14 +136,14 @@ export function injectSystemPrompt(body) {
|
||||
if (suffix) sys = sys + "\n\n" + suffix;
|
||||
result.system = sys;
|
||||
} else if (Array.isArray(result.system)) {
|
||||
let arr = [...result.system];
|
||||
let arr: unknown[] = [...result.system];
|
||||
if (prefix) arr = [{ type: "text", text: prefix }, ...arr];
|
||||
if (suffix) arr = [...arr, { type: "text", text: suffix }];
|
||||
result.system = arr;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return Object.assign({}, body, result);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" role="img" aria-label="Nimble">
|
||||
<rect width="24" height="24" rx="5" fill="#6D28D9" />
|
||||
<path
|
||||
d="M7 17V7h2.1l5.8 6.6V7H17v10h-2.1L9.1 10.4V17H7z"
|
||||
fill="#fff"
|
||||
/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 240 B |
@@ -19,53 +19,15 @@ import { execFileSync } from "node:child_process";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { diffAgainstBaseline, parseTscOutput } from "./typecheckBaseline.mjs";
|
||||
|
||||
export { diffAgainstBaseline, parseTscOutput } from "./typecheckBaseline.mjs";
|
||||
|
||||
const ROOT = process.cwd();
|
||||
const TSCONFIG = path.join(ROOT, "tsconfig.typecheck-api.json");
|
||||
const BASELINE_PATH = path.join(ROOT, "config/quality/api-typecheck-baseline.json");
|
||||
const UPDATE = process.argv.includes("--update");
|
||||
|
||||
const TSC_ERROR_LINE = /^(.+?)\((\d+),(\d+)\): error (TS\d+):/;
|
||||
|
||||
export function parseTscOutput(raw) {
|
||||
const counts = {};
|
||||
for (const line of String(raw).split("\n")) {
|
||||
const match = TSC_ERROR_LINE.exec(line);
|
||||
if (!match) continue;
|
||||
const [, file, , , code] = match;
|
||||
if (!counts[file]) counts[file] = {};
|
||||
counts[file][code] = (counts[file][code] || 0) + 1;
|
||||
}
|
||||
return counts;
|
||||
}
|
||||
|
||||
export function diffAgainstBaseline(live, baseline) {
|
||||
const regressions = [];
|
||||
const improvements = [];
|
||||
|
||||
for (const [file, codes] of Object.entries(live)) {
|
||||
for (const [code, liveCount] of Object.entries(codes)) {
|
||||
const baselineCount = (baseline[file] && baseline[file][code]) || 0;
|
||||
if (liveCount > baselineCount) {
|
||||
regressions.push({ file, code, liveCount, baselineCount });
|
||||
} else if (liveCount < baselineCount) {
|
||||
improvements.push({ file, code, liveCount, baselineCount });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const [file, codes] of Object.entries(baseline)) {
|
||||
for (const [code, baselineCount] of Object.entries(codes)) {
|
||||
const liveCount = (live[file] && live[file][code]) || 0;
|
||||
if (liveCount === 0 && baselineCount > 0) {
|
||||
improvements.push({ file, code, liveCount: 0, baselineCount });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { regressions, improvements };
|
||||
}
|
||||
|
||||
function runTsc() {
|
||||
try {
|
||||
return execFileSync(
|
||||
@@ -117,7 +79,9 @@ function main() {
|
||||
`[api-typecheck] ${improvements.length} baselined error(s) no longer present ` +
|
||||
`— run 'node scripts/check/check-api-typecheck.mjs --update' to ratchet the baseline down:\n` +
|
||||
improvements
|
||||
.map((i) => ` - ${i.file} ${i.code} (baseline ${i.baselineCount} -> live ${i.liveCount})`)
|
||||
.map(
|
||||
(i) => ` - ${i.file} ${i.code} (baseline ${i.baselineCount} -> live ${i.liveCount})`
|
||||
)
|
||||
.join("\n")
|
||||
);
|
||||
}
|
||||
|
||||
@@ -22,74 +22,15 @@ import { execFileSync } from "node:child_process";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { diffAgainstBaseline, parseTscOutput } from "./typecheckBaseline.mjs";
|
||||
|
||||
export { diffAgainstBaseline, parseTscOutput } from "./typecheckBaseline.mjs";
|
||||
|
||||
const ROOT = process.cwd();
|
||||
const TSCONFIG = path.join(ROOT, "open-sse", "tsconfig.json");
|
||||
const BASELINE_PATH = path.join(ROOT, "config/quality/open-sse-typecheck-baseline.json");
|
||||
const UPDATE = process.argv.includes("--update");
|
||||
|
||||
// Matches tsc --pretty false output lines, e.g.:
|
||||
// src/app/api/v1/chat/route.ts(12,7): error TS2304: Cannot find name 'bar'.
|
||||
// open-sse/handlers/chatCore.ts(45,3): error TS7053: Element implicitly has an 'any'...
|
||||
const TSC_ERROR_LINE = /^(.+?)\((\d+),(\d+)\): error (TS\d+):/;
|
||||
|
||||
/**
|
||||
* Parses raw `tsc --pretty false` stdout into a nested count map:
|
||||
* { "<relative file path>": { "<TS code>": <count> } }
|
||||
*
|
||||
* Pure/exported for unit testing against synthetic tsc output — no child
|
||||
* process involved here.
|
||||
*/
|
||||
export function parseTscOutput(raw) {
|
||||
const counts = {};
|
||||
const lines = String(raw).split("\n");
|
||||
for (const line of lines) {
|
||||
const match = TSC_ERROR_LINE.exec(line);
|
||||
if (!match) continue;
|
||||
const [, file, , , code] = match;
|
||||
if (!counts[file]) counts[file] = {};
|
||||
counts[file][code] = (counts[file][code] || 0) + 1;
|
||||
}
|
||||
return counts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares live (file, TS code) error counts against a frozen baseline.
|
||||
* Returns `{ regressions, improvements }`:
|
||||
* - regressions: entries where live count > baselined count (or the pair is
|
||||
* entirely new/unbaselined) — these fail the gate.
|
||||
* - improvements: entries where live count < baselined count — informational,
|
||||
* do not fail (use --update to ratchet the baseline down).
|
||||
*
|
||||
* Exported for unit testing.
|
||||
*/
|
||||
export function diffAgainstBaseline(live, baseline) {
|
||||
const regressions = [];
|
||||
const improvements = [];
|
||||
|
||||
for (const [file, codes] of Object.entries(live)) {
|
||||
for (const [code, liveCount] of Object.entries(codes)) {
|
||||
const baselineCount = (baseline[file] && baseline[file][code]) || 0;
|
||||
if (liveCount > baselineCount) {
|
||||
regressions.push({ file, code, liveCount, baselineCount });
|
||||
} else if (liveCount < baselineCount) {
|
||||
improvements.push({ file, code, liveCount, baselineCount });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const [file, codes] of Object.entries(baseline)) {
|
||||
for (const [code, baselineCount] of Object.entries(codes)) {
|
||||
const liveCount = (live[file] && live[file][code]) || 0;
|
||||
if (liveCount === 0 && baselineCount > 0) {
|
||||
improvements.push({ file, code, liveCount: 0, baselineCount });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { regressions, improvements };
|
||||
}
|
||||
|
||||
function runTsc() {
|
||||
try {
|
||||
const stdout = execFileSync(
|
||||
@@ -143,7 +84,9 @@ function main() {
|
||||
`[open-sse-typecheck] ${improvements.length} baselined error(s) no longer present ` +
|
||||
`— run 'node scripts/check/check-open-sse-typecheck.mjs --update' to ratchet the baseline down:\n` +
|
||||
improvements
|
||||
.map((i) => ` - ${i.file} ${i.code} (baseline ${i.baselineCount} -> live ${i.liveCount})`)
|
||||
.map(
|
||||
(i) => ` - ${i.file} ${i.code} (baseline ${i.baselineCount} -> live ${i.liveCount})`
|
||||
)
|
||||
.join("\n")
|
||||
);
|
||||
}
|
||||
|
||||
91
scripts/check/typecheckBaseline.mjs
Normal file
91
scripts/check/typecheckBaseline.mjs
Normal file
@@ -0,0 +1,91 @@
|
||||
// Shared parsing and frozen-baseline comparison for the scoped TypeScript gates.
|
||||
|
||||
const TSC_ERROR_LINE = /^(.+?)\((\d+),(\d+)\): error (TS\d+):/;
|
||||
const TS_CODE = /^TS\d+$/;
|
||||
const UNSAFE_PROPERTY_KEYS = new Set(["__proto__", "constructor", "prototype"]);
|
||||
|
||||
function isPlainObject(value) {
|
||||
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
||||
const prototype = Object.getPrototypeOf(value);
|
||||
return prototype === Object.prototype || prototype === null;
|
||||
}
|
||||
|
||||
function normalizeDiagnosticCounts(value, label) {
|
||||
if (!isPlainObject(value)) {
|
||||
throw new TypeError(`${label} must be a plain object`);
|
||||
}
|
||||
|
||||
const normalized = Object.create(null);
|
||||
for (const [file, codes] of Object.entries(value)) {
|
||||
if (UNSAFE_PROPERTY_KEYS.has(file)) {
|
||||
throw new TypeError(`${label} contains unsupported property key "${file}"`);
|
||||
}
|
||||
if (file.startsWith("_")) continue;
|
||||
if (!isPlainObject(codes)) {
|
||||
throw new TypeError(`${label} entry "${file}" must be a plain object`);
|
||||
}
|
||||
|
||||
const normalizedCodes = Object.create(null);
|
||||
for (const [code, count] of Object.entries(codes)) {
|
||||
if (!TS_CODE.test(code)) {
|
||||
throw new TypeError(`${label} entry "${file}" has invalid TypeScript code "${code}"`);
|
||||
}
|
||||
if (!Number.isFinite(count) || !Number.isInteger(count) || count < 0) {
|
||||
throw new TypeError(
|
||||
`${label} entry "${file}" code "${code}" must be a finite nonnegative integer`
|
||||
);
|
||||
}
|
||||
normalizedCodes[code] = count;
|
||||
}
|
||||
normalized[file] = normalizedCodes;
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
/** Parse `tsc --pretty false` output into per-file/per-code diagnostic counts. */
|
||||
export function parseTscOutput(raw) {
|
||||
const counts = {};
|
||||
for (const line of String(raw).split("\n")) {
|
||||
const match = TSC_ERROR_LINE.exec(line);
|
||||
if (!match) continue;
|
||||
const [, file, , , code] = match;
|
||||
if (!counts[file]) counts[file] = {};
|
||||
counts[file][code] = (counts[file][code] || 0) + 1;
|
||||
}
|
||||
return counts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare live diagnostic counts with a frozen baseline.
|
||||
*
|
||||
* Underscore-prefixed top-level keys are reserved for baseline metadata and
|
||||
* never participate in the diagnostic comparison.
|
||||
*/
|
||||
export function diffAgainstBaseline(live, baseline) {
|
||||
const liveCounts = normalizeDiagnosticCounts(live, "live diagnostics");
|
||||
const baselineCounts = normalizeDiagnosticCounts(baseline, "typecheck baseline");
|
||||
const regressions = [];
|
||||
const improvements = [];
|
||||
|
||||
for (const [file, codes] of Object.entries(liveCounts)) {
|
||||
for (const [code, liveCount] of Object.entries(codes)) {
|
||||
const baselineCount = baselineCounts[file]?.[code] ?? 0;
|
||||
if (liveCount > baselineCount) {
|
||||
regressions.push({ file, code, liveCount, baselineCount });
|
||||
} else if (liveCount < baselineCount) {
|
||||
improvements.push({ file, code, liveCount, baselineCount });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const [file, codes] of Object.entries(baselineCounts)) {
|
||||
for (const [code, baselineCount] of Object.entries(codes)) {
|
||||
const liveCodes = liveCounts[file];
|
||||
if (!Object.hasOwn(liveCodes ?? {}, code) && baselineCount > 0) {
|
||||
improvements.push({ file, code, liveCount: 0, baselineCount });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { regressions, improvements };
|
||||
}
|
||||
@@ -141,7 +141,6 @@ const KNOWN_SVGS = new Set([
|
||||
"moonshot",
|
||||
"morph",
|
||||
"nebius",
|
||||
"nimble-search",
|
||||
"nlpcloud",
|
||||
"nomic",
|
||||
"novita",
|
||||
@@ -152,6 +151,7 @@ const KNOWN_SVGS = new Set([
|
||||
"openai",
|
||||
"openclaw",
|
||||
"openrouter",
|
||||
"opper",
|
||||
"orcarouter",
|
||||
"ovhcloud",
|
||||
"perplexity",
|
||||
@@ -240,6 +240,7 @@ const GENERIC_PROVIDER_IDS = new Set([
|
||||
"leonardo",
|
||||
"modal",
|
||||
"modelscope",
|
||||
"nimble-search",
|
||||
"nlpcloud",
|
||||
"oauth",
|
||||
"oci",
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
parseTscOutput,
|
||||
diffAgainstBaseline,
|
||||
} from "../../../scripts/check/check-api-typecheck.mjs";
|
||||
import { diffAgainstBaseline as diffOpenSseAgainstBaseline } from "../../../scripts/check/check-open-sse-typecheck.mjs";
|
||||
|
||||
test("parseTscOutput: parses an API-route TS2554 regression", () => {
|
||||
const raw =
|
||||
@@ -85,3 +86,130 @@ test("diffAgainstBaseline: reports a disappeared diagnostic as an improvement",
|
||||
assert.equal(improvements[0].liveCount, 0);
|
||||
assert.equal(improvements[0].baselineCount, 2);
|
||||
});
|
||||
|
||||
test("diffAgainstBaseline: ignores underscore-prefixed baseline metadata", () => {
|
||||
const baseline = {
|
||||
_relax_velocity_2026_08_30:
|
||||
"per-file TS diagnostic counts raised by 20% (289 -> 455); velocity phase",
|
||||
"src/app/api/foo/route.ts": { TS2339: 1 },
|
||||
};
|
||||
const live = { "src/app/api/foo/route.ts": { TS2339: 1 } };
|
||||
|
||||
for (const compare of [diffAgainstBaseline, diffOpenSseAgainstBaseline]) {
|
||||
assert.deepEqual(compare(live, baseline), {
|
||||
regressions: [],
|
||||
improvements: [],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
test("diffAgainstBaseline: rejects a string in place of a real file diagnostic map", () => {
|
||||
const malformedBaseline = {
|
||||
"src/app/api/foo/route.ts": "TS2339: 1",
|
||||
};
|
||||
|
||||
assert.throws(
|
||||
() => diffAgainstBaseline({}, malformedBaseline),
|
||||
/src\/app\/api\/foo\/route\.ts.*plain object/
|
||||
);
|
||||
assert.throws(
|
||||
() => diffOpenSseAgainstBaseline({}, malformedBaseline),
|
||||
/src\/app\/api\/foo\/route\.ts.*plain object/
|
||||
);
|
||||
});
|
||||
|
||||
test("diffAgainstBaseline: rejects non-plain roots and file maps", () => {
|
||||
const inheritedRoot = Object.create({
|
||||
"src/app/api/inherited/route.ts": { TS2339: 1 },
|
||||
});
|
||||
const inheritedFileMap = Object.create({ TS2339: 1 });
|
||||
|
||||
for (const malformedBaseline of [[], "not an object", null, inheritedRoot]) {
|
||||
assert.throws(
|
||||
() => diffAgainstBaseline({}, malformedBaseline),
|
||||
/typecheck baseline must be a plain object/
|
||||
);
|
||||
}
|
||||
for (const malformedFileMap of [[], null, inheritedFileMap]) {
|
||||
assert.throws(
|
||||
() =>
|
||||
diffAgainstBaseline(
|
||||
{},
|
||||
{
|
||||
"src/app/api/foo/route.ts": malformedFileMap,
|
||||
}
|
||||
),
|
||||
/src\/app\/api\/foo\/route\.ts.*plain object/
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test("diffAgainstBaseline: rejects prototype property keys", () => {
|
||||
const malformedBaseline = JSON.parse('{"__proto__":{"TS2339":1}}');
|
||||
|
||||
assert.throws(
|
||||
() => diffAgainstBaseline({}, malformedBaseline),
|
||||
/unsupported property key "__proto__"/
|
||||
);
|
||||
});
|
||||
|
||||
test("diffAgainstBaseline: rejects non-TypeScript diagnostic keys", () => {
|
||||
for (const code of ["2339", "TSX2339", "TS23x", "constructor"]) {
|
||||
assert.throws(
|
||||
() =>
|
||||
diffAgainstBaseline(
|
||||
{},
|
||||
{
|
||||
"src/app/api/foo/route.ts": { [code]: 1 },
|
||||
}
|
||||
),
|
||||
/invalid TypeScript code/
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test("diffAgainstBaseline: rejects invalid diagnostic counts", () => {
|
||||
for (const count of [Number.NaN, Number.POSITIVE_INFINITY, -1, 1.5, "1"]) {
|
||||
assert.throws(
|
||||
() =>
|
||||
diffAgainstBaseline(
|
||||
{},
|
||||
{
|
||||
"src/app/api/foo/route.ts": { TS2339: count },
|
||||
}
|
||||
),
|
||||
/finite nonnegative integer/
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test("diffAgainstBaseline: validates live diagnostics with the same schema", () => {
|
||||
assert.throws(
|
||||
() =>
|
||||
diffAgainstBaseline(
|
||||
{ "src/app/api/foo/route.ts": { TS2339: -1 } },
|
||||
{ "src/app/api/foo/route.ts": { TS2339: 1 } }
|
||||
),
|
||||
/live diagnostics.*finite nonnegative integer/
|
||||
);
|
||||
});
|
||||
|
||||
test("diffAgainstBaseline: accepts zero counts without fabricating a second improvement", () => {
|
||||
assert.deepEqual(
|
||||
diffAgainstBaseline(
|
||||
{ "src/app/api/foo/route.ts": { TS2339: 0 } },
|
||||
{ "src/app/api/foo/route.ts": { TS2339: 1 } }
|
||||
),
|
||||
{
|
||||
regressions: [],
|
||||
improvements: [
|
||||
{
|
||||
file: "src/app/api/foo/route.ts",
|
||||
code: "TS2339",
|
||||
liveCount: 0,
|
||||
baselineCount: 1,
|
||||
},
|
||||
],
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
@@ -541,7 +541,7 @@ test("provider asset provenance gate binds auditedCommit to the physical provide
|
||||
}
|
||||
});
|
||||
|
||||
test("repository provider asset manifest covers the audited 142-file snapshot", (t) => {
|
||||
test("repository provider asset manifest covers the audited 141-file snapshot", (t) => {
|
||||
const manifestPath = join(REPO_ROOT, "config/quality/provider-assets-provenance.jsonl");
|
||||
const { auditedCommit } = JSON.parse(readFileSync(manifestPath, "utf8").split("\n")[0]);
|
||||
if (!gitHasCommit(auditedCommit) && isShallowRepository()) {
|
||||
@@ -556,7 +556,7 @@ test("repository provider asset manifest covers the audited 142-file snapshot",
|
||||
assert.equal(result.status, 0, `${result.stdout}\n${result.stderr}`);
|
||||
assert.match(
|
||||
result.stdout,
|
||||
/142\/142 registered; proven=71 probable=69 unresolved=2; duplicate-groups=1/
|
||||
/141\/141 registered; proven=72 probable=69 unresolved=0; duplicate-groups=1/
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -609,7 +609,9 @@ test("Non-streaming: routes native Grok webSearch to URL fetch tool when user as
|
||||
const result = await executor.execute({
|
||||
model: "grok-4.1-fast",
|
||||
body: {
|
||||
messages: [{ role: "user", content: "Haz webfetch de http://endless.horse/ y dime que hay" }],
|
||||
messages: [
|
||||
{ role: "user", content: "Haz webfetch de http://endless.horse/ y dime que hay" },
|
||||
],
|
||||
stream: false,
|
||||
tools: [
|
||||
{
|
||||
@@ -645,7 +647,10 @@ test("Non-streaming: routes native Grok webSearch to URL fetch tool when user as
|
||||
});
|
||||
const json = (await result.response.json()) as any;
|
||||
assert.equal(json.choices[0].message.tool_calls[0].function.name, "webfetch");
|
||||
assert.equal(json.choices[0].message.tool_calls[0].function.arguments, JSON.stringify({ url: "http://endless.horse/" }));
|
||||
assert.equal(
|
||||
json.choices[0].message.tool_calls[0].function.arguments,
|
||||
JSON.stringify({ url: "http://endless.horse/" })
|
||||
);
|
||||
} finally {
|
||||
restore();
|
||||
}
|
||||
@@ -678,7 +683,11 @@ test("Non-streaming: keeps native Grok webSearch on search tool when user asks s
|
||||
function: {
|
||||
name: "public_search_tool",
|
||||
description: "Search the web for any topic",
|
||||
parameters: { type: "object", properties: { query: { type: "string" } }, required: ["query"] },
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { query: { type: "string" } },
|
||||
required: ["query"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -686,7 +695,11 @@ test("Non-streaming: keeps native Grok webSearch on search tool when user asks s
|
||||
function: {
|
||||
name: "webfetch",
|
||||
description: "Fetch a URL and extract page content",
|
||||
parameters: { type: "object", properties: { url: { type: "string" } }, required: ["url"] },
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { url: { type: "string" } },
|
||||
required: ["url"],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -729,7 +742,11 @@ test("Non-streaming: native Grok webSearch does not choose context memory search
|
||||
function: {
|
||||
name: "memory_context_tool",
|
||||
description: "Search across project memories and conversation history",
|
||||
parameters: { type: "object", properties: { query: { type: "string" } }, required: ["query"] },
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { query: { type: "string" } },
|
||||
required: ["query"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -737,7 +754,11 @@ test("Non-streaming: native Grok webSearch does not choose context memory search
|
||||
function: {
|
||||
name: "public_search_tool",
|
||||
description: "Search the web for current public information",
|
||||
parameters: { type: "object", properties: { query: { type: "string" } }, required: ["query"] },
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { query: { type: "string" } },
|
||||
required: ["query"],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -778,7 +799,12 @@ test("Non-streaming: maps native Grok browsePage to URL fetch tool", async () =>
|
||||
const result = await executor.execute({
|
||||
model: "grok-4.1-fast",
|
||||
body: {
|
||||
messages: [{ role: "user", content: "Busca la release oficial de Ubuntu y abre la pagina del anuncio" }],
|
||||
messages: [
|
||||
{
|
||||
role: "user",
|
||||
content: "Busca la release oficial de Ubuntu y abre la pagina del anuncio",
|
||||
},
|
||||
],
|
||||
stream: false,
|
||||
tools: [
|
||||
{
|
||||
@@ -786,7 +812,11 @@ test("Non-streaming: maps native Grok browsePage to URL fetch tool", async () =>
|
||||
function: {
|
||||
name: "public_search_tool",
|
||||
description: "Search the web for any topic",
|
||||
parameters: { type: "object", properties: { query: { type: "string" } }, required: ["query"] },
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { query: { type: "string" } },
|
||||
required: ["query"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -794,7 +824,11 @@ test("Non-streaming: maps native Grok browsePage to URL fetch tool", async () =>
|
||||
function: {
|
||||
name: "webfetch",
|
||||
description: "Fetch a URL with better extraction for static/docs pages",
|
||||
parameters: { type: "object", properties: { url: { type: "string" }, prompt: { type: "string" } }, required: ["url"] },
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { url: { type: "string" }, prompt: { type: "string" } },
|
||||
required: ["url"],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -849,7 +883,15 @@ test("Non-streaming: does not repeat a tool call that already has a tool result"
|
||||
{ role: "tool", tool_call_id: "call_1", name: "bash", content: "413 /tmp/a" },
|
||||
],
|
||||
stream: false,
|
||||
tools: [{ type: "function", function: { name: "bash", parameters: { type: "object", properties: { command: { type: "string" } } } } }],
|
||||
tools: [
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "bash",
|
||||
parameters: { type: "object", properties: { command: { type: "string" } } },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
stream: false,
|
||||
credentials: { apiKey: "test-sso-token" },
|
||||
@@ -894,7 +936,10 @@ test("Non-streaming: does not repeat equivalent terminal command with different
|
||||
type: "function",
|
||||
function: {
|
||||
name: "bash",
|
||||
arguments: JSON.stringify({ command: 'wc -l "/tmp/a"', description: "previous run" }),
|
||||
arguments: JSON.stringify({
|
||||
command: 'wc -l "/tmp/a"',
|
||||
description: "previous run",
|
||||
}),
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -953,15 +998,31 @@ test("Non-streaming: allows a different tool after a completed call", async () =
|
||||
role: "assistant",
|
||||
content: null,
|
||||
tool_calls: [
|
||||
{ id: "call_1", type: "function", function: { name: "bash", arguments: JSON.stringify({ command: "wc -l /tmp/a" }) } },
|
||||
{
|
||||
id: "call_1",
|
||||
type: "function",
|
||||
function: { name: "bash", arguments: JSON.stringify({ command: "wc -l /tmp/a" }) },
|
||||
},
|
||||
],
|
||||
},
|
||||
{ role: "tool", tool_call_id: "call_1", name: "bash", content: "413 /tmp/a" },
|
||||
],
|
||||
stream: false,
|
||||
tools: [
|
||||
{ type: "function", function: { name: "bash", parameters: { type: "object", properties: { command: { type: "string" } } } } },
|
||||
{ type: "function", function: { name: "read", parameters: { type: "object", properties: { filePath: { type: "string" } } } } },
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "bash",
|
||||
parameters: { type: "object", properties: { command: { type: "string" } } },
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "read",
|
||||
parameters: { type: "object", properties: { filePath: { type: "string" } } },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
stream: false,
|
||||
@@ -1011,11 +1072,19 @@ test("Non-streaming: raw_function_result is not emitted as final content", async
|
||||
{
|
||||
id: "call_1",
|
||||
type: "function",
|
||||
function: { name: "bash", arguments: JSON.stringify({ command: "wc -l /tmp/project/config.json" }) },
|
||||
function: {
|
||||
name: "bash",
|
||||
arguments: JSON.stringify({ command: "wc -l /tmp/project/config.json" }),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{ role: "tool", tool_call_id: "call_1", name: "bash", content: "413 /tmp/project/config.json" },
|
||||
{
|
||||
role: "tool",
|
||||
tool_call_id: "call_1",
|
||||
name: "bash",
|
||||
content: "413 /tmp/project/config.json",
|
||||
},
|
||||
],
|
||||
stream: false,
|
||||
},
|
||||
@@ -1099,7 +1168,9 @@ test("Request: forwards tool results into Grok prompt for the next turn", async
|
||||
status: 200,
|
||||
headers: new Headers({ "Content-Type": "application/json" }),
|
||||
text: null,
|
||||
body: mockGrokStream([{ result: { response: { modelResponse: { message: "It is sunny." } } } }]),
|
||||
body: mockGrokStream([
|
||||
{ result: { response: { modelResponse: { message: "It is sunny." } } } },
|
||||
]),
|
||||
};
|
||||
});
|
||||
try {
|
||||
@@ -1113,7 +1184,11 @@ test("Request: forwards tool results into Grok prompt for the next turn", async
|
||||
role: "assistant",
|
||||
content: null,
|
||||
tool_calls: [
|
||||
{ id: "call_weather", type: "function", function: { name: "get_weather", arguments: "{}" } },
|
||||
{
|
||||
id: "call_weather",
|
||||
type: "function",
|
||||
function: { name: "get_weather", arguments: "{}" },
|
||||
},
|
||||
],
|
||||
},
|
||||
{ role: "tool", tool_call_id: "call_weather", name: "get_weather", content: "sunny" },
|
||||
@@ -1127,7 +1202,11 @@ test("Request: forwards tool results into Grok prompt for the next turn", async
|
||||
});
|
||||
const payload = JSON.parse(capturedBody);
|
||||
assert.ok(payload.message.includes("Previous assistant tool calls"));
|
||||
assert.ok(payload.message.includes("CLIENT TOOL RESULT from caller runtime for get_weather (call_weather)"));
|
||||
assert.ok(
|
||||
payload.message.includes(
|
||||
"CLIENT TOOL RESULT from caller runtime for get_weather (call_weather)"
|
||||
)
|
||||
);
|
||||
assert.ok(payload.message.includes("do not call the same tool again"));
|
||||
assert.ok(payload.message.includes("sunny"));
|
||||
} finally {
|
||||
@@ -1209,7 +1288,9 @@ test("Request: leaves native Grok search enabled when client tools are absent",
|
||||
});
|
||||
|
||||
test("Request: places tool manifest next to latest user after noisy history", async () => {
|
||||
const capture = mockFetchCapture([{ result: { response: { modelResponse: { message: "{}" } } } }]);
|
||||
const capture = mockFetchCapture([
|
||||
{ result: { response: { modelResponse: { message: "{}" } } } },
|
||||
]);
|
||||
try {
|
||||
const executor = new GrokWebExecutor();
|
||||
await executor.execute({
|
||||
@@ -1218,7 +1299,10 @@ test("Request: places tool manifest next to latest user after noisy history", as
|
||||
messages: [
|
||||
{ role: "user", content: "old question" },
|
||||
{ role: "assistant", content: "old answer claiming file does not exist" },
|
||||
{ role: "user", content: "/tmp/project/config.json dime cuantas lineas tiene este archivo" },
|
||||
{
|
||||
role: "user",
|
||||
content: "/tmp/project/config.json dime cuantas lineas tiene este archivo",
|
||||
},
|
||||
],
|
||||
stream: false,
|
||||
tools: [
|
||||
@@ -1227,7 +1311,11 @@ test("Request: places tool manifest next to latest user after noisy history", as
|
||||
function: {
|
||||
name: "bash",
|
||||
description: "Run a shell command",
|
||||
parameters: { type: "object", properties: { command: { type: "string" } }, required: ["command"] },
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { command: { type: "string" } },
|
||||
required: ["command"],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -1250,7 +1338,9 @@ test("Request: places tool manifest next to latest user after noisy history", as
|
||||
});
|
||||
|
||||
test("Request: strips injected internal reminders from Grok prompt", async () => {
|
||||
const capture = mockFetchCapture([{ result: { response: { modelResponse: { message: "{}" } } } }]);
|
||||
const capture = mockFetchCapture([
|
||||
{ result: { response: { modelResponse: { message: "{}" } } } },
|
||||
]);
|
||||
try {
|
||||
const executor = new GrokWebExecutor();
|
||||
await executor.execute({
|
||||
@@ -1270,7 +1360,11 @@ test("Request: strips injected internal reminders from Grok prompt", async () =>
|
||||
function: {
|
||||
name: "fetch_url_tool",
|
||||
description: "Fetch URL or browse web page content",
|
||||
parameters: { type: "object", properties: { url: { type: "string" } }, required: ["url"] },
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { url: { type: "string" } },
|
||||
required: ["url"],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -1290,7 +1384,9 @@ test("Request: strips injected internal reminders from Grok prompt", async () =>
|
||||
});
|
||||
|
||||
test("Request: old completed tools do not suppress fresh latest-user tool calls", async () => {
|
||||
const capture = mockFetchCapture([{ result: { response: { modelResponse: { message: "{}" } } } }]);
|
||||
const capture = mockFetchCapture([
|
||||
{ result: { response: { modelResponse: { message: "{}" } } } },
|
||||
]);
|
||||
try {
|
||||
const executor = new GrokWebExecutor();
|
||||
await executor.execute({
|
||||
@@ -1320,7 +1416,11 @@ test("Request: old completed tools do not suppress fresh latest-user tool calls"
|
||||
function: {
|
||||
name: "bash",
|
||||
description: "Run a shell command",
|
||||
parameters: { type: "object", properties: { command: { type: "string" } }, required: ["command"] },
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { command: { type: "string" } },
|
||||
required: ["command"],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -1339,7 +1439,9 @@ test("Request: old completed tools do not suppress fresh latest-user tool calls"
|
||||
});
|
||||
|
||||
test("Request: appends tool manifest after tool results during multi-step continuation", async () => {
|
||||
const capture = mockFetchCapture([{ result: { response: { modelResponse: { message: "{}" } } } }]);
|
||||
const capture = mockFetchCapture([
|
||||
{ result: { response: { modelResponse: { message: "{}" } } } },
|
||||
]);
|
||||
try {
|
||||
const executor = new GrokWebExecutor();
|
||||
await executor.execute({
|
||||
@@ -1351,7 +1453,11 @@ test("Request: appends tool manifest after tool results during multi-step contin
|
||||
role: "assistant",
|
||||
content: "",
|
||||
tool_calls: [
|
||||
{ id: "read_call", type: "function", function: { name: "read", arguments: JSON.stringify({ filePath: "/tmp/a" }) } },
|
||||
{
|
||||
id: "read_call",
|
||||
type: "function",
|
||||
function: { name: "read", arguments: JSON.stringify({ filePath: "/tmp/a" }) },
|
||||
},
|
||||
],
|
||||
},
|
||||
{ role: "tool", tool_call_id: "read_call", name: "read", content: "file content" },
|
||||
@@ -1363,7 +1469,11 @@ test("Request: appends tool manifest after tool results during multi-step contin
|
||||
function: {
|
||||
name: "memory_context_tool",
|
||||
description: "Search across project memories and raw conversation history.",
|
||||
parameters: { type: "object", properties: { query: { type: "string" } }, required: ["query"] },
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { query: { type: "string" } },
|
||||
required: ["query"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -1371,7 +1481,11 @@ test("Request: appends tool manifest after tool results during multi-step contin
|
||||
function: {
|
||||
name: "public_search_tool",
|
||||
description: "Search the web for any topic and get clean content.",
|
||||
parameters: { type: "object", properties: { query: { type: "string" } }, required: ["query"] },
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { query: { type: "string" } },
|
||||
required: ["query"],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -1393,7 +1507,9 @@ test("Request: appends tool manifest after tool results during multi-step contin
|
||||
});
|
||||
|
||||
test("Request: keeps generic manifest ordered for file understanding tasks", async () => {
|
||||
const capture = mockFetchCapture([{ result: { response: { modelResponse: { message: "{}" } } } }]);
|
||||
const capture = mockFetchCapture([
|
||||
{ result: { response: { modelResponse: { message: "{}" } } } },
|
||||
]);
|
||||
try {
|
||||
const executor = new GrokWebExecutor();
|
||||
await executor.execute({
|
||||
@@ -1412,7 +1528,11 @@ test("Request: keeps generic manifest ordered for file understanding tasks", asy
|
||||
function: {
|
||||
name: "bash",
|
||||
description: "Execute shell command",
|
||||
parameters: { type: "object", properties: { command: { type: "string" }, description: { type: "string" } }, required: ["command"] },
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { command: { type: "string" }, description: { type: "string" } },
|
||||
required: ["command"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -1420,7 +1540,11 @@ test("Request: keeps generic manifest ordered for file understanding tasks", asy
|
||||
function: {
|
||||
name: "read",
|
||||
description: "Read a file or directory from the local filesystem",
|
||||
parameters: { type: "object", properties: { filePath: { type: "string" } }, required: ["filePath"] },
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { filePath: { type: "string" } },
|
||||
required: ["filePath"],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -1448,21 +1572,33 @@ test("Request: keeps generic manifest ordered for file understanding tasks", asy
|
||||
});
|
||||
|
||||
test("Request: keeps generic manifest ordered for official web facts", async () => {
|
||||
const capture = mockFetchCapture([{ result: { response: { modelResponse: { message: "{}" } } } }]);
|
||||
const capture = mockFetchCapture([
|
||||
{ result: { response: { modelResponse: { message: "{}" } } } },
|
||||
]);
|
||||
try {
|
||||
const executor = new GrokWebExecutor();
|
||||
await executor.execute({
|
||||
model: "grok-4.1-fast",
|
||||
body: {
|
||||
messages: [{ role: "user", content: "contrasta con una fuente web oficial la ultima release de Ubuntu 24.04" }],
|
||||
messages: [
|
||||
{
|
||||
role: "user",
|
||||
content: "contrasta con una fuente web oficial la ultima release de Ubuntu 24.04",
|
||||
},
|
||||
],
|
||||
stream: false,
|
||||
tools: [
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "memory_context_tool",
|
||||
description: "Search across project memories, indexed git commits, and raw conversation history.",
|
||||
parameters: { type: "object", properties: { query: { type: "string" } }, required: ["query"] },
|
||||
description:
|
||||
"Search across project memories, indexed git commits, and raw conversation history.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { query: { type: "string" } },
|
||||
required: ["query"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -1470,7 +1606,11 @@ test("Request: keeps generic manifest ordered for official web facts", async ()
|
||||
function: {
|
||||
name: "public_search_tool",
|
||||
description: "Search the web for any topic and get clean, ready-to-use content.",
|
||||
parameters: { type: "object", properties: { query: { type: "string" }, numResults: { type: "number" } }, required: ["query"] },
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { query: { type: "string" }, numResults: { type: "number" } },
|
||||
required: ["query"],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -1498,7 +1638,9 @@ test("Request: keeps generic manifest ordered for official web facts", async ()
|
||||
});
|
||||
|
||||
test("Request: base manifest order puts public web search before context memory", async () => {
|
||||
const capture = mockFetchCapture([{ result: { response: { modelResponse: { message: "{}" } } } }]);
|
||||
const capture = mockFetchCapture([
|
||||
{ result: { response: { modelResponse: { message: "{}" } } } },
|
||||
]);
|
||||
try {
|
||||
const executor = new GrokWebExecutor();
|
||||
await executor.execute({
|
||||
@@ -1512,7 +1654,11 @@ test("Request: base manifest order puts public web search before context memory"
|
||||
function: {
|
||||
name: "memory_context_tool",
|
||||
description: "Search across project memories and conversation history",
|
||||
parameters: { type: "object", properties: { query: { type: "string" } }, required: ["query"] },
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { query: { type: "string" } },
|
||||
required: ["query"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -1520,7 +1666,11 @@ test("Request: base manifest order puts public web search before context memory"
|
||||
function: {
|
||||
name: "public_search_tool",
|
||||
description: "Search the web for current public information",
|
||||
parameters: { type: "object", properties: { query: { type: "string" } }, required: ["query"] },
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { query: { type: "string" } },
|
||||
required: ["query"],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -1531,14 +1681,18 @@ test("Request: base manifest order puts public web search before context memory"
|
||||
log: null,
|
||||
});
|
||||
const prompt = String(capture.body.message);
|
||||
assert.ok(prompt.indexOf("name: public_search_tool") < prompt.indexOf("name: memory_context_tool"));
|
||||
assert.ok(
|
||||
prompt.indexOf("name: public_search_tool") < prompt.indexOf("name: memory_context_tool")
|
||||
);
|
||||
} finally {
|
||||
capture.restore();
|
||||
}
|
||||
});
|
||||
|
||||
test("Request: ranks public web search before infrastructure search", async () => {
|
||||
const capture = mockFetchCapture([{ result: { response: { modelResponse: { message: "{}" } } } }]);
|
||||
const capture = mockFetchCapture([
|
||||
{ result: { response: { modelResponse: { message: "{}" } } } },
|
||||
]);
|
||||
try {
|
||||
const executor = new GrokWebExecutor();
|
||||
await executor.execute({
|
||||
@@ -1551,8 +1705,13 @@ test("Request: ranks public web search before infrastructure search", async () =
|
||||
type: "function",
|
||||
function: {
|
||||
name: "tool_discovery_search",
|
||||
description: "Search and discover available upstream tools using BM25 full-text search",
|
||||
parameters: { type: "object", properties: { query: { type: "string" } }, required: ["query"] },
|
||||
description:
|
||||
"Search and discover available upstream tools using BM25 full-text search",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { query: { type: "string" } },
|
||||
required: ["query"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -1560,7 +1719,11 @@ test("Request: ranks public web search before infrastructure search", async () =
|
||||
function: {
|
||||
name: "public_search_tool",
|
||||
description: "Search the web for current public information",
|
||||
parameters: { type: "object", properties: { query: { type: "string" } }, required: ["query"] },
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { query: { type: "string" } },
|
||||
required: ["query"],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -1571,14 +1734,18 @@ test("Request: ranks public web search before infrastructure search", async () =
|
||||
log: null,
|
||||
});
|
||||
const prompt = String(capture.body.message);
|
||||
assert.ok(prompt.indexOf("name: public_search_tool") < prompt.indexOf("name: tool_discovery_search"));
|
||||
assert.ok(
|
||||
prompt.indexOf("name: public_search_tool") < prompt.indexOf("name: tool_discovery_search")
|
||||
);
|
||||
} finally {
|
||||
capture.restore();
|
||||
}
|
||||
});
|
||||
|
||||
test("Request: ranks URL fetch before generic MCP read", async () => {
|
||||
const capture = mockFetchCapture([{ result: { response: { modelResponse: { message: "{}" } } } }]);
|
||||
const capture = mockFetchCapture([
|
||||
{ result: { response: { modelResponse: { message: "{}" } } } },
|
||||
]);
|
||||
try {
|
||||
const executor = new GrokWebExecutor();
|
||||
await executor.execute({
|
||||
@@ -1591,8 +1758,13 @@ test("Request: ranks URL fetch before generic MCP read", async () => {
|
||||
type: "function",
|
||||
function: {
|
||||
name: "mcp_read_tool",
|
||||
description: "Execute a read-only upstream tool such as fetch, get, query, list, or search",
|
||||
parameters: { type: "object", properties: { name: { type: "string" }, args: { type: "object" } }, required: ["name"] },
|
||||
description:
|
||||
"Execute a read-only upstream tool such as fetch, get, query, list, or search",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { name: { type: "string" }, args: { type: "object" } },
|
||||
required: ["name"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -1600,7 +1772,11 @@ test("Request: ranks URL fetch before generic MCP read", async () => {
|
||||
function: {
|
||||
name: "fetch_url_tool",
|
||||
description: "Fetch URL or browse web page content",
|
||||
parameters: { type: "object", properties: { url: { type: "string" } }, required: ["url"] },
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { url: { type: "string" } },
|
||||
required: ["url"],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -1618,7 +1794,9 @@ test("Request: ranks URL fetch before generic MCP read", async () => {
|
||||
});
|
||||
|
||||
test("Request: ranks shell command before infrastructure command config", async () => {
|
||||
const capture = mockFetchCapture([{ result: { response: { modelResponse: { message: "{}" } } } }]);
|
||||
const capture = mockFetchCapture([
|
||||
{ result: { response: { modelResponse: { message: "{}" } } } },
|
||||
]);
|
||||
try {
|
||||
const executor = new GrokWebExecutor();
|
||||
await executor.execute({
|
||||
@@ -1632,7 +1810,11 @@ test("Request: ranks shell command before infrastructure command config", async
|
||||
function: {
|
||||
name: "upstream_server_config",
|
||||
description: "Manage upstream MCP servers, including stdio command configuration",
|
||||
parameters: { type: "object", properties: { command: { type: "string" }, name: { type: "string" } }, required: ["name"] },
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { command: { type: "string" }, name: { type: "string" } },
|
||||
required: ["name"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -1640,7 +1822,11 @@ test("Request: ranks shell command before infrastructure command config", async
|
||||
function: {
|
||||
name: "shell_command_tool",
|
||||
description: "Execute a shell command and return output",
|
||||
parameters: { type: "object", properties: { command: { type: "string" } }, required: ["command"] },
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { command: { type: "string" } },
|
||||
required: ["command"],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -1651,20 +1837,26 @@ test("Request: ranks shell command before infrastructure command config", async
|
||||
log: null,
|
||||
});
|
||||
const prompt = String(capture.body.message);
|
||||
assert.ok(prompt.indexOf("name: shell_command_tool") < prompt.indexOf("name: upstream_server_config"));
|
||||
assert.ok(
|
||||
prompt.indexOf("name: shell_command_tool") < prompt.indexOf("name: upstream_server_config")
|
||||
);
|
||||
} finally {
|
||||
capture.restore();
|
||||
}
|
||||
});
|
||||
|
||||
test("Request: commit wording does not prioritize memory over shell", async () => {
|
||||
const capture = mockFetchCapture([{ result: { response: { modelResponse: { message: "{}" } } } }]);
|
||||
const capture = mockFetchCapture([
|
||||
{ result: { response: { modelResponse: { message: "{}" } } } },
|
||||
]);
|
||||
try {
|
||||
const executor = new GrokWebExecutor();
|
||||
await executor.execute({
|
||||
model: "grok-4.1-fast",
|
||||
body: {
|
||||
messages: [{ role: "user", content: "ejecuta git rev-parse HEAD para ver el commit actual" }],
|
||||
messages: [
|
||||
{ role: "user", content: "ejecuta git rev-parse HEAD para ver el commit actual" },
|
||||
],
|
||||
stream: false,
|
||||
tools: [
|
||||
{
|
||||
@@ -1672,7 +1864,11 @@ test("Request: commit wording does not prioritize memory over shell", async () =
|
||||
function: {
|
||||
name: "memory_context_tool",
|
||||
description: "Search project memories and conversation history",
|
||||
parameters: { type: "object", properties: { query: { type: "string" } }, required: ["query"] },
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { query: { type: "string" } },
|
||||
required: ["query"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -1680,7 +1876,11 @@ test("Request: commit wording does not prioritize memory over shell", async () =
|
||||
function: {
|
||||
name: "shell_command_tool",
|
||||
description: "Execute a shell command and return output",
|
||||
parameters: { type: "object", properties: { command: { type: "string" } }, required: ["command"] },
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { command: { type: "string" } },
|
||||
required: ["command"],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -1691,14 +1891,18 @@ test("Request: commit wording does not prioritize memory over shell", async () =
|
||||
log: null,
|
||||
});
|
||||
const prompt = String(capture.body.message);
|
||||
assert.ok(prompt.indexOf("name: shell_command_tool") < prompt.indexOf("name: memory_context_tool"));
|
||||
assert.ok(
|
||||
prompt.indexOf("name: shell_command_tool") < prompt.indexOf("name: memory_context_tool")
|
||||
);
|
||||
} finally {
|
||||
capture.restore();
|
||||
}
|
||||
});
|
||||
|
||||
test("Request: explicit memory request prioritizes context over public web", async () => {
|
||||
const capture = mockFetchCapture([{ result: { response: { modelResponse: { message: "{}" } } } }]);
|
||||
const capture = mockFetchCapture([
|
||||
{ result: { response: { modelResponse: { message: "{}" } } } },
|
||||
]);
|
||||
try {
|
||||
const executor = new GrokWebExecutor();
|
||||
await executor.execute({
|
||||
@@ -1712,7 +1916,11 @@ test("Request: explicit memory request prioritizes context over public web", asy
|
||||
function: {
|
||||
name: "public_search_tool",
|
||||
description: "Search the web for current public information",
|
||||
parameters: { type: "object", properties: { query: { type: "string" } }, required: ["query"] },
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { query: { type: "string" } },
|
||||
required: ["query"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -1720,7 +1928,11 @@ test("Request: explicit memory request prioritizes context over public web", asy
|
||||
function: {
|
||||
name: "memory_context_tool",
|
||||
description: "Search project memories and conversation history",
|
||||
parameters: { type: "object", properties: { query: { type: "string" } }, required: ["query"] },
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { query: { type: "string" } },
|
||||
required: ["query"],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -1731,14 +1943,18 @@ test("Request: explicit memory request prioritizes context over public web", asy
|
||||
log: null,
|
||||
});
|
||||
const prompt = String(capture.body.message);
|
||||
assert.ok(prompt.indexOf("name: memory_context_tool") < prompt.indexOf("name: public_search_tool"));
|
||||
assert.ok(
|
||||
prompt.indexOf("name: memory_context_tool") < prompt.indexOf("name: public_search_tool")
|
||||
);
|
||||
} finally {
|
||||
capture.restore();
|
||||
}
|
||||
});
|
||||
|
||||
test("Request: explicit tool_choice exposes only the forced tool", async () => {
|
||||
const capture = mockFetchCapture([{ result: { response: { modelResponse: { message: "{}" } } } }]);
|
||||
const capture = mockFetchCapture([
|
||||
{ result: { response: { modelResponse: { message: "{}" } } } },
|
||||
]);
|
||||
try {
|
||||
const executor = new GrokWebExecutor();
|
||||
await executor.execute({
|
||||
@@ -1753,7 +1969,11 @@ test("Request: explicit tool_choice exposes only the forced tool", async () => {
|
||||
function: {
|
||||
name: "other_tool",
|
||||
description: "Other available tool",
|
||||
parameters: { type: "object", properties: { input: { type: "string" } }, required: ["input"] },
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { input: { type: "string" } },
|
||||
required: ["input"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -1761,7 +1981,11 @@ test("Request: explicit tool_choice exposes only the forced tool", async () => {
|
||||
function: {
|
||||
name: "forced_tool",
|
||||
description: "Forced tool",
|
||||
parameters: { type: "object", properties: { input: { type: "string" } }, required: ["input"] },
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { input: { type: "string" } },
|
||||
required: ["input"],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -1843,7 +2067,11 @@ test("Non-streaming: routes URL-like webfetch requests conservatively", async ()
|
||||
function: {
|
||||
name: "fetch_url_tool",
|
||||
description: "Fetch URL or browse web page content",
|
||||
parameters: { type: "object", properties: { url: { type: "string" } }, required: ["url"] },
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { url: { type: "string" } },
|
||||
required: ["url"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -1851,7 +2079,11 @@ test("Non-streaming: routes URL-like webfetch requests conservatively", async ()
|
||||
function: {
|
||||
name: "public_search_tool",
|
||||
description: "Search the web for current public information",
|
||||
parameters: { type: "object", properties: { query: { type: "string" } }, required: ["query"] },
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { query: { type: "string" } },
|
||||
required: ["query"],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -1862,8 +2094,16 @@ test("Non-streaming: routes URL-like webfetch requests conservatively", async ()
|
||||
log: null,
|
||||
});
|
||||
const json = (await result.response.json()) as any;
|
||||
assert.equal(json.choices[0].message.tool_calls[0].function.name, item.expectedName, item.title);
|
||||
assert.equal(json.choices[0].message.tool_calls[0].function.arguments, JSON.stringify(item.expectedArgs), item.title);
|
||||
assert.equal(
|
||||
json.choices[0].message.tool_calls[0].function.name,
|
||||
item.expectedName,
|
||||
item.title
|
||||
);
|
||||
assert.equal(
|
||||
json.choices[0].message.tool_calls[0].function.arguments,
|
||||
JSON.stringify(item.expectedArgs),
|
||||
item.title
|
||||
);
|
||||
} finally {
|
||||
restore();
|
||||
}
|
||||
@@ -1963,7 +2203,11 @@ test("Streaming: handles Grok card closing tags split across chunks", async () =
|
||||
test("Streaming: strips self-closing Grok render cards without swallowing later text", async () => {
|
||||
const restore = mockFetch(200, [
|
||||
{ result: { response: { token: "Alpha " } } },
|
||||
{ result: { response: { token: '<grok:render card_id="c1" card_type="citation_card" /> omega' } } },
|
||||
{
|
||||
result: {
|
||||
response: { token: '<grok:render card_id="c1" card_type="citation_card" /> omega' },
|
||||
},
|
||||
},
|
||||
{ result: { response: { modelResponse: { message: "Alpha omega" } } } },
|
||||
]);
|
||||
try {
|
||||
@@ -2040,10 +2284,39 @@ test("Streaming: maps structured Grok thinking to reasoning_content", async () =
|
||||
|
||||
test("Streaming: routes Grok thinking tokens separately from content", async () => {
|
||||
const restore = mockFetch(200, [
|
||||
{ result: { response: { token: "Thinking about your request", isThinking: true, messageTag: "header", messageStepId: 0 } } },
|
||||
{ result: { response: { token: "Buscando fecha de lanzamiento", isThinking: true, messageTag: "header" } } },
|
||||
{ result: { response: { token: "- Tool calls succeeded, confirming Ubuntu 24.04.4.\n", isThinking: true, messageTag: "summary" } } },
|
||||
{ result: { response: { token: "Tool call ejecutado.\nweb_search ejecutado correctamente.\n" } } },
|
||||
{
|
||||
result: {
|
||||
response: {
|
||||
token: "Thinking about your request",
|
||||
isThinking: true,
|
||||
messageTag: "header",
|
||||
messageStepId: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
result: {
|
||||
response: {
|
||||
token: "Buscando fecha de lanzamiento",
|
||||
isThinking: true,
|
||||
messageTag: "header",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
result: {
|
||||
response: {
|
||||
token: "- Tool calls succeeded, confirming Ubuntu 24.04.4.\n",
|
||||
isThinking: true,
|
||||
messageTag: "summary",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
result: {
|
||||
response: { token: "Tool call ejecutado.\nweb_search ejecutado correctamente.\n" },
|
||||
},
|
||||
},
|
||||
{ result: { response: { token: "Resultados clave:\n- Ubuntu 24.04.4 LTS liberado.\n" } } },
|
||||
{
|
||||
result: {
|
||||
@@ -2322,7 +2595,11 @@ test("Request: preserves selected mode and native search state when client tools
|
||||
function: {
|
||||
name: "public_search_tool",
|
||||
description: "Search the public web",
|
||||
parameters: { type: "object", properties: { query: { type: "string" } }, required: ["query"] },
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { query: { type: "string" } },
|
||||
required: ["query"],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
@@ -35,6 +35,7 @@ const LOCAL_SVG_IDS_WITHOUT_PROVENANCE = [
|
||||
"leonardo",
|
||||
"modal",
|
||||
"modelscope",
|
||||
"nimble-search",
|
||||
"nlpcloud",
|
||||
"oauth",
|
||||
"oci",
|
||||
@@ -177,9 +178,9 @@ const AUDITED_REFERENCE_FILES = [
|
||||
...referenceRoots.flatMap((directory) => collectTextFiles(join(root, directory))),
|
||||
];
|
||||
|
||||
test("provider bundle retires exactly the 78 unresolved assets and keeps the generic icon", () => {
|
||||
assert.equal(retiredAssetNames.length, 78);
|
||||
assert.equal(new Set(retiredAssetNames).size, 78);
|
||||
test("provider bundle retires exactly the 79 unresolved assets and keeps the generic icon", () => {
|
||||
assert.equal(retiredAssetNames.length, 79);
|
||||
assert.equal(new Set(retiredAssetNames).size, 79);
|
||||
|
||||
for (const assetName of retiredAssetNames) {
|
||||
assert.equal(
|
||||
@@ -196,8 +197,9 @@ test("provider bundle retires exactly the 78 unresolved assets and keeps the gen
|
||||
// provenance PRs (#11735, #11736, #11711) landed first and independently retired
|
||||
// 6 further unproven files this PR never targeted (freebuff-dark.svg,
|
||||
// freebuff-light.svg, freebuff.png, openvecta.svg, picoclaw.jpg, zoocode.png),
|
||||
// so the real remaining count is 142, not 148.
|
||||
assert.equal(distributedAssets.length, 142, "all 142 non-target assets must remain");
|
||||
// so the real pre-fix count was 142, not 148. This fix retires the unresolved
|
||||
// Nimble asset as well, leaving 141 distributed assets.
|
||||
assert.equal(distributedAssets.length, 141, "all 141 non-target assets must remain");
|
||||
assert.ok(distributedAssets.includes("cli-generic.svg"));
|
||||
});
|
||||
|
||||
|
||||
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`);
|
||||
}
|
||||
});
|
||||
42
tests/unit/rerank-providers-route.test.ts
Normal file
42
tests/unit/rerank-providers-route.test.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import assert from "node:assert/strict";
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import test from "node:test";
|
||||
import { NextRequest } from "next/server";
|
||||
|
||||
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-rerank-providers-route-"));
|
||||
process.env.DATA_DIR = TEST_DATA_DIR;
|
||||
|
||||
const core = await import("../../src/lib/db/core.ts");
|
||||
const { createProviderNode } = await import("../../src/lib/db/providers/nodes.ts");
|
||||
const rerankProvidersRoute = await import("../../src/app/api/memory/rerank-providers/route.ts");
|
||||
|
||||
test.after(() => {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
test("GET /api/memory/rerank-providers includes rerank-capable local provider nodes", async () => {
|
||||
await createProviderNode({
|
||||
id: "rerank-route-test-node",
|
||||
type: "openai-compatible",
|
||||
name: "Local reranker",
|
||||
prefix: "local-reranker",
|
||||
apiType: "rerank",
|
||||
baseUrl: "http://127.0.0.1:8099/v1",
|
||||
});
|
||||
|
||||
const response = await rerankProvidersRoute.GET(
|
||||
new NextRequest("http://localhost/api/memory/rerank-providers")
|
||||
);
|
||||
const body = await response.json();
|
||||
|
||||
assert.equal(response.status, 200);
|
||||
assert.deepEqual(
|
||||
body.providers.find(
|
||||
(provider: { provider?: string }) => provider.provider === "local-reranker"
|
||||
),
|
||||
{ provider: "local-reranker", hasKey: true, models: [] }
|
||||
);
|
||||
});
|
||||
@@ -120,6 +120,52 @@ test("injectSystemPrompt: null body returns as-is", () => {
|
||||
assert.equal(injectSystemPrompt(null), null);
|
||||
});
|
||||
|
||||
test("injectSystemPrompt: non-object bodies return as-is", () => {
|
||||
setSystemPromptConfig({ enabled: true, suffixPrompt: "test" });
|
||||
|
||||
for (const body of [undefined, "prompt", 42, true]) {
|
||||
assert.equal(injectSystemPrompt(body), body);
|
||||
}
|
||||
});
|
||||
|
||||
test("injectSystemPrompt: skips malformed message entries safely", () => {
|
||||
setSystemPromptConfig({ enabled: true, prefixPrompt: "PRE", suffixPrompt: "SUF" });
|
||||
const body = {
|
||||
messages: [
|
||||
{ role: "user", content: "hi" },
|
||||
null,
|
||||
{ role: "system", content: "Original prompt" },
|
||||
],
|
||||
};
|
||||
|
||||
const result = injectSystemPrompt(body);
|
||||
|
||||
assert.equal(result.messages[2].content, "PRE\n\nOriginal prompt\n\nSUF");
|
||||
assert.equal(result.messages[1], null);
|
||||
});
|
||||
|
||||
test("injectSystemPrompt: does not mutate the request or nested message content", () => {
|
||||
setSystemPromptConfig({ enabled: true, prefixPrompt: "PRE", suffixPrompt: "SUF" });
|
||||
const systemContent = [{ type: "text", text: "Original prompt" }];
|
||||
const systemMessage = { role: "system", content: systemContent };
|
||||
const body = {
|
||||
messages: [systemMessage, { role: "user", content: "hi" }],
|
||||
};
|
||||
|
||||
const result = injectSystemPrompt(body);
|
||||
|
||||
assert.notEqual(result, body);
|
||||
assert.notEqual(result.messages, body.messages);
|
||||
assert.notEqual(result.messages[0], systemMessage);
|
||||
assert.notEqual(result.messages[0].content, systemContent);
|
||||
assert.deepEqual(body, {
|
||||
messages: [
|
||||
{ role: "system", content: [{ type: "text", text: "Original prompt" }] },
|
||||
{ role: "user", content: "hi" },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
test("injectSystemPrompt: developer role treated as system", () => {
|
||||
setSystemPromptConfig({ enabled: true, prefixPrompt: "PRE", suffixPrompt: "SUF" });
|
||||
const body = {
|
||||
|
||||
@@ -54,6 +54,7 @@ const PROVIDER_IDS_WITHOUT_LOCAL_ASSET_PROVENANCE = [
|
||||
"leonardo",
|
||||
"modal",
|
||||
"modelscope",
|
||||
"nimble-search",
|
||||
"nlpcloud",
|
||||
"oauth",
|
||||
"oci",
|
||||
@@ -229,6 +230,7 @@ describe("ProviderIcon — local SVG dimensions", () => {
|
||||
it.each([
|
||||
["cline", "/providers/cline.svg"],
|
||||
["kimi-coding", "/providers/kimi-logomark-light.svg"],
|
||||
["opper", "/providers/opper.svg"],
|
||||
])("gives %s a definite square layout size", (providerId, expectedSrc) => {
|
||||
const container = renderIcon({ providerId, size: 24 });
|
||||
const img = container.querySelector(`img[src="${expectedSrc}"]`);
|
||||
@@ -244,8 +246,8 @@ describe("ProviderIcon — local SVG dimensions", () => {
|
||||
|
||||
describe("ProviderIcon — unresolved local asset provenance", () => {
|
||||
it("covers the complete provider and alias inventory", () => {
|
||||
expect(PROVIDER_IDS_WITHOUT_LOCAL_ASSET_PROVENANCE).toHaveLength(78);
|
||||
expect(new Set(PROVIDER_IDS_WITHOUT_LOCAL_ASSET_PROVENANCE)).toHaveLength(78);
|
||||
expect(PROVIDER_IDS_WITHOUT_LOCAL_ASSET_PROVENANCE).toHaveLength(79);
|
||||
expect(new Set(PROVIDER_IDS_WITHOUT_LOCAL_ASSET_PROVENANCE)).toHaveLength(79);
|
||||
});
|
||||
|
||||
it.each(PROVIDER_IDS_WITHOUT_LOCAL_ASSET_PROVENANCE)(
|
||||
|
||||
Reference in New Issue
Block a user