Vendor OpenClaw source as Adolf fork baseline
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Adolf is a fork/vendored clone of github.com/openclaw/openclaw (v2026.6.11), free to diverge. Tree copied sans upstream .git; upstream remote added for future syncs. Node pinned to 24 (.nvmrc); engines already require >=22.19. Preserves docs/ARCHITECTURE.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
This commit is contained in:
475
extensions/deepinfra/provider-models.ts
Normal file
475
extensions/deepinfra/provider-models.ts
Normal file
@@ -0,0 +1,475 @@
|
||||
// Deepinfra provider module implements model/runtime integration.
|
||||
import { isProviderApiKeyConfigured } from "openclaw/plugin-sdk/provider-auth";
|
||||
import {
|
||||
clearLiveCatalogCacheForTests,
|
||||
getCachedLiveProviderModelRows,
|
||||
LiveModelCatalogHttpError,
|
||||
} from "openclaw/plugin-sdk/provider-catalog-live-runtime";
|
||||
import { buildManifestModelProviderConfig } from "openclaw/plugin-sdk/provider-catalog-shared";
|
||||
import type { ModelDefinitionConfig } from "openclaw/plugin-sdk/provider-model-shared";
|
||||
import { createSubsystemLogger } from "openclaw/plugin-sdk/runtime-env";
|
||||
import { hasConfiguredSecretInput } from "openclaw/plugin-sdk/secret-input";
|
||||
import { asPositiveSafeInteger } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||
import manifest from "./openclaw.plugin.json" with { type: "json" };
|
||||
|
||||
const log = createSubsystemLogger("deepinfra-models");
|
||||
|
||||
const DEEPINFRA_MANIFEST_PROVIDER = buildManifestModelProviderConfig({
|
||||
providerId: "deepinfra",
|
||||
catalog: manifest.modelCatalog.providers.deepinfra,
|
||||
});
|
||||
|
||||
export const DEEPINFRA_BASE_URL = DEEPINFRA_MANIFEST_PROVIDER.baseUrl;
|
||||
export const DEEPINFRA_MODELS_URL = `${DEEPINFRA_BASE_URL}/models?sort_by=openclaw&filter=with_meta`;
|
||||
|
||||
export const DEEPINFRA_DEFAULT_MODEL_ID = "deepseek-ai/DeepSeek-V4-Flash";
|
||||
export const DEEPINFRA_DEFAULT_MODEL_REF = `deepinfra/${DEEPINFRA_DEFAULT_MODEL_ID}`;
|
||||
|
||||
const DEEPINFRA_DEFAULT_CONTEXT_WINDOW = 128000;
|
||||
const DEEPINFRA_DEFAULT_MAX_TOKENS = 8192;
|
||||
|
||||
export const DEEPINFRA_MODEL_CATALOG: ModelDefinitionConfig[] = DEEPINFRA_MANIFEST_PROVIDER.models;
|
||||
|
||||
const DISCOVERY_TIMEOUT_MS = 5000;
|
||||
const DISCOVERY_CACHE_TTL_MS = 5 * 60 * 1000;
|
||||
|
||||
type DeepInfraAuthConfig = {
|
||||
secrets?: { defaults?: { env?: string; file?: string; exec?: string } };
|
||||
models?: { providers?: Record<string, { apiKey?: unknown } | undefined> };
|
||||
};
|
||||
|
||||
// Wire format — mirrors deepapi/agent_models_api.AgentOpenAIModelsOut.
|
||||
interface DeepInfraAgentModelPricing {
|
||||
// chat / vlm / embed
|
||||
input_tokens?: number;
|
||||
output_tokens?: number;
|
||||
cache_read_tokens?: number;
|
||||
// image-gen
|
||||
per_image_unit?: number;
|
||||
// video-gen
|
||||
output_seconds?: number;
|
||||
// tts
|
||||
input_characters?: number;
|
||||
// stt
|
||||
input_seconds?: number;
|
||||
}
|
||||
|
||||
interface DeepInfraAgentModelMetadata {
|
||||
description?: string;
|
||||
context_length?: number | null;
|
||||
max_tokens?: number | null;
|
||||
pricing?: DeepInfraAgentModelPricing;
|
||||
tags?: string[];
|
||||
default_width?: number | null;
|
||||
default_height?: number | null;
|
||||
default_iterations?: number | null;
|
||||
}
|
||||
|
||||
interface DeepInfraAgentModelEntry {
|
||||
id: string;
|
||||
metadata: DeepInfraAgentModelMetadata | null;
|
||||
}
|
||||
|
||||
export type DeepInfraSurface = "chat" | "vlm" | "embed" | "image-gen" | "video-gen" | "tts" | "stt";
|
||||
|
||||
export interface DeepInfraSurfaceModel {
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
tags: string[];
|
||||
contextWindow?: number;
|
||||
maxTokens?: number;
|
||||
pricing: DeepInfraAgentModelPricing;
|
||||
defaultWidth?: number;
|
||||
defaultHeight?: number;
|
||||
defaultIterations?: number;
|
||||
}
|
||||
|
||||
export interface DeepInfraDiscoveredCatalog {
|
||||
chat: DeepInfraSurfaceModel[];
|
||||
vlm: DeepInfraSurfaceModel[];
|
||||
embed: DeepInfraSurfaceModel[];
|
||||
imageGen: DeepInfraSurfaceModel[];
|
||||
videoGen: DeepInfraSurfaceModel[];
|
||||
tts: DeepInfraSurfaceModel[];
|
||||
stt: DeepInfraSurfaceModel[];
|
||||
/** True iff served from a successful live fetch; false for the static fallback. */
|
||||
live: boolean;
|
||||
}
|
||||
|
||||
export function resetDeepInfraModelCacheForTest(): void {
|
||||
clearLiveCatalogCacheForTests();
|
||||
}
|
||||
|
||||
const SURFACE_FOR_TAG: Record<string, DeepInfraSurface> = {
|
||||
chat: "chat",
|
||||
vlm: "vlm",
|
||||
embed: "embed",
|
||||
"image-gen": "image-gen",
|
||||
"video-gen": "video-gen",
|
||||
tts: "tts",
|
||||
stt: "stt",
|
||||
};
|
||||
|
||||
function entryToSurfaceModel(entry: DeepInfraAgentModelEntry): DeepInfraSurfaceModel | null {
|
||||
const id = typeof entry?.id === "string" ? entry.id.trim() : "";
|
||||
if (!id) {
|
||||
return null;
|
||||
}
|
||||
const metadata = entry.metadata;
|
||||
if (!metadata) {
|
||||
return null;
|
||||
}
|
||||
const tags = Array.isArray(metadata.tags)
|
||||
? metadata.tags.filter((t): t is string => typeof t === "string")
|
||||
: [];
|
||||
const pricing: DeepInfraAgentModelPricing = metadata.pricing ?? {};
|
||||
return {
|
||||
id,
|
||||
name: id,
|
||||
description: metadata.description ?? undefined,
|
||||
tags,
|
||||
contextWindow: asPositiveSafeInteger(metadata.context_length),
|
||||
maxTokens: asPositiveSafeInteger(metadata.max_tokens),
|
||||
pricing,
|
||||
defaultWidth: asPositiveSafeInteger(metadata.default_width),
|
||||
defaultHeight: asPositiveSafeInteger(metadata.default_height),
|
||||
defaultIterations: asPositiveSafeInteger(metadata.default_iterations),
|
||||
};
|
||||
}
|
||||
|
||||
function bucketBySurface(models: DeepInfraSurfaceModel[]): DeepInfraDiscoveredCatalog {
|
||||
const catalog: DeepInfraDiscoveredCatalog = {
|
||||
chat: [],
|
||||
vlm: [],
|
||||
embed: [],
|
||||
imageGen: [],
|
||||
videoGen: [],
|
||||
tts: [],
|
||||
stt: [],
|
||||
live: true,
|
||||
};
|
||||
const buckets: Record<DeepInfraSurface, DeepInfraSurfaceModel[]> = {
|
||||
chat: catalog.chat,
|
||||
vlm: catalog.vlm,
|
||||
embed: catalog.embed,
|
||||
"image-gen": catalog.imageGen,
|
||||
"video-gen": catalog.videoGen,
|
||||
tts: catalog.tts,
|
||||
stt: catalog.stt,
|
||||
};
|
||||
for (const model of models) {
|
||||
const seen = new Set<DeepInfraSurface>();
|
||||
for (const tag of model.tags) {
|
||||
const surface = SURFACE_FOR_TAG[tag];
|
||||
if (surface && !seen.has(surface)) {
|
||||
seen.add(surface);
|
||||
buckets[surface].push(model);
|
||||
}
|
||||
}
|
||||
}
|
||||
return catalog;
|
||||
}
|
||||
|
||||
function hasDeepInfraSurfaceModelRows(rows: readonly unknown[]): boolean {
|
||||
return rows.some((entry) => entryToSurfaceModel(entry as DeepInfraAgentModelEntry) !== null);
|
||||
}
|
||||
|
||||
// Static fallback. Chat rows live in openclaw.plugin.json (manifest-validated);
|
||||
// non-chat surfaces live below because the manifest validator only accepts
|
||||
// chat-shaped rows. These are used pre-auth / offline; live discovery
|
||||
// overrides once a key is configured.
|
||||
interface ManifestChatModelEntry {
|
||||
id: string;
|
||||
name?: string;
|
||||
contextWindow?: number;
|
||||
maxTokens?: number;
|
||||
reasoning?: boolean;
|
||||
input?: Array<"text" | "image">;
|
||||
cost?: { input?: number; output?: number; cacheRead?: number };
|
||||
}
|
||||
|
||||
function manifestChatEntryToSurfaceModel(entry: ManifestChatModelEntry): DeepInfraSurfaceModel {
|
||||
const cost = entry.cost ?? {};
|
||||
const pricing: DeepInfraAgentModelPricing = {};
|
||||
if (typeof cost.input === "number") {
|
||||
pricing.input_tokens = cost.input;
|
||||
}
|
||||
if (typeof cost.output === "number") {
|
||||
pricing.output_tokens = cost.output;
|
||||
}
|
||||
if (typeof cost.cacheRead === "number" && cost.cacheRead > 0) {
|
||||
pricing.cache_read_tokens = cost.cacheRead;
|
||||
}
|
||||
const tags: string[] = ["chat"];
|
||||
if (entry.input?.includes("image")) {
|
||||
tags.push("vlm");
|
||||
}
|
||||
if (entry.reasoning) {
|
||||
tags.push("reasoning");
|
||||
}
|
||||
return {
|
||||
id: entry.id,
|
||||
name: entry.name ?? entry.id,
|
||||
tags,
|
||||
contextWindow: entry.contextWindow,
|
||||
maxTokens: entry.maxTokens,
|
||||
pricing,
|
||||
};
|
||||
}
|
||||
|
||||
// Per-surface static fallback used only when no API key is configured or
|
||||
// live discovery fails. Kept deliberately minimal: the dynamic
|
||||
// `/v1/openai/models?sort_by=openclaw&filter=with_meta` projection is the
|
||||
// real source of truth (140 tagged rows today), so every retired model
|
||||
// removed from the DeepInfra catalog disappears here automatically the
|
||||
// next time discovery runs. Newer entries — additional image-gen models,
|
||||
// video-gen models, additional TTS voices — arrive through discovery
|
||||
// without a code change.
|
||||
//
|
||||
// Every entry below is verified against the live catalog at the time of
|
||||
// addition; entries are not pinned to historical shipped models if the
|
||||
// upstream provider has retired them (e.g. `run-diffusion/Juggernaut-
|
||||
// Lightning-Flux` was removed from DeepInfra and is therefore not listed
|
||||
// even though earlier main releases shipped it as a fallback).
|
||||
const STATIC_NON_CHAT_FALLBACK: DeepInfraSurfaceModel[] = [
|
||||
// image-gen — representative subset of currently-served models.
|
||||
{
|
||||
id: "black-forest-labs/FLUX-1-schnell",
|
||||
name: "black-forest-labs/FLUX-1-schnell",
|
||||
tags: ["image-gen"],
|
||||
pricing: { per_image_unit: 0.003 },
|
||||
defaultWidth: 1024,
|
||||
defaultHeight: 1024,
|
||||
defaultIterations: 4,
|
||||
},
|
||||
{
|
||||
id: "black-forest-labs/FLUX-1-dev",
|
||||
name: "black-forest-labs/FLUX-1-dev",
|
||||
tags: ["image-gen"],
|
||||
pricing: { per_image_unit: 0.025 },
|
||||
defaultWidth: 1024,
|
||||
defaultHeight: 1024,
|
||||
defaultIterations: 28,
|
||||
},
|
||||
{
|
||||
id: "Qwen/Qwen-Image-Max",
|
||||
name: "Qwen/Qwen-Image-Max",
|
||||
tags: ["image-gen"],
|
||||
pricing: { per_image_unit: 0.075 },
|
||||
defaultWidth: 1024,
|
||||
defaultHeight: 1024,
|
||||
defaultIterations: 28,
|
||||
},
|
||||
{
|
||||
id: "stabilityai/sdxl-turbo",
|
||||
name: "stabilityai/sdxl-turbo",
|
||||
tags: ["image-gen"],
|
||||
pricing: { per_image_unit: 0.0002 },
|
||||
defaultWidth: 1024,
|
||||
defaultHeight: 1024,
|
||||
defaultIterations: 4,
|
||||
},
|
||||
// video-gen — DeepInfra has no live video-gen catalog rows today;
|
||||
// intentionally empty here. Live discovery picks up text-to-video
|
||||
// models as soon as the backend tags them, no static row required.
|
||||
// tts — Kokoro first so the shipped default voice (af_bella) pairs with
|
||||
// the chosen default model; the rest are alternative TTS providers
|
||||
// currently served by DeepInfra. Qwen3-TTS / chatterbox-turbo / csm-1b
|
||||
// each require their own voice; they ship as discoverable alternatives,
|
||||
// not the implicit default.
|
||||
{
|
||||
id: "hexgrad/Kokoro-82M",
|
||||
name: "hexgrad/Kokoro-82M",
|
||||
tags: ["tts"],
|
||||
pricing: { input_characters: 0.65 },
|
||||
},
|
||||
{
|
||||
id: "Qwen/Qwen3-TTS",
|
||||
name: "Qwen/Qwen3-TTS",
|
||||
tags: ["tts"],
|
||||
pricing: { input_characters: 0.65 },
|
||||
},
|
||||
{
|
||||
id: "ResembleAI/chatterbox-turbo",
|
||||
name: "ResembleAI/chatterbox-turbo",
|
||||
tags: ["tts"],
|
||||
pricing: { input_characters: 1 },
|
||||
},
|
||||
{
|
||||
id: "sesame/csm-1b",
|
||||
name: "sesame/csm-1b",
|
||||
tags: ["tts"],
|
||||
pricing: { input_characters: 7 },
|
||||
},
|
||||
// stt
|
||||
{
|
||||
id: "openai/whisper-large-v3-turbo",
|
||||
name: "openai/whisper-large-v3-turbo",
|
||||
tags: ["stt"],
|
||||
pricing: { input_seconds: 0.00004 },
|
||||
},
|
||||
// embed
|
||||
{
|
||||
id: "BAAI/bge-m3",
|
||||
name: "BAAI/bge-m3",
|
||||
tags: ["embed"],
|
||||
pricing: { input_tokens: 0.01 },
|
||||
maxTokens: 8192,
|
||||
contextWindow: 8192,
|
||||
},
|
||||
];
|
||||
|
||||
function manifestFallbackCatalog(): DeepInfraDiscoveredCatalog {
|
||||
const rawChat = (manifest.modelCatalog.providers.deepinfra.models ??
|
||||
[]) as ManifestChatModelEntry[];
|
||||
const chatModels = rawChat.map(manifestChatEntryToSurfaceModel);
|
||||
const catalog = bucketBySurface([...chatModels, ...STATIC_NON_CHAT_FALLBACK]);
|
||||
catalog.live = false;
|
||||
return catalog;
|
||||
}
|
||||
|
||||
// Sync per-surface fallback for the (sync) register callback. Media providers
|
||||
// register with these defaults; live discovery feeds the chat surface via
|
||||
// augmentModelCatalog and the catalog seams for image/video-gen.
|
||||
export function getDeepInfraSurfaceFallbackCatalog(): DeepInfraDiscoveredCatalog {
|
||||
return manifestFallbackCatalog();
|
||||
}
|
||||
|
||||
export function buildDeepInfraModelDefinition(model: ModelDefinitionConfig): ModelDefinitionConfig {
|
||||
return {
|
||||
...model,
|
||||
compat: {
|
||||
...model.compat,
|
||||
supportsUsageInStreaming: model.compat?.supportsUsageInStreaming ?? true,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function chatSurfaceModelToModelDefinition(model: DeepInfraSurfaceModel): ModelDefinitionConfig {
|
||||
const input: Array<"text" | "image"> = model.tags.includes("vlm") ? ["text", "image"] : ["text"];
|
||||
const reasoning = model.tags.includes("reasoning") || model.tags.includes("reasoning_effort");
|
||||
return buildDeepInfraModelDefinition({
|
||||
id: model.id,
|
||||
name: model.name,
|
||||
reasoning,
|
||||
input,
|
||||
contextWindow: model.contextWindow ?? DEEPINFRA_DEFAULT_CONTEXT_WINDOW,
|
||||
maxTokens: model.maxTokens ?? DEEPINFRA_DEFAULT_MAX_TOKENS,
|
||||
cost: {
|
||||
input: model.pricing.input_tokens ?? 0,
|
||||
output: model.pricing.output_tokens ?? 0,
|
||||
cacheRead: model.pricing.cache_read_tokens ?? 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Gate dynamic discovery on key presence: pre-auth keeps the picker tight and
|
||||
// avoids a useless network call. The endpoint itself is unauthenticated.
|
||||
// Accepts env-var keys and auth-profile-store keys via the shared
|
||||
// `isProviderApiKeyConfigured` helper (covers SecretRef / `OPENCLAW_LIVE_*`
|
||||
// indirection too).
|
||||
export function hasDeepInfraApiKey(options?: {
|
||||
env?: NodeJS.ProcessEnv;
|
||||
agentDir?: string;
|
||||
config?: DeepInfraAuthConfig;
|
||||
}): boolean {
|
||||
const env = options?.env ?? process.env;
|
||||
const fromEnv = env.DEEPINFRA_API_KEY;
|
||||
if (typeof fromEnv === "string" && fromEnv.trim() !== "") {
|
||||
return true;
|
||||
}
|
||||
const providers = options?.config?.models?.providers;
|
||||
for (const [providerId, provider] of Object.entries(providers ?? {})) {
|
||||
if (
|
||||
providerId.trim().toLowerCase() === "deepinfra" &&
|
||||
hasConfiguredSecretInput(provider?.apiKey, options?.config?.secrets?.defaults)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return isProviderApiKeyConfigured({ provider: "deepinfra", agentDir: options?.agentDir });
|
||||
}
|
||||
|
||||
// Discover the per-surface catalog. Falls back to the static manifest when
|
||||
// no key, fetch fails, or running under Vitest. 5-minute cache on success.
|
||||
export async function discoverDeepInfraSurfaces(options?: {
|
||||
hasApiKey?: boolean;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
agentDir?: string;
|
||||
}): Promise<DeepInfraDiscoveredCatalog> {
|
||||
if (process.env.NODE_ENV === "test" || process.env.VITEST) {
|
||||
return manifestFallbackCatalog();
|
||||
}
|
||||
|
||||
const env = options?.env ?? process.env;
|
||||
const hasKey = options?.hasApiKey ?? hasDeepInfraApiKey({ env, agentDir: options?.agentDir });
|
||||
if (!hasKey) {
|
||||
return manifestFallbackCatalog();
|
||||
}
|
||||
|
||||
try {
|
||||
const data = await getCachedLiveProviderModelRows({
|
||||
providerId: "deepinfra",
|
||||
endpoint: DEEPINFRA_MODELS_URL,
|
||||
timeoutMs: DISCOVERY_TIMEOUT_MS,
|
||||
ttlMs: DISCOVERY_CACHE_TTL_MS,
|
||||
buildRequestHeaders: () => ({ Accept: "application/json" }),
|
||||
auditContext: "deepinfra-model-discovery",
|
||||
shouldCacheRows: hasDeepInfraSurfaceModelRows,
|
||||
});
|
||||
if (data.length === 0) {
|
||||
log.warn("No models found from DeepInfra agent-projection endpoint, using static catalog");
|
||||
return manifestFallbackCatalog();
|
||||
}
|
||||
const seenIds = new Set<string>();
|
||||
const surfaceModels: DeepInfraSurfaceModel[] = [];
|
||||
for (const entry of data) {
|
||||
const model = entryToSurfaceModel(entry as DeepInfraAgentModelEntry);
|
||||
if (!model || seenIds.has(model.id)) {
|
||||
continue;
|
||||
}
|
||||
seenIds.add(model.id);
|
||||
surfaceModels.push(model);
|
||||
}
|
||||
if (surfaceModels.length === 0) {
|
||||
return manifestFallbackCatalog();
|
||||
}
|
||||
return bucketBySurface(surfaceModels);
|
||||
} catch (error) {
|
||||
if (error instanceof LiveModelCatalogHttpError) {
|
||||
log.warn(`Failed to discover models: HTTP ${error.status}, using static catalog`);
|
||||
return manifestFallbackCatalog();
|
||||
}
|
||||
log.warn(`Discovery failed: ${String(error)}, using static catalog`);
|
||||
return manifestFallbackCatalog();
|
||||
}
|
||||
}
|
||||
|
||||
// Chat-only shim for callers that haven't migrated to the per-surface catalog
|
||||
// (provider-catalog.ts, augmentModelCatalog).
|
||||
export async function discoverDeepInfraModels(options?: {
|
||||
hasApiKey?: boolean;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
agentDir?: string;
|
||||
}): Promise<ModelDefinitionConfig[]> {
|
||||
const catalog = await discoverDeepInfraSurfaces(options);
|
||||
const chatModels = catalog.chat.length > 0 ? catalog.chat : [...catalog.chat, ...catalog.vlm];
|
||||
if (chatModels.length === 0) {
|
||||
// True empty (no manifest entries either) — keep behavior stable.
|
||||
return DEEPINFRA_MODEL_CATALOG.map(buildDeepInfraModelDefinition);
|
||||
}
|
||||
const liveModels = chatModels.map(chatSurfaceModelToModelDefinition);
|
||||
const seen = new Set(liveModels.map((model) => model.id));
|
||||
const manifestModels = DEEPINFRA_MODEL_CATALOG.map(buildDeepInfraModelDefinition).filter(
|
||||
(model) => {
|
||||
if (seen.has(model.id)) {
|
||||
return false;
|
||||
}
|
||||
seen.add(model.id);
|
||||
return true;
|
||||
},
|
||||
);
|
||||
return [...liveModels, ...manifestModels];
|
||||
}
|
||||
Reference in New Issue
Block a user