fix(ollama): route models by advertised capability (#11088)

Landed with the design call resolved per the owner's pick — **option 1**: the synced store is now endpoint-agnostic (persistDiscoveredModels and managedModelImport no longer drop non-chat models at write time), and chat selectability moved to read time (auto-pool expansion in autoStrategy applies filterChatSelectableModels; the models-route projection already had its chatOnly filter). Your discovery test now passes end-to-end (3/3): /api/show capabilities persist per connection and image/embedding requests route through the advertising host.

Reconciliation notes: conflicted areas merged onto the current tip (adobe discovery import, requestedModel preflight signature, resolvedProvider fast-path coexists with the synced-route override — explicit resolution wins); carried base-red drains (#10055 memoization, #11071 test variants) dropped as already-landed; the managed-model-import exclusion test was propagated to the new contract (image/video models persist; the read filter still hides them from chat pickers — pinned by a new assertion). Full battery: 205/206 focused (the one red is a confirmed periodic-timer timing flake on the loaded devbox — 20/20 isolated), autoCombo vitest 30/30, combo suites 46/46, gates + typecheck clean.

Thank you @yourspraveen — the capability probe + routing design was right; it just needed the store contract opened up. Fixes #11087.
This commit is contained in:
Praveen K Palaniswamy
2026-08-23 10:45:01 -04:00
committed by GitHub
parent c68cda7dfb
commit 65e81158ab
5094 changed files with 564668 additions and 80301 deletions

View File

@@ -7,6 +7,7 @@ import path from "node:path";
import { fileURLToPath } from "node:url";
import {
FREE_PROVIDERS,
NOAUTH_PROVIDERS,
OAUTH_PROVIDERS,
WEB_COOKIE_PROVIDERS,
APIKEY_PROVIDERS,
@@ -136,6 +137,7 @@ function buildHeader(total: number): string {
"## Categories",
"",
"- **Free** — free tier with API key (configured via dashboard)",
"- **No-auth** — public endpoints that require no key or sign-in at all",
"- **OAuth** — sign-in flow handled by OmniRoute, no API key needed",
"- **Web cookie** — wraps the provider's web app via cookie auth",
"- **API key** — paid provider configured via API key (free credits may apply)",
@@ -159,8 +161,19 @@ function buildHeader(total: number): string {
].join("\n");
}
function countExecutorImpls(): number {
const dir = path.join(ROOT, "open-sse", "executors");
const nonImpl = new Set(["index.ts", "index.mts", "types.ts", "base.ts", "constants.ts"]);
return fs
.readdirSync(dir)
.filter(
(f) => f.endsWith(".ts") && !f.endsWith(".test.ts") && !f.startsWith("__") && !nonImpl.has(f)
).length;
}
function main() {
const free = asRecords(FREE_PROVIDERS);
const noauth = asRecords(NOAUTH_PROVIDERS as Record<string, ProviderRecord>);
const oauth = asRecords(OAUTH_PROVIDERS);
const webCookie = asRecords(WEB_COOKIE_PROVIDERS);
const apiKey = asRecords(APIKEY_PROVIDERS);
@@ -173,6 +186,7 @@ function main() {
const allIds = new Set<string>([
...free.map((p) => p.id),
...noauth.map((p) => p.id),
...oauth.map((p) => p.id),
...webCookie.map((p) => p.id),
...apiKey.map((p) => p.id),
@@ -186,6 +200,7 @@ function main() {
const sections = [
buildSection("Free Tier (OAuth-first or no-key)", free, "Free"),
buildSection("No-auth Providers (no key required)", noauth, "No-auth"),
buildSection("OAuth Providers", oauth, "OAuth"),
buildSection("Web Cookie Providers", webCookie, "Web cookie"),
buildSection("API Key Providers (paid / paid-with-free-credits)", apiKey, "API key"),
@@ -202,7 +217,7 @@ function main() {
"",
"- Catalog: [`src/shared/constants/providers.ts`](../../src/shared/constants/providers.ts)",
"- Registry (per-model details): [`open-sse/config/providerRegistry.ts`](../../open-sse/config/providerRegistry.ts)",
"- Executors: [`open-sse/executors/`](../../open-sse/executors/) (31 files)",
`- Executors: [\`open-sse/executors/\`](../../open-sse/executors/) (${countExecutorImpls()} implementations)`,
"- Translators: [`open-sse/translator/`](../../open-sse/translator/)",
"",
"## See Also",
@@ -218,9 +233,10 @@ function main() {
console.log(`✓ Wrote ${OUT_FILE}`);
console.log(` Providers: ${allIds.size} unique IDs`);
console.log(
` Sections: free=${free.length}, oauth=${oauth.length}, web=${webCookie.length}, ` +
`apikey=${apiKey.length}, local=${local.length}, search=${search.length}, ` +
`audio=${audio.length}, proxy=${upstreamProxy.length}, cloud=${cloudAgent.length}, system=${system.length}`
` Sections: free=${free.length}, noauth=${noauth.length}, oauth=${oauth.length}, ` +
`web=${webCookie.length}, apikey=${apiKey.length}, local=${local.length}, ` +
`search=${search.length}, audio=${audio.length}, proxy=${upstreamProxy.length}, ` +
`cloud=${cloudAgent.length}, system=${system.length}`
);
}

View File

@@ -1,143 +0,0 @@
#!/usr/bin/env node
// One-shot: FASE 3 helper, safe to delete after merge.
//
// Moves existing i18n mirror docs from `docs/i18n/<lang>/docs/X.md` into the
// matching subfolder `docs/i18n/<lang>/docs/<sub>/X.md`, mirroring the new
// docs/ layout. Uses `git mv` to preserve history.
//
// Usage:
// node scripts/docs/move-i18n-mirrors.mjs [--dry]
//
// Notes:
// - Skips files that don't appear in DOC_TO_SUBFOLDER (e.g., the legacy
// `cloudflare-zero-trust-guide.md` or `features/` subfolder — those will be
// handled in FASE 5 when translations are regenerated).
// - Idempotent: if the target already lives under a subfolder, the entry is
// skipped.
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { execFileSync } from "node:child_process";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const ROOT = path.resolve(__dirname, "..", "..");
const I18N_DIR = path.join(ROOT, "docs", "i18n");
const DRY = process.argv.includes("--dry");
const DOC_TO_SUBFOLDER = {
// architecture
"ARCHITECTURE.md": "architecture",
"CODEBASE_DOCUMENTATION.md": "architecture",
"REPOSITORY_MAP.md": "architecture",
"AUTHZ_GUIDE.md": "architecture",
"RESILIENCE_GUIDE.md": "architecture",
// guides
"SETUP_GUIDE.md": "guides",
"USER_GUIDE.md": "guides",
"DOCKER_GUIDE.md": "guides",
"ELECTRON_GUIDE.md": "guides",
"TERMUX_GUIDE.md": "guides",
"PWA_GUIDE.md": "guides",
"TROUBLESHOOTING.md": "guides",
"UNINSTALL.md": "guides",
"I18N.md": "guides",
"FEATURES.md": "guides",
// reference
"API_REFERENCE.md": "reference",
"PROVIDER_REFERENCE.md": "reference",
"openapi.yaml": "reference",
"ENVIRONMENT.md": "reference",
"CLI-TOOLS.md": "reference",
"FREE_TIERS.md": "reference",
// frameworks
"MCP-SERVER.md": "frameworks",
"A2A-SERVER.md": "frameworks",
"AGENT_PROTOCOLS_GUIDE.md": "frameworks",
"CLOUD_AGENT.md": "frameworks",
"SKILLS.md": "frameworks",
"MEMORY.md": "frameworks",
"WEBHOOKS.md": "frameworks",
"EVALS.md": "frameworks",
// routing
"AUTO-COMBO.md": "routing",
"REASONING_REPLAY.md": "routing",
// security
"GUARDRAILS.md": "security",
"COMPLIANCE.md": "security",
"STEALTH_GUIDE.md": "security",
// compression
"COMPRESSION_GUIDE.md": "compression",
"COMPRESSION_ENGINES.md": "compression",
"COMPRESSION_RULES_FORMAT.md": "compression",
"COMPRESSION_LANGUAGE_PACKS.md": "compression",
"RTK_COMPRESSION.md": "compression",
// ops
"RELEASE_CHECKLIST.md": "ops",
"COVERAGE_PLAN.md": "ops",
"FLY_IO_DEPLOYMENT_GUIDE.md": "ops",
"VM_DEPLOYMENT_GUIDE.md": "ops",
"PROXY_GUIDE.md": "ops",
"TUNNELS_GUIDE.md": "ops",
};
let moved = 0;
let skipped = 0;
const seenLocales = [];
for (const locale of fs.readdirSync(I18N_DIR)) {
const localeDir = path.join(I18N_DIR, locale);
const stat = fs.statSync(localeDir);
if (!stat.isDirectory()) continue;
const docsDir = path.join(localeDir, "docs");
if (!fs.existsSync(docsDir)) continue;
seenLocales.push(locale);
for (const fname of fs.readdirSync(docsDir)) {
const sub = DOC_TO_SUBFOLDER[fname];
if (!sub) continue; // not in our mapping (e.g. features/, cloudflare-zero-trust-guide.md)
const src = path.join(docsDir, fname);
if (!fs.statSync(src).isFile()) continue;
const subDir = path.join(docsDir, sub);
const dst = path.join(subDir, fname);
if (fs.existsSync(dst)) {
skipped++;
continue;
}
if (DRY) {
console.log(`would move: ${path.relative(ROOT, src)} -> ${path.relative(ROOT, dst)}`);
moved++;
continue;
}
if (!fs.existsSync(subDir)) fs.mkdirSync(subDir, { recursive: true });
const relSrc = path.relative(ROOT, src);
const relDst = path.relative(ROOT, dst);
try {
execFileSync("git", ["mv", "-k", "--", relSrc, relDst], {
cwd: ROOT,
stdio: "pipe",
});
moved++;
} catch {
// fallback: copy + delete; emulate `|| true` for the rm by ignoring its failure
fs.renameSync(src, dst);
try {
execFileSync("git", ["rm", "--cached", "--", relSrc], { cwd: ROOT, stdio: "pipe" });
} catch {
// file may not be tracked yet — safe to ignore
}
execFileSync("git", ["add", "--", relDst], { cwd: ROOT, stdio: "pipe" });
moved++;
}
}
}
console.log(
`[i18n-mirrors] locales=${seenLocales.length} moved=${moved} skipped=${skipped}${DRY ? " (dry-run)" : ""}`
);