openvibers/openvibe.ai Commits
Add eleven cheap hosted inference providers (disabled) (#444)
Changes
@@@ -314,6 +314,116 @@"terms_url": "Various OS licenses","terms_effective": null,"checked": "2026-11-03"++ },++ {++ "provider": "Parasail",++ "api_format": "OpenAI-compatible",++ "existing_adapter": "openai",++ "byok": null,++ "access_mode_default": "UNVERIFIED",++ "resale_terms_quote": "No resale clause verified; terms silent on retention and training.",++ "terms_url": "https://docs.parasail.io/parasail-docs/products/overview/models",++ "terms_effective": null,++ "checked": "2026-11-10"++ },++ {++ "provider": "Particle AI (particle.ai)",++ "api_format": "OpenAI-compatible",++ "existing_adapter": "openai",++ "byok": null,++ "access_mode_default": "UNVERIFIED",++ "resale_terms_quote": "No resale clause verified; terms silent on retention and training.",++ "terms_url": "https://docs.particle.ai/",++ "terms_effective": null,++ "checked": "2026-11-10"++ },++ {++ "provider": "Inference.net",++ "api_format": "OpenAI-compatible",++ "existing_adapter": "openai",++ "byok": null,++ "access_mode_default": "UNVERIFIED",++ "resale_terms_quote": "No resale clause verified; terms silent on retention and training.",++ "terms_url": "https://docs.inference.net/",++ "terms_effective": null,++ "checked": "2026-11-10"++ },++ {++ "provider": "AtlasCloud",++ "api_format": "OpenAI-compatible",++ "existing_adapter": "openai",++ "byok": null,++ "access_mode_default": "UNVERIFIED",++ "resale_terms_quote": "No resale clause verified; terms silent on retention and training.",++ "terms_url": "https://www.atlascloud.ai/docs",++ "terms_effective": null,++ "checked": "2026-11-10"++ },++ {++ "provider": "Avian (avian.io)",++ "api_format": "OpenAI-compatible",++ "existing_adapter": "openai",++ "byok": null,++ "access_mode_default": "UNVERIFIED",++ "resale_terms_quote": "No resale clause verified; terms silent on retention and training.",++ "terms_url": "https://avian.io/docs/",++ "terms_effective": null,++ "checked": "2026-11-10"++ },++ {++ "provider": "NanoGPT",++ "api_format": "OpenAI-compatible",++ "existing_adapter": "openai",++ "byok": null,++ "access_mode_default": "UNVERIFIED",++ "resale_terms_quote": "PAYG terms explicitly allow reselling access to the Services as part of your own service offerings; subscription use prohibits commercial/resale use. No executed wholesale agreement, so still UNVERIFIED.",++ "terms_url": "https://nano-gpt.com/pricing",++ "terms_effective": null,++ "checked": "2026-11-10"++ },++ {++ "provider": "Infermatic",++ "api_format": "OpenAI-compatible",++ "existing_adapter": "openai",++ "byok": null,++ "access_mode_default": "UNVERIFIED",++ "resale_terms_quote": "No resale clause verified; terms silent on retention and training.",++ "terms_url": "https://ui.infermatic.ai/docs",++ "terms_effective": null,++ "checked": "2026-11-10"++ },++ {++ "provider": "ArliAI",++ "api_format": "OpenAI-compatible",++ "existing_adapter": "openai",++ "byok": null,++ "access_mode_default": "UNVERIFIED",++ "resale_terms_quote": "No resale clause verified; terms silent on retention and training.",++ "terms_url": "https://www.arliai.com/",++ "terms_effective": null,++ "checked": "2026-11-10"++ },++ {++ "provider": "Friendli",++ "api_format": "OpenAI-compatible",++ "existing_adapter": "openai",++ "byok": null,++ "access_mode_default": "UNVERIFIED",++ "resale_terms_quote": "No resale clause verified; terms silent on retention and training.",++ "terms_url": "https://friendli.ai/docs/guides/model-apis/introduction",++ "terms_effective": null,++ "checked": "2026-11-10"++ },++ {++ "provider": "Scaleway Generative APIs",++ "api_format": "OpenAI-compatible",++ "existing_adapter": "openai",++ "byok": null,++ "access_mode_default": "UNVERIFIED",++ "resale_terms_quote": "No resale clause verified; terms silent on retention and training.",++ "terms_url": "https://www.scaleway.com/en/developers/api/generative-apis",++ "terms_effective": null,++ "checked": "2026-11-10"}]}@@@ -65,6 +65,23 @@ These presets are seeded disabled in practice until the owner adds a provider ke| Baseten | `https://inference.baseten.co/v1` | Retention/training unstated | `UNVERIFIED` | [Pricing](https://www.baseten.co/pricing/) || Hyperbolic | `https://api.hyperbolic.xyz/v1` | Retention/training unstated | `UNVERIFIED` | [Inference pricing](https://docs.hyperbolic.xyz/docs/hyperbolic-ai-inference-pricing) |++## Cheap hosted inference added 2026-11-10++++Endpoints, keyless model lists and key requirements were probed on 2026-11-10 (no account, no key, no paid call). Retention, training and resale terms are unstated for all ten; NanoGPT's terms alone explicitly allow reselling PAYG access, which still keeps its default `UNVERIFIED` until an executed agreement exists. Prices are not read by the price book for these ten (it takes per-1M figures; the catalogue sync takes the per-token strings Inference.net, AtlasCloud and Friendli publish). Avian publishes per-million figures the existing sync cannot read, so its prices stay on the owner's panel.++++| Provider | Base URL | Privacy facts confirmed | Access default | Own provider listing / terms |++|---|---|---|---|---|++| Parasail | `https://api.parasail.io/v1` | Retention/training unstated; model list needs a key | `UNVERIFIED` | [Model docs](https://docs.parasail.io/parasail-docs/products/overview/models) |++| Particle AI | `https://api.particle.ai/v1` | Retention/training unstated; model list needs a key | `UNVERIFIED` | [Docs](https://docs.particle.ai/) |++| Inference.net | `https://api.inference.net/v1` | Retention/training unstated; keyless model list with per-token prices | `UNVERIFIED` | [Docs](https://docs.inference.net/) |++| AtlasCloud | `https://api.atlascloud.ai/v1` | Retention/training unstated; keyless model list with per-token prices | `UNVERIFIED` | [Docs](https://www.atlascloud.ai/docs) |++| Avian | `https://api.avian.io/v1` | Retention/training unstated; keyless model list, per-million prices not readable by sync | `UNVERIFIED` | [Docs](https://avian.io/docs/) |++| NanoGPT | `https://api.nano-gpt.com/v1` | Terms allow reselling PAYG access (no markup); keyless model list carries no prices | `UNVERIFIED` | [Pricing](https://nano-gpt.com/pricing) |++| Infermatic | `https://api.totalgpt.ai/v1` | Retention/training unstated; model list needs a key | `UNVERIFIED` | [Docs](https://ui.infermatic.ai/docs) |++| ArliAI | `https://api.arliai.com/v1` | Retention/training unstated; keyless custom model list at `/model/all` | `UNVERIFIED` | [Site](https://www.arliai.com/) |++| Friendli | `https://api.friendli.ai/serverless/v1` | Retention/training unstated; keyless model list with per-token prices | `UNVERIFIED` | [Model APIs intro](https://friendli.ai/docs/guides/model-apis/introduction) |++| Scaleway | `https://api.scaleway.ai/v1` | Retention/training unstated; model list needs a key | `UNVERIFIED` | [Generative APIs](https://www.scaleway.com/en/developers/api/generative-apis) |++## Cheapest confirmed open-weight pricesThese are the lowest prices confirmed among the listed provider-owned source pages; an unpriced provider is not assumed to cost more. Cached prices are `—` when the provider does not publish one.@@@ -48,9 +48,21 @@ Required-provider documentation was checked on 2026-11-10. Meta now documents Mo| SearXNG (searxng) | adapter / http | Owner-configured | Not applicable | [Source](https://docs.searxng.org/dev/search_api.html) || Openverse (openverse) | adapter / http | https://api.openverse.org/v1 | Not applicable | [Source](https://api.openverse.org/) || Wikimedia Commons (commons) | adapter / http | https://commons.wikimedia.org/w/api.php | Not applicable | [Source](https://www.mediawiki.org/wiki/API:Search) |++| Parasail (parasail) | openai / openai | https://api.parasail.io/v1 | https://api.parasail.io/v1/models (key required) | [Source](https://docs.parasail.io/parasail-docs/products/overview/models) |++| Particle AI (particle-ai) | openai / openai | https://api.particle.ai/v1 | https://api.particle.ai/v1/models (key required) | [Source](https://docs.particle.ai/) |++| Inference.net (inference-net) | openai / openai | https://api.inference.net/v1 | https://api.inference.net/v1/models (keyless) | [Source](https://docs.inference.net/) |++| AtlasCloud (atlas-cloud) | openai / openai | https://api.atlascloud.ai/v1 | https://api.atlascloud.ai/v1/models (keyless) | [Source](https://www.atlascloud.ai/docs) |++| Avian (avian) | openai / openai | https://api.avian.io/v1 | https://api.avian.io/v1/models (keyless) | [Source](https://avian.io/docs/) |++| NanoGPT (nanogpt) | openai / openai | https://api.nano-gpt.com/v1 | https://api.nano-gpt.com/v1/models (keyless, no prices) | [Source](https://nano-gpt.com/pricing) |++| Infermatic (infermatic) | openai / openai | https://api.totalgpt.ai/v1 | https://api.totalgpt.ai/v1/models (key required) | [Source](https://ui.infermatic.ai/docs) |++| ArliAI (arliai) | openai / openai | https://api.arliai.com/v1 | https://api.arliai.com/model/all (keyless) | [Source](https://www.arliai.com/) |++| Friendli (friendli) | openai / openai | https://api.friendli.ai/serverless/v1 | https://api.friendli.ai/serverless/v1/models (keyless) | [Source](https://friendli.ai/docs/guides/model-apis/introduction) |++| Scaleway (scaleway) | openai / openai | https://api.scaleway.ai/v1 | https://api.scaleway.ai/v1/models (key required) | [Source](https://www.scaleway.com/en/developers/api/generative-apis) |Search capabilities use web-search and image-search, matching the shared placement capability syntax. Brave web and images share one provider/key, as do Serper web and images. Openverse and Wikimedia Commons require no key. SearXNG has no invented public address; the owner supplies their self-hosted base.++Cheap hosted inference (2026-11-10, owner request): the ten providers added at the end of the table are seeded disabled with a `vault:<key>` slot and no key, exactly like the existing pool rows, so they take part in model sync, circuits, failover order and capability memberships as soon as the owner puts a key in the console and enables them. The catalogue sync (`server/providers/catalog.js`) reads the per-token prices each model list carries and imports them into new models (Inference.net, AtlasCloud and Friendli carry per-token strings; the price book only reads per-1M figures, so it stays out of these lists). Parasail, Particle AI, Infermatic and Scaleway need a key for their model lists, so sync runs after the owner adds one; NanoGPT's list carries no prices, and Avian publishes per-million figures the existing parsers cannot read — both stay on the owner's price panel. Skipped, not guessed: kluster.ai (services sunset 2026-06-09), Lambda's hosted inference (winding down; `api.lambda.com` does not answer), Targon (`api.targon.com/v1/models` answers 410 Gone), GMI Cloud, nCompass and Crusoe (no docs-confirmed plain OpenAI chat base URL today), Mancer (its model list carries credit-unit prices whose dollar value nobody has verified; sync would import them as per-token dollars), and OVHcloud AI Endpoints (per-model regional URLs under `endpoints.kepler.ai.cloud.ovh.net`, no single base URL). Two research notes disagree with the live docs and the docs win: Infermatic's base is `api.totalgpt.ai/v1`, not `ui.infermatic.ai/v1` (prices.json), and Friendli's serverless base `api.friendli.ai/serverless/v1` is published in its docs today.++For Fable: the existing providers console now receives all seeded provider rows and protocol metadata. Its visual grouping and capability presentation remain for the main session.Owner key controls use the existing encrypted `ai.provider_keys` store. The console JSON exposes only presence,@@@ -716,6 +716,30 @@ const PROVIDERS = [{ key: 'commons', display_name: 'Wikimedia Commons', kind: 'http', base_url: 'https://commons.wikimedia.org/w/api.php', auth_mode: 'none', models_endpoint: null,capabilities: ['image-search'], search_adapters: ['commons'], docs_url: 'https://www.mediawiki.org/wiki/API:Search' },++ // Cheap hosted inference (owner, 2026-11-10): every endpoint below was read and probed keyless today.++ // Each starts disabled with no key; the owner puts its key in the owner settings and syncs its model list.++ { key: 'parasail', display_name: 'Parasail', base_url: 'https://api.parasail.io/v1',++ docs_url: 'https://docs.parasail.io/parasail-docs/products/overview/models', capabilities: ['chat', 'embed'] },++ { key: 'particle-ai', display_name: 'Particle AI', base_url: 'https://api.particle.ai/v1',++ docs_url: 'https://docs.particle.ai/', capabilities: ['chat'] },++ { key: 'inference-net', display_name: 'Inference.net', base_url: 'https://api.inference.net/v1', models_public: true,++ docs_url: 'https://docs.inference.net/', capabilities: ['chat'] },++ { key: 'atlas-cloud', display_name: 'AtlasCloud', base_url: 'https://api.atlascloud.ai/v1', models_public: true,++ docs_url: 'https://www.atlascloud.ai/docs', capabilities: ['chat'] },++ { key: 'avian', display_name: 'Avian', base_url: 'https://api.avian.io/v1', models_public: true,++ docs_url: 'https://avian.io/docs/', capabilities: ['chat'] },++ { key: 'nanogpt', display_name: 'NanoGPT', base_url: 'https://api.nano-gpt.com/v1', models_public: true,++ docs_url: 'https://nano-gpt.com/pricing', capabilities: ['chat'] },++ { key: 'infermatic', display_name: 'Infermatic', base_url: 'https://api.totalgpt.ai/v1',++ docs_url: 'https://ui.infermatic.ai/docs', capabilities: ['chat'] },++ { key: 'arliai', display_name: 'ArliAI', base_url: 'https://api.arliai.com/v1',++ models_endpoint: 'https://api.arliai.com/model/all', models_public: true,++ docs_url: 'https://www.arliai.com/', capabilities: ['chat', 'embed', 'image'] },++ { key: 'friendli', display_name: 'Friendli', base_url: 'https://api.friendli.ai/serverless/v1', models_public: true,++ docs_url: 'https://friendli.ai/docs/guides/model-apis/introduction', capabilities: ['chat'] },++ { key: 'scaleway', display_name: 'Scaleway', base_url: 'https://api.scaleway.ai/v1',++ docs_url: 'https://www.scaleway.com/en/developers/api/generative-apis', capabilities: ['chat', 'embed'] },++];// The seed, console and personal-key forms consume these same records.@@@ -33,7 +33,9 @@ const DOMAIN = {openrouter: 'openrouter.ai', venice: 'venice.ai', abliteratedmodels: 'abliteratedmodels.cc', uplinkswitch: 'uplinkswitch.ai',chutes: 'chutes.ai', siliconflow: 'siliconflow.com', nebius: 'nebius.com', together: 'together.ai',fireworks: 'fireworks.ai', groq: 'groq.com', cerebras: 'cerebras.ai', sambanova: 'sambanova.ai',-- baseten: 'baseten.co', hyperbolic: 'hyperbolic.xyz',++ baseten: 'baseten.co', hyperbolic: 'hyperbolic.xyz', parasail: 'parasail.io', 'particle-ai': 'particle.ai',++ 'inference-net': 'inference.net', 'atlas-cloud': 'atlascloud.ai', avian: 'avian.io',++ nanogpt: 'nano-gpt.com', infermatic: 'totalgpt.ai', arliai: 'arliai.com', friendli: 'friendli.ai', scaleway: 'scaleway.com',};const onItsOwnSite = (url, key) => {const host = new URL(url).hostname;@@@ -47,7 +49,7 @@ t.test('provider presets are OpenAI-compatible, with a base URL, a timeout, an aassert.deepStrictEqual(pool.PROVIDERS.slice(0, 23).map((p) => p.key),['qwen', 'kimi', 'glm', 'minimax', 'deepinfra', 'novita', 'featherless', 'refuseless', 'abliteration', 'openrouter', 'venice', 'abliteratedmodels', 'uplinkswitch', 'chutes', 'siliconflow', 'nebius', 'together', 'fireworks', 'groq', 'cerebras', 'sambanova', 'baseten', 'hyperbolic']);assert.deepStrictEqual(pool.PROVIDER_KEYS, pool.PROVIDERS.map((p) => p.key));-- assert.strictEqual(new Set(pool.PROVIDER_KEYS).size, 40);++ assert.strictEqual(new Set(pool.PROVIDER_KEYS).size, 50);assert.strictEqual(pool.byKey.get('meta').base_url, 'https://api.meta.ai/v1');assert.strictEqual(pool.byKey.get('perplexity').kind, 'responses');assert.strictEqual(require('../server/console/presets').credentialProvider('groq').model, 'openai/gpt-oss-20b', 'existing personal-key default is preserved');nnew file mode 100644@@@ -0,0 +1,96 @@++'use strict';++/**++ * The cheap hosted inference providers added 2026-11-10 (each endpoint read and probed keyless that day).++ * Every new row starts disabled with no key and an https endpoint on its own documented host; sync imports++ * a recorded models list (no network in tests) with prices from the provider's own list, models off.++ */++const assert = require('assert');++const { boot, request, token, suite } = require('./helpers');++const { PROVIDERS } = require('../server/providers/pool-providers');++const seed = require('../server/providers/pool-seed');++++const t = suite('cheap-providers');++const owner = token('exchange', ['ai.provider.manage', 'ai.usage.read']);++++const NEW_KEYS = ['parasail', 'particle-ai', 'inference-net', 'atlas-cloud', 'avian', 'nanogpt', 'infermatic', 'arliai', 'friendli', 'scaleway'];++const HOST = {++ parasail: 'api.parasail.io', 'particle-ai': 'api.particle.ai', 'inference-net': 'api.inference.net',++ 'atlas-cloud': 'api.atlascloud.ai', avian: 'api.avian.io', nanogpt: 'api.nano-gpt.com',++ infermatic: 'api.totalgpt.ai', arliai: 'api.arliai.com', friendli: 'api.friendli.ai', scaleway: 'api.scaleway.ai',++};++const KEYLESS = ['inference-net', 'atlas-cloud', 'avian', 'nanogpt', 'arliai', 'friendli'];++++const json = (body, status = 200) => new Response(JSON.stringify(body), { status, headers: { 'content-type': 'application/json' } });++async function fetchImpl(input) {++ const url = String(input);++ if (url === 'https://api.inference.net/v1/models') return json({ object: 'list', data: [++ { id: 'llama-3.3-70b', object: 'model', context_length: 131072, pricing: { prompt: '0.00000013', completion: '0.00000040', input_cache_read: '0.00000001' } },++ { id: 'glm-5.3-flash', object: 'model', context_length: 1048576, pricing: { prompt: '0.00000010', completion: '0.00000045' } },++ ] });++ if (url === 'https://api.atlascloud.ai/v1/models') return json({ code: 200, msg: 'succeed', data: [++ { id: 'deepseek-ai/DeepSeek-V3.2-Exp', object: 'model', context_length: 163840, quantization: 'fp8',++ pricing: { prompt: '0.00000026', completion: '0.00000038', input_cache_read: '0.00000013' } },++ ] });++ return json({}, 404);++}++++t.test('every new row: https endpoint on its own host, off, no key', () => {++ for (const key of NEW_KEYS) {++ const p = PROVIDERS.find((x) => x.key === key);++ assert.ok(p, `${key} is catalogued`);++ assert.strictEqual(new URL(p.base_url).hostname, HOST[key], `${key} base host`);++ assert.ok(p.base_url.startsWith('https://'), `${key} https`);++ assert.ok(p.metadata === undefined || typeof p === 'object', `${key} record`);++ const row = seed.recordOf(p);++ assert.strictEqual(row.status, 'disabled', `${key} starts off`);++ assert.strictEqual(row.secret_ref, `vault:${key}`, `${key} key slot named, no key`);++ assert.strictEqual(row.metadata.api_family, 'openai', `${key} OpenAI wire format`);++ assert.ok(new URL(row.metadata.models_endpoint).hostname === HOST[key] || key === 'arliai', `${key} models on its own host`);++ }++ // Key-required lists stay out of the keyless sync, keyless ones join it.++ for (const key of ['parasail', 'particle-ai', 'infermatic', 'scaleway'])++ assert.notStrictEqual(PROVIDERS.find((x) => x.key === key).models_public, true, `${key} needs a key for its list`);++ for (const key of KEYLESS)++ assert.strictEqual(PROVIDERS.find((x) => x.key === key).models_public, true, `${key} list answers keyless`);++});++++let h;++t.test('boot seeds the new providers disabled and credentialless; sync imports prices from the recorded list', async () => {++ h = await boot({ fetchImpl, env: { AI_USER_MODULES: 'off' } });++ for (const key of NEW_KEYS) {++ const p = await h.registry.getProvider(key);++ assert.ok(p, `${key} seeded`);++ assert.strictEqual(p.status, 'disabled', `${key} off`);++ assert.strictEqual(p.secret_ref, `vault:${key}`, `${key} key slot`);++ assert.strictEqual(p.metadata.models_public === true, KEYLESS.includes(key), `${key} keyless list flag`);++ }++ const pub = await request(h.base, 'GET', '/api/v1/providers/inference-net', { tok: owner });++ assert.strictEqual(pub.body.provider.credentials, 'missing', 'no key configured');++ assert.strictEqual(pub.body.provider.secret_ref, 'vault:inference-net', 'only the slot name is exposed, never a value');++++ const syncOpts = { actor: 'cheap-providers-test' };++ const meta = { models_endpoint: 'https://api.inference.net/v1/models' };++ const provider = await h.registry.getProvider('inference-net');++ const { sync } = require('../server/providers/catalog');++ const result = await sync({ registry: h.registry, provider: { ...provider, metadata: { ...provider.metadata, ...meta } }, apiKey: null, fetchImpl, actor: 'cheap-providers-test' });++ assert.deepStrictEqual(result.added.sort(), ['glm-5.3-flash', 'llama-3.3-70b']);++ const llama = await h.registry.getModel('inference-net', 'llama-3.3-70b');++ assert.strictEqual(llama.status, 'disabled', 'synced models arrive off');++ const near = (a, b) => Math.abs(a - b) < 1e-9;++ assert.ok(near(llama.cost.in_per_mtok, 0.13), `prompt per-token string read as per-MTok, got ${llama.cost.in_per_mtok}`);++ assert.ok(near(llama.cost.out_per_mtok, 0.40), `got ${llama.cost.out_per_mtok}`);++ assert.ok(near(llama.cost.cached_per_mtok, 0.01), `got ${llama.cost.cached_per_mtok}`);++ assert.strictEqual(llama.cost.source, 'https://api.inference.net/v1/models', 'price source is the provider list');++ assert.strictEqual(llama.context_window, 131072);++++ const atlas = await h.registry.getProvider('atlas-cloud');++ const atlasResult = await sync({ registry: h.registry, provider: { ...atlas, metadata: { ...atlas.metadata, models_endpoint: 'https://api.atlascloud.ai/v1/models' } }, apiKey: null, fetchImpl, actor: 'cheap-providers-test' });++ assert.deepStrictEqual(atlasResult.added, ['deepseek-ai/DeepSeek-V3.2-Exp']);++ const deepseek = await h.registry.getModel('atlas-cloud', 'deepseek-ai/DeepSeek-V3.2-Exp');++ assert.strictEqual(deepseek.status, 'disabled');++ assert.ok(near(deepseek.cost.in_per_mtok, 0.26), `got ${deepseek.cost.in_per_mtok}`);++ assert.ok(near(deepseek.cost.out_per_mtok, 0.38), `got ${deepseek.cost.out_per_mtok}`);++ assert.strictEqual(deepseek.context_window, 163840);++});++++t.run(() => h && h.stop());
@@ -314,6 +314,116 @@
@@ -314,6 +314,116 @@
"terms_url": "Various OS licenses",
"terms_url": "Various OS licenses",
"terms_effective": null,
"terms_effective": null,
"checked": "2026-11-03"
"checked": "2026-11-03"
+ },
+ {
+ "provider": "Parasail",
+ "api_format": "OpenAI-compatible",
+ "existing_adapter": "openai",
+ "byok": null,
+ "access_mode_default": "UNVERIFIED",
+ "resale_terms_quote": "No resale clause verified; terms silent on retention and training.",
+ "terms_url": "https://docs.parasail.io/parasail-docs/products/overview/models",
+ "terms_effective": null,
+ "checked": "2026-11-10"
+ },
+ {
+ "provider": "Particle AI (particle.ai)",
+ "api_format": "OpenAI-compatible",
+ "existing_adapter": "openai",
+ "byok": null,
+ "access_mode_default": "UNVERIFIED",
+ "resale_terms_quote": "No resale clause verified; terms silent on retention and training.",
+ "terms_url": "https://docs.particle.ai/",
+ "terms_effective": null,
+ "checked": "2026-11-10"
+ },
+ {
+ "provider": "Inference.net",
+ "api_format": "OpenAI-compatible",
+ "existing_adapter": "openai",
+ "byok": null,
+ "access_mode_default": "UNVERIFIED",
+ "resale_terms_quote": "No resale clause verified; terms silent on retention and training.",
+ "terms_url": "https://docs.inference.net/",
+ "terms_effective": null,
+ "checked": "2026-11-10"
+ },
+ {
+ "provider": "AtlasCloud",
+ "api_format": "OpenAI-compatible",
+ "existing_adapter": "openai",
+ "byok": null,
+ "access_mode_default": "UNVERIFIED",
+ "resale_terms_quote": "No resale clause verified; terms silent on retention and training.",
+ "terms_url": "https://www.atlascloud.ai/docs",
+ "terms_effective": null,
+ "checked": "2026-11-10"
+ },
+ {
+ "provider": "Avian (avian.io)",
+ "api_format": "OpenAI-compatible",
+ "existing_adapter": "openai",
+ "byok": null,
+ "access_mode_default": "UNVERIFIED",
+ "resale_terms_quote": "No resale clause verified; terms silent on retention and training.",
+ "terms_url": "https://avian.io/docs/",
+ "terms_effective": null,
+ "checked": "2026-11-10"
+ },
+ {
+ "provider": "NanoGPT",
+ "api_format": "OpenAI-compatible",
+ "existing_adapter": "openai",
+ "byok": null,
+ "access_mode_default": "UNVERIFIED",
+ "resale_terms_quote": "PAYG terms explicitly allow reselling access to the Services as part of your own service offerings; subscription use prohibits commercial/resale use. No executed wholesale agreement, so still UNVERIFIED.",
+ "terms_url": "https://nano-gpt.com/pricing",
+ "terms_effective": null,
+ "checked": "2026-11-10"
+ },
+ {
+ "provider": "Infermatic",
+ "api_format": "OpenAI-compatible",
+ "existing_adapter": "openai",
+ "byok": null,
+ "access_mode_default": "UNVERIFIED",
+ "resale_terms_quote": "No resale clause verified; terms silent on retention and training.",
+ "terms_url": "https://ui.infermatic.ai/docs",
+ "terms_effective": null,
+ "checked": "2026-11-10"
+ },
+ {
+ "provider": "ArliAI",
+ "api_format": "OpenAI-compatible",
+ "existing_adapter": "openai",
+ "byok": null,
+ "access_mode_default": "UNVERIFIED",
+ "resale_terms_quote": "No resale clause verified; terms silent on retention and training.",
+ "terms_url": "https://www.arliai.com/",
+ "terms_effective": null,
+ "checked": "2026-11-10"
+ },
+ {
+ "provider": "Friendli",
+ "api_format": "OpenAI-compatible",
+ "existing_adapter": "openai",
+ "byok": null,
+ "access_mode_default": "UNVERIFIED",
+ "resale_terms_quote": "No resale clause verified; terms silent on retention and training.",
+ "terms_url": "https://friendli.ai/docs/guides/model-apis/introduction",
+ "terms_effective": null,
+ "checked": "2026-11-10"
+ },
+ {
+ "provider": "Scaleway Generative APIs",
+ "api_format": "OpenAI-compatible",
+ "existing_adapter": "openai",
+ "byok": null,
+ "access_mode_default": "UNVERIFIED",
+ "resale_terms_quote": "No resale clause verified; terms silent on retention and training.",
+ "terms_url": "https://www.scaleway.com/en/developers/api/generative-apis",
+ "terms_effective": null,
+ "checked": "2026-11-10"
}
}
]
]
}
}
@@ -65,6 +65,23 @@ These presets are seeded disabled in practice until the owner adds a provider ke
@@ -65,6 +65,23 @@ These presets are seeded disabled in practice until the owner adds a provider ke
| Baseten | `https://inference.baseten.co/v1` | Retention/training unstated | `UNVERIFIED` | [Pricing](https://www.baseten.co/pricing/) |
| Baseten | `https://inference.baseten.co/v1` | Retention/training unstated | `UNVERIFIED` | [Pricing](https://www.baseten.co/pricing/) |
| Hyperbolic | `https://api.hyperbolic.xyz/v1` | Retention/training unstated | `UNVERIFIED` | [Inference pricing](https://docs.hyperbolic.xyz/docs/hyperbolic-ai-inference-pricing) |
| Hyperbolic | `https://api.hyperbolic.xyz/v1` | Retention/training unstated | `UNVERIFIED` | [Inference pricing](https://docs.hyperbolic.xyz/docs/hyperbolic-ai-inference-pricing) |
+## Cheap hosted inference added 2026-11-10
+
+Endpoints, keyless model lists and key requirements were probed on 2026-11-10 (no account, no key, no paid call). Retention, training and resale terms are unstated for all ten; NanoGPT's terms alone explicitly allow reselling PAYG access, which still keeps its default `UNVERIFIED` until an executed agreement exists. Prices are not read by the price book for these ten (it takes per-1M figures; the catalogue sync takes the per-token strings Inference.net, AtlasCloud and Friendli publish). Avian publishes per-million figures the existing sync cannot read, so its prices stay on the owner's panel.
+
+| Provider | Base URL | Privacy facts confirmed | Access default | Own provider listing / terms |
+|---|---|---|---|---|
+| Parasail | `https://api.parasail.io/v1` | Retention/training unstated; model list needs a key | `UNVERIFIED` | [Model docs](https://docs.parasail.io/parasail-docs/products/overview/models) |
+| Particle AI | `https://api.particle.ai/v1` | Retention/training unstated; model list needs a key | `UNVERIFIED` | [Docs](https://docs.particle.ai/) |
+| Inference.net | `https://api.inference.net/v1` | Retention/training unstated; keyless model list with per-token prices | `UNVERIFIED` | [Docs](https://docs.inference.net/) |
+| AtlasCloud | `https://api.atlascloud.ai/v1` | Retention/training unstated; keyless model list with per-token prices | `UNVERIFIED` | [Docs](https://www.atlascloud.ai/docs) |
+| Avian | `https://api.avian.io/v1` | Retention/training unstated; keyless model list, per-million prices not readable by sync | `UNVERIFIED` | [Docs](https://avian.io/docs/) |
+| NanoGPT | `https://api.nano-gpt.com/v1` | Terms allow reselling PAYG access (no markup); keyless model list carries no prices | `UNVERIFIED` | [Pricing](https://nano-gpt.com/pricing) |
+| Infermatic | `https://api.totalgpt.ai/v1` | Retention/training unstated; model list needs a key | `UNVERIFIED` | [Docs](https://ui.infermatic.ai/docs) |
+| ArliAI | `https://api.arliai.com/v1` | Retention/training unstated; keyless custom model list at `/model/all` | `UNVERIFIED` | [Site](https://www.arliai.com/) |
+| Friendli | `https://api.friendli.ai/serverless/v1` | Retention/training unstated; keyless model list with per-token prices | `UNVERIFIED` | [Model APIs intro](https://friendli.ai/docs/guides/model-apis/introduction) |
+| Scaleway | `https://api.scaleway.ai/v1` | Retention/training unstated; model list needs a key | `UNVERIFIED` | [Generative APIs](https://www.scaleway.com/en/developers/api/generative-apis) |
+
## Cheapest confirmed open-weight prices
## Cheapest confirmed open-weight prices
These are the lowest prices confirmed among the listed provider-owned source pages; an unpriced provider is not assumed to cost more. Cached prices are `—` when the provider does not publish one.
These are the lowest prices confirmed among the listed provider-owned source pages; an unpriced provider is not assumed to cost more. Cached prices are `—` when the provider does not publish one.
@@ -48,9 +48,21 @@ Required-provider documentation was checked on 2026-11-10. Meta now documents Mo
@@ -48,9 +48,21 @@ Required-provider documentation was checked on 2026-11-10. Meta now documents Mo
| SearXNG (searxng) | adapter / http | Owner-configured | Not applicable | [Source](https://docs.searxng.org/dev/search_api.html) |
| SearXNG (searxng) | adapter / http | Owner-configured | Not applicable | [Source](https://docs.searxng.org/dev/search_api.html) |
| Openverse (openverse) | adapter / http | https://api.openverse.org/v1 | Not applicable | [Source](https://api.openverse.org/) |
| Openverse (openverse) | adapter / http | https://api.openverse.org/v1 | Not applicable | [Source](https://api.openverse.org/) |
| Wikimedia Commons (commons) | adapter / http | https://commons.wikimedia.org/w/api.php | Not applicable | [Source](https://www.mediawiki.org/wiki/API:Search) |
| Wikimedia Commons (commons) | adapter / http | https://commons.wikimedia.org/w/api.php | Not applicable | [Source](https://www.mediawiki.org/wiki/API:Search) |
+| Parasail (parasail) | openai / openai | https://api.parasail.io/v1 | https://api.parasail.io/v1/models (key required) | [Source](https://docs.parasail.io/parasail-docs/products/overview/models) |
+| Particle AI (particle-ai) | openai / openai | https://api.particle.ai/v1 | https://api.particle.ai/v1/models (key required) | [Source](https://docs.particle.ai/) |
+| Inference.net (inference-net) | openai / openai | https://api.inference.net/v1 | https://api.inference.net/v1/models (keyless) | [Source](https://docs.inference.net/) |
+| AtlasCloud (atlas-cloud) | openai / openai | https://api.atlascloud.ai/v1 | https://api.atlascloud.ai/v1/models (keyless) | [Source](https://www.atlascloud.ai/docs) |
+| Avian (avian) | openai / openai | https://api.avian.io/v1 | https://api.avian.io/v1/models (keyless) | [Source](https://avian.io/docs/) |
+| NanoGPT (nanogpt) | openai / openai | https://api.nano-gpt.com/v1 | https://api.nano-gpt.com/v1/models (keyless, no prices) | [Source](https://nano-gpt.com/pricing) |
+| Infermatic (infermatic) | openai / openai | https://api.totalgpt.ai/v1 | https://api.totalgpt.ai/v1/models (key required) | [Source](https://ui.infermatic.ai/docs) |
+| ArliAI (arliai) | openai / openai | https://api.arliai.com/v1 | https://api.arliai.com/model/all (keyless) | [Source](https://www.arliai.com/) |
+| Friendli (friendli) | openai / openai | https://api.friendli.ai/serverless/v1 | https://api.friendli.ai/serverless/v1/models (keyless) | [Source](https://friendli.ai/docs/guides/model-apis/introduction) |
+| Scaleway (scaleway) | openai / openai | https://api.scaleway.ai/v1 | https://api.scaleway.ai/v1/models (key required) | [Source](https://www.scaleway.com/en/developers/api/generative-apis) |
Search capabilities use web-search and image-search, matching the shared placement capability syntax. Brave web and images share one provider/key, as do Serper web and images. Openverse and Wikimedia Commons require no key. SearXNG has no invented public address; the owner supplies their self-hosted base.
Search capabilities use web-search and image-search, matching the shared placement capability syntax. Brave web and images share one provider/key, as do Serper web and images. Openverse and Wikimedia Commons require no key. SearXNG has no invented public address; the owner supplies their self-hosted base.
+Cheap hosted inference (2026-11-10, owner request): the ten providers added at the end of the table are seeded disabled with a `vault:<key>` slot and no key, exactly like the existing pool rows, so they take part in model sync, circuits, failover order and capability memberships as soon as the owner puts a key in the console and enables them. The catalogue sync (`server/providers/catalog.js`) reads the per-token prices each model list carries and imports them into new models (Inference.net, AtlasCloud and Friendli carry per-token strings; the price book only reads per-1M figures, so it stays out of these lists). Parasail, Particle AI, Infermatic and Scaleway need a key for their model lists, so sync runs after the owner adds one; NanoGPT's list carries no prices, and Avian publishes per-million figures the existing parsers cannot read — both stay on the owner's price panel. Skipped, not guessed: kluster.ai (services sunset 2026-06-09), Lambda's hosted inference (winding down; `api.lambda.com` does not answer), Targon (`api.targon.com/v1/models` answers 410 Gone), GMI Cloud, nCompass and Crusoe (no docs-confirmed plain OpenAI chat base URL today), Mancer (its model list carries credit-unit prices whose dollar value nobody has verified; sync would import them as per-token dollars), and OVHcloud AI Endpoints (per-model regional URLs under `endpoints.kepler.ai.cloud.ovh.net`, no single base URL). Two research notes disagree with the live docs and the docs win: Infermatic's base is `api.totalgpt.ai/v1`, not `ui.infermatic.ai/v1` (prices.json), and Friendli's serverless base `api.friendli.ai/serverless/v1` is published in its docs today.
+
For Fable: the existing providers console now receives all seeded provider rows and protocol metadata. Its visual grouping and capability presentation remain for the main session.
For Fable: the existing providers console now receives all seeded provider rows and protocol metadata. Its visual grouping and capability presentation remain for the main session.
Owner key controls use the existing encrypted `ai.provider_keys` store. The console JSON exposes only presence,
Owner key controls use the existing encrypted `ai.provider_keys` store. The console JSON exposes only presence,
@@ -716,6 +716,30 @@ const PROVIDERS = [
@@ -716,6 +716,30 @@ const PROVIDERS = [
{ key: 'commons', display_name: 'Wikimedia Commons', kind: 'http', base_url: 'https://commons.wikimedia.org/w/api.php', auth_mode: 'none', models_endpoint: null,
{ key: 'commons', display_name: 'Wikimedia Commons', kind: 'http', base_url: 'https://commons.wikimedia.org/w/api.php', auth_mode: 'none', models_endpoint: null,
capabilities: ['image-search'], search_adapters: ['commons'], docs_url: 'https://www.mediawiki.org/wiki/API:Search' },
capabilities: ['image-search'], search_adapters: ['commons'], docs_url: 'https://www.mediawiki.org/wiki/API:Search' },
+ // Cheap hosted inference (owner, 2026-11-10): every endpoint below was read and probed keyless today.
+ // Each starts disabled with no key; the owner puts its key in the owner settings and syncs its model list.
+ { key: 'parasail', display_name: 'Parasail', base_url: 'https://api.parasail.io/v1',
+ docs_url: 'https://docs.parasail.io/parasail-docs/products/overview/models', capabilities: ['chat', 'embed'] },
+ { key: 'particle-ai', display_name: 'Particle AI', base_url: 'https://api.particle.ai/v1',
+ docs_url: 'https://docs.particle.ai/', capabilities: ['chat'] },
+ { key: 'inference-net', display_name: 'Inference.net', base_url: 'https://api.inference.net/v1', models_public: true,
+ docs_url: 'https://docs.inference.net/', capabilities: ['chat'] },
+ { key: 'atlas-cloud', display_name: 'AtlasCloud', base_url: 'https://api.atlascloud.ai/v1', models_public: true,
+ docs_url: 'https://www.atlascloud.ai/docs', capabilities: ['chat'] },
+ { key: 'avian', display_name: 'Avian', base_url: 'https://api.avian.io/v1', models_public: true,
+ docs_url: 'https://avian.io/docs/', capabilities: ['chat'] },
+ { key: 'nanogpt', display_name: 'NanoGPT', base_url: 'https://api.nano-gpt.com/v1', models_public: true,
+ docs_url: 'https://nano-gpt.com/pricing', capabilities: ['chat'] },
+ { key: 'infermatic', display_name: 'Infermatic', base_url: 'https://api.totalgpt.ai/v1',
+ docs_url: 'https://ui.infermatic.ai/docs', capabilities: ['chat'] },
+ { key: 'arliai', display_name: 'ArliAI', base_url: 'https://api.arliai.com/v1',
+ models_endpoint: 'https://api.arliai.com/model/all', models_public: true,
+ docs_url: 'https://www.arliai.com/', capabilities: ['chat', 'embed', 'image'] },
+ { key: 'friendli', display_name: 'Friendli', base_url: 'https://api.friendli.ai/serverless/v1', models_public: true,
+ docs_url: 'https://friendli.ai/docs/guides/model-apis/introduction', capabilities: ['chat'] },
+ { key: 'scaleway', display_name: 'Scaleway', base_url: 'https://api.scaleway.ai/v1',
+ docs_url: 'https://www.scaleway.com/en/developers/api/generative-apis', capabilities: ['chat', 'embed'] },
+
];
];
// The seed, console and personal-key forms consume these same records.
// The seed, console and personal-key forms consume these same records.
@@ -33,7 +33,9 @@ const DOMAIN = {
@@ -33,7 +33,9 @@ const DOMAIN = {
openrouter: 'openrouter.ai', venice: 'venice.ai', abliteratedmodels: 'abliteratedmodels.cc', uplinkswitch: 'uplinkswitch.ai',
openrouter: 'openrouter.ai', venice: 'venice.ai', abliteratedmodels: 'abliteratedmodels.cc', uplinkswitch: 'uplinkswitch.ai',
chutes: 'chutes.ai', siliconflow: 'siliconflow.com', nebius: 'nebius.com', together: 'together.ai',
chutes: 'chutes.ai', siliconflow: 'siliconflow.com', nebius: 'nebius.com', together: 'together.ai',
fireworks: 'fireworks.ai', groq: 'groq.com', cerebras: 'cerebras.ai', sambanova: 'sambanova.ai',
fireworks: 'fireworks.ai', groq: 'groq.com', cerebras: 'cerebras.ai', sambanova: 'sambanova.ai',
- baseten: 'baseten.co', hyperbolic: 'hyperbolic.xyz',
+ baseten: 'baseten.co', hyperbolic: 'hyperbolic.xyz', parasail: 'parasail.io', 'particle-ai': 'particle.ai',
+ 'inference-net': 'inference.net', 'atlas-cloud': 'atlascloud.ai', avian: 'avian.io',
+ nanogpt: 'nano-gpt.com', infermatic: 'totalgpt.ai', arliai: 'arliai.com', friendli: 'friendli.ai', scaleway: 'scaleway.com',
};
};
const onItsOwnSite = (url, key) => {
const onItsOwnSite = (url, key) => {
const host = new URL(url).hostname;
const host = new URL(url).hostname;
@@ -47,7 +49,7 @@ t.test('provider presets are OpenAI-compatible, with a base URL, a timeout, an a
@@ -47,7 +49,7 @@ t.test('provider presets are OpenAI-compatible, with a base URL, a timeout, an a
assert.deepStrictEqual(pool.PROVIDERS.slice(0, 23).map((p) => p.key),
assert.deepStrictEqual(pool.PROVIDERS.slice(0, 23).map((p) => p.key),
['qwen', 'kimi', 'glm', 'minimax', 'deepinfra', 'novita', 'featherless', 'refuseless', 'abliteration', 'openrouter', 'venice', 'abliteratedmodels', 'uplinkswitch', 'chutes', 'siliconflow', 'nebius', 'together', 'fireworks', 'groq', 'cerebras', 'sambanova', 'baseten', 'hyperbolic']);
['qwen', 'kimi', 'glm', 'minimax', 'deepinfra', 'novita', 'featherless', 'refuseless', 'abliteration', 'openrouter', 'venice', 'abliteratedmodels', 'uplinkswitch', 'chutes', 'siliconflow', 'nebius', 'together', 'fireworks', 'groq', 'cerebras', 'sambanova', 'baseten', 'hyperbolic']);
assert.deepStrictEqual(pool.PROVIDER_KEYS, pool.PROVIDERS.map((p) => p.key));
assert.deepStrictEqual(pool.PROVIDER_KEYS, pool.PROVIDERS.map((p) => p.key));
- assert.strictEqual(new Set(pool.PROVIDER_KEYS).size, 40);
+ assert.strictEqual(new Set(pool.PROVIDER_KEYS).size, 50);
assert.strictEqual(pool.byKey.get('meta').base_url, 'https://api.meta.ai/v1');
assert.strictEqual(pool.byKey.get('meta').base_url, 'https://api.meta.ai/v1');
assert.strictEqual(pool.byKey.get('perplexity').kind, 'responses');
assert.strictEqual(pool.byKey.get('perplexity').kind, 'responses');
assert.strictEqual(require('../server/console/presets').credentialProvider('groq').model, 'openai/gpt-oss-20b', 'existing personal-key default is preserved');
assert.strictEqual(require('../server/console/presets').credentialProvider('groq').model, 'openai/gpt-oss-20b', 'existing personal-key default is preserved');
new file mode 100644
new file mode 100644
@@ -0,0 +1,96 @@
@@ -0,0 +1,96 @@
+'use strict';
+/**
+ * The cheap hosted inference providers added 2026-11-10 (each endpoint read and probed keyless that day).
+ * Every new row starts disabled with no key and an https endpoint on its own documented host; sync imports
+ * a recorded models list (no network in tests) with prices from the provider's own list, models off.
+ */
+const assert = require('assert');
+const { boot, request, token, suite } = require('./helpers');
+const { PROVIDERS } = require('../server/providers/pool-providers');
+const seed = require('../server/providers/pool-seed');
+
+const t = suite('cheap-providers');
+const owner = token('exchange', ['ai.provider.manage', 'ai.usage.read']);
+
+const NEW_KEYS = ['parasail', 'particle-ai', 'inference-net', 'atlas-cloud', 'avian', 'nanogpt', 'infermatic', 'arliai', 'friendli', 'scaleway'];
+const HOST = {
+ parasail: 'api.parasail.io', 'particle-ai': 'api.particle.ai', 'inference-net': 'api.inference.net',
+ 'atlas-cloud': 'api.atlascloud.ai', avian: 'api.avian.io', nanogpt: 'api.nano-gpt.com',
+ infermatic: 'api.totalgpt.ai', arliai: 'api.arliai.com', friendli: 'api.friendli.ai', scaleway: 'api.scaleway.ai',
+};
+const KEYLESS = ['inference-net', 'atlas-cloud', 'avian', 'nanogpt', 'arliai', 'friendli'];
+
+const json = (body, status = 200) => new Response(JSON.stringify(body), { status, headers: { 'content-type': 'application/json' } });
+async function fetchImpl(input) {
+ const url = String(input);
+ if (url === 'https://api.inference.net/v1/models') return json({ object: 'list', data: [
+ { id: 'llama-3.3-70b', object: 'model', context_length: 131072, pricing: { prompt: '0.00000013', completion: '0.00000040', input_cache_read: '0.00000001' } },
+ { id: 'glm-5.3-flash', object: 'model', context_length: 1048576, pricing: { prompt: '0.00000010', completion: '0.00000045' } },
+ ] });
+ if (url === 'https://api.atlascloud.ai/v1/models') return json({ code: 200, msg: 'succeed', data: [
+ { id: 'deepseek-ai/DeepSeek-V3.2-Exp', object: 'model', context_length: 163840, quantization: 'fp8',
+ pricing: { prompt: '0.00000026', completion: '0.00000038', input_cache_read: '0.00000013' } },
+ ] });
+ return json({}, 404);
+}
+
+t.test('every new row: https endpoint on its own host, off, no key', () => {
+ for (const key of NEW_KEYS) {
+ const p = PROVIDERS.find((x) => x.key === key);
+ assert.ok(p, `${key} is catalogued`);
+ assert.strictEqual(new URL(p.base_url).hostname, HOST[key], `${key} base host`);
+ assert.ok(p.base_url.startsWith('https://'), `${key} https`);
+ assert.ok(p.metadata === undefined || typeof p === 'object', `${key} record`);
+ const row = seed.recordOf(p);
+ assert.strictEqual(row.status, 'disabled', `${key} starts off`);
+ assert.strictEqual(row.secret_ref, `vault:${key}`, `${key} key slot named, no key`);
+ assert.strictEqual(row.metadata.api_family, 'openai', `${key} OpenAI wire format`);
+ assert.ok(new URL(row.metadata.models_endpoint).hostname === HOST[key] || key === 'arliai', `${key} models on its own host`);
+ }
+ // Key-required lists stay out of the keyless sync, keyless ones join it.
+ for (const key of ['parasail', 'particle-ai', 'infermatic', 'scaleway'])
+ assert.notStrictEqual(PROVIDERS.find((x) => x.key === key).models_public, true, `${key} needs a key for its list`);
+ for (const key of KEYLESS)
+ assert.strictEqual(PROVIDERS.find((x) => x.key === key).models_public, true, `${key} list answers keyless`);
+});
+
+let h;
+t.test('boot seeds the new providers disabled and credentialless; sync imports prices from the recorded list', async () => {
+ h = await boot({ fetchImpl, env: { AI_USER_MODULES: 'off' } });
+ for (const key of NEW_KEYS) {
+ const p = await h.registry.getProvider(key);
+ assert.ok(p, `${key} seeded`);
+ assert.strictEqual(p.status, 'disabled', `${key} off`);
+ assert.strictEqual(p.secret_ref, `vault:${key}`, `${key} key slot`);
+ assert.strictEqual(p.metadata.models_public === true, KEYLESS.includes(key), `${key} keyless list flag`);
+ }
+ const pub = await request(h.base, 'GET', '/api/v1/providers/inference-net', { tok: owner });
+ assert.strictEqual(pub.body.provider.credentials, 'missing', 'no key configured');
+ assert.strictEqual(pub.body.provider.secret_ref, 'vault:inference-net', 'only the slot name is exposed, never a value');
+
+ const syncOpts = { actor: 'cheap-providers-test' };
+ const meta = { models_endpoint: 'https://api.inference.net/v1/models' };
+ const provider = await h.registry.getProvider('inference-net');
+ const { sync } = require('../server/providers/catalog');
+ const result = await sync({ registry: h.registry, provider: { ...provider, metadata: { ...provider.metadata, ...meta } }, apiKey: null, fetchImpl, actor: 'cheap-providers-test' });
+ assert.deepStrictEqual(result.added.sort(), ['glm-5.3-flash', 'llama-3.3-70b']);
+ const llama = await h.registry.getModel('inference-net', 'llama-3.3-70b');
+ assert.strictEqual(llama.status, 'disabled', 'synced models arrive off');
+ const near = (a, b) => Math.abs(a - b) < 1e-9;
+ assert.ok(near(llama.cost.in_per_mtok, 0.13), `prompt per-token string read as per-MTok, got ${llama.cost.in_per_mtok}`);
+ assert.ok(near(llama.cost.out_per_mtok, 0.40), `got ${llama.cost.out_per_mtok}`);
+ assert.ok(near(llama.cost.cached_per_mtok, 0.01), `got ${llama.cost.cached_per_mtok}`);
+ assert.strictEqual(llama.cost.source, 'https://api.inference.net/v1/models', 'price source is the provider list');
+ assert.strictEqual(llama.context_window, 131072);
+
+ const atlas = await h.registry.getProvider('atlas-cloud');
+ const atlasResult = await sync({ registry: h.registry, provider: { ...atlas, metadata: { ...atlas.metadata, models_endpoint: 'https://api.atlascloud.ai/v1/models' } }, apiKey: null, fetchImpl, actor: 'cheap-providers-test' });
+ assert.deepStrictEqual(atlasResult.added, ['deepseek-ai/DeepSeek-V3.2-Exp']);
+ const deepseek = await h.registry.getModel('atlas-cloud', 'deepseek-ai/DeepSeek-V3.2-Exp');
+ assert.strictEqual(deepseek.status, 'disabled');
+ assert.ok(near(deepseek.cost.in_per_mtok, 0.26), `got ${deepseek.cost.in_per_mtok}`);
+ assert.ok(near(deepseek.cost.out_per_mtok, 0.38), `got ${deepseek.cost.out_per_mtok}`);
+ assert.strictEqual(deepseek.context_window, 163840);
+});
+
+t.run(() => h && h.stop());