refactor(providers): shared manifest row builder and fleet default-model adoption (#113879)

* feat(plugin-sdk): add manifest model row builder

* refactor(providers): share manifest model row building

* refactor(providers): source defaults from manifests

* refactor(providers): drop unconsumed builder aliases

* fix(providers): keep Cohere catalog internal
This commit is contained in:
Peter Steinberger
2026-07-25 15:00:02 -07:00
committed by GitHub
parent 5402ef192d
commit e30217d25c
54 changed files with 276 additions and 341 deletions
-1
View File
@@ -5,7 +5,6 @@ export {
BASETEN_DEFAULT_MODEL_REF,
BASETEN_MODEL_CATALOG,
buildBasetenModelCompat,
buildBasetenModelDefinition,
buildStaticBasetenModels,
discoverBasetenModels,
projectBasetenLiveModels,
+16 -22
View File
@@ -5,7 +5,10 @@ import {
getCachedLiveProviderModelRows,
type LiveModelCatalogFetchGuard,
} from "openclaw/plugin-sdk/provider-catalog-live-runtime";
import { buildManifestModelProviderConfig } from "openclaw/plugin-sdk/provider-catalog-shared";
import {
buildManifestModelDefinition,
readManifestProviderDefaultModelRef,
} from "openclaw/plugin-sdk/provider-catalog-shared";
import type {
ModelCompatConfig,
ModelDefinitionConfig,
@@ -49,9 +52,9 @@ const BASE_COMPAT: ModelCompatConfig = {
/** Base URL for Baseten's OpenAI-compatible Model APIs. */
export const BASETEN_BASE_URL = BASETEN_MANIFEST_CATALOG.baseUrl;
/** Default Baseten model id used for onboarding. */
export const BASETEN_DEFAULT_MODEL_ID = "thinkingmachines/inkling";
export const BASETEN_DEFAULT_MODEL_ID = BASETEN_MANIFEST_CATALOG.defaultModel;
/** Default Baseten model ref used for onboarding. */
export const BASETEN_DEFAULT_MODEL_REF = `baseten/${BASETEN_DEFAULT_MODEL_ID}`;
export const BASETEN_DEFAULT_MODEL_REF = readManifestProviderDefaultModelRef(manifest, "baseten")!;
/** Bundled fallback rows for all Baseten Model APIs available at release time. */
export const BASETEN_MODEL_CATALOG = BASETEN_MANIFEST_CATALOG.models;
@@ -110,27 +113,18 @@ export function buildBasetenModelCompat(modelId: string): ModelCompatConfig {
};
}
/** Builds one normalized Baseten model definition from a manifest entry. */
export function buildBasetenModelDefinition(
model: (typeof BASETEN_MODEL_CATALOG)[number],
): ModelDefinitionConfig {
const provider = buildManifestModelProviderConfig({
providerId: "baseten",
catalog: { ...BASETEN_MANIFEST_CATALOG, models: [model] },
});
const normalized = provider.models[0];
if (!normalized) {
throw new Error(`Missing normalized Baseten model ${model.id}`);
}
return {
...normalized,
compat: buildBasetenModelCompat(normalized.id),
};
}
/** Builds the network-free fallback catalog. */
export function buildStaticBasetenModels(): ModelDefinitionConfig[] {
return BASETEN_MODEL_CATALOG.map(buildBasetenModelDefinition);
return BASETEN_MODEL_CATALOG.map(
buildManifestModelDefinition({
providerId: "baseten",
catalog: BASETEN_MANIFEST_CATALOG,
decorate: (normalized) => ({
...normalized,
compat: buildBasetenModelCompat(normalized.id),
}),
}),
);
}
type BasetenLiveModelRow = {
+1
View File
@@ -24,6 +24,7 @@
"baseten": {
"baseUrl": "https://inference.baseten.co/v1",
"api": "openai-completions",
"defaultModel": "thinkingmachines/inkling",
"models": [
{
"id": "deepseek-ai/DeepSeek-V4-Pro",
-1
View File
@@ -3,7 +3,6 @@
*/
export { buildBytePlusCodingProvider, buildBytePlusProvider } from "./provider-catalog.js";
export {
buildBytePlusModelDefinition,
BYTEPLUS_BASE_URL,
BYTEPLUS_CODING_BASE_URL,
BYTEPLUS_CODING_MODEL_CATALOG,
+1
View File
@@ -10,6 +10,7 @@ import { BYTEPLUS_CODING_MODEL_CATALOG, BYTEPLUS_MODEL_CATALOG } from "./models.
describe("byteplus plugin", () => {
it("augments the catalog with bundled standard and plan models", async () => {
const provider = await registerSingleProviderPlugin(plugin);
expect(provider.auth?.[0]?.starterModel).toBe("byteplus-plan/ark-code-latest");
const standardModel = expectDefined(BYTEPLUS_MODEL_CATALOG[0], "BytePlus standard model");
const codingModel = expectDefined(BYTEPLUS_CODING_MODEL_CATALOG[0], "BytePlus coding model");
const entries = await provider.augmentModelCatalog?.({
+3 -1
View File
@@ -4,12 +4,14 @@
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth-api-key";
import { buildOpenAICompatibleLiveModelProviderConfig } from "openclaw/plugin-sdk/provider-catalog-live-runtime";
import { readManifestProviderDefaultModelRef } from "openclaw/plugin-sdk/provider-catalog-shared";
import { ensureModelAllowlistEntry } from "openclaw/plugin-sdk/provider-onboard";
import manifest from "./openclaw.plugin.json" with { type: "json" };
import { BYTEPLUS_PROVIDER_CATALOG_ENTRIES } from "./provider-catalog.js";
import { buildBytePlusVideoGenerationProvider } from "./video-generation-provider.js";
const PROVIDER_ID = "byteplus";
const BYTEPLUS_DEFAULT_MODEL_REF = "byteplus-plan/ark-code-latest";
const BYTEPLUS_DEFAULT_MODEL_REF = readManifestProviderDefaultModelRef(manifest, "byteplus-plan")!;
export default definePluginEntry({
id: PROVIDER_ID,
-9
View File
@@ -25,12 +25,3 @@ export const BYTEPLUS_MODEL_CATALOG: ModelDefinitionConfig[] = BYTEPLUS_MANIFEST
/** BytePlus coding/planning model catalog entries. */
export const BYTEPLUS_CODING_MODEL_CATALOG: ModelDefinitionConfig[] =
BYTEPLUS_CODING_MANIFEST_PROVIDER.models;
/** Clones one manifest model definition so callers can mutate safely. */
export function buildBytePlusModelDefinition(entry: ModelDefinitionConfig): ModelDefinitionConfig {
return {
...entry,
input: [...entry.input],
cost: { ...entry.cost },
};
}
+1
View File
@@ -114,6 +114,7 @@
"byteplus-plan": {
"baseUrl": "https://ark.ap-southeast.bytepluses.com/api/coding/v3",
"api": "openai-completions",
"defaultModel": "ark-code-latest",
"models": [
{
"id": "ark-code-latest",
+1 -5
View File
@@ -1,10 +1,6 @@
/**
* Public Cerebras provider plugin API exports.
*/
export {
buildCerebrasModelDefinition,
CEREBRAS_BASE_URL,
CEREBRAS_MODEL_CATALOG,
} from "./models.js";
export { buildCerebrasCatalogModels, CEREBRAS_BASE_URL, CEREBRAS_MODEL_CATALOG } from "./models.js";
export { buildCerebrasProvider } from "./provider-catalog.js";
export { applyCerebrasConfig, CEREBRAS_DEFAULT_MODEL_REF } from "./onboard.js";
+4 -17
View File
@@ -1,8 +1,7 @@
/**
* Cerebras model catalog helpers derived from the plugin manifest.
*/
import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
import { buildManifestModelProviderConfig } from "openclaw/plugin-sdk/provider-catalog-shared";
import { buildManifestModelDefinition } from "openclaw/plugin-sdk/provider-catalog-shared";
import type { ModelDefinitionConfig } from "openclaw/plugin-sdk/provider-model-shared";
import manifest from "./openclaw.plugin.json" with { type: "json" };
@@ -15,19 +14,7 @@ export const CEREBRAS_MODEL_CATALOG = CEREBRAS_MANIFEST_CATALOG.models;
/** Builds normalized Cerebras catalog model definitions. */
export function buildCerebrasCatalogModels(): ModelDefinitionConfig[] {
return buildManifestModelProviderConfig({
providerId: "cerebras",
catalog: CEREBRAS_MANIFEST_CATALOG,
}).models;
}
/** Builds one normalized Cerebras model definition from a manifest entry. */
export function buildCerebrasModelDefinition(
model: (typeof CEREBRAS_MODEL_CATALOG)[number],
): ModelDefinitionConfig {
const providerConfig = buildManifestModelProviderConfig({
providerId: "cerebras",
catalog: { ...CEREBRAS_MANIFEST_CATALOG, models: [model] },
});
return expectDefined(providerConfig.models.at(0), "normalized Cerebras manifest model");
return CEREBRAS_MODEL_CATALOG.map(
buildManifestModelDefinition({ providerId: "cerebras", catalog: CEREBRAS_MANIFEST_CATALOG }),
);
}
+2 -6
View File
@@ -3,11 +3,7 @@ import {
createModelCatalogPresetAppliers,
type OpenClawConfig,
} from "openclaw/plugin-sdk/provider-onboard";
import {
buildCerebrasModelDefinition,
CEREBRAS_BASE_URL,
CEREBRAS_MODEL_CATALOG,
} from "./models.js";
import { buildCerebrasCatalogModels, CEREBRAS_BASE_URL } from "./models.js";
import manifest from "./openclaw.plugin.json" with { type: "json" };
export const CEREBRAS_DEFAULT_MODEL_REF = readManifestProviderDefaultModelRef(
@@ -21,7 +17,7 @@ const cerebrasPresetAppliers = createModelCatalogPresetAppliers({
providerId: "cerebras",
api: "openai-completions",
baseUrl: CEREBRAS_BASE_URL,
catalogModels: CEREBRAS_MODEL_CATALOG.map(buildCerebrasModelDefinition),
catalogModels: buildCerebrasCatalogModels(),
aliases: [{ modelRef: CEREBRAS_DEFAULT_MODEL_REF, alias: "Cerebras GPT OSS 120B" }],
}),
});
+1 -6
View File
@@ -1,9 +1,4 @@
export {
buildChutesModelDefinition,
CHUTES_BASE_URL,
CHUTES_MODEL_CATALOG,
discoverChutesModels,
} from "./models.js";
export { CHUTES_BASE_URL, CHUTES_MODEL_CATALOG, discoverChutesModels } from "./models.js";
export { buildChutesProvider } from "./provider-catalog.js";
export {
applyChutesApiKeyConfig,
+4 -8
View File
@@ -3,11 +3,7 @@ import { expectDefined } from "@openclaw/normalization-core";
import { clearLiveCatalogCacheForTests } from "openclaw/plugin-sdk/provider-catalog-live-runtime";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { CHUTES_DEFAULT_MODEL_ID } from "./api.js";
import {
buildChutesModelDefinition,
CHUTES_MODEL_CATALOG,
discoverChutesModels,
} from "./models.js";
import { CHUTES_MODEL_CATALOG, discoverChutesModels } from "./models.js";
import { applyChutesConfig, CHUTES_DEFAULT_MODEL_REF } from "./onboard.js";
import manifest from "./openclaw.plugin.json" with { type: "json" };
@@ -102,9 +98,9 @@ describe("chutes-models", () => {
clearLiveCatalogCacheForTests();
});
it("buildChutesModelDefinition returns config with required fields", () => {
it("builds static definitions with required fields", () => {
const entry = expectDefined(CHUTES_MODEL_CATALOG[0], "first Chutes catalog model");
const def = buildChutesModelDefinition(entry);
const def = entry;
expect(def.id).toBe(entry.id);
expect(def.name).toBe(entry.name);
expect(def.reasoning).toBe(entry.reasoning);
@@ -126,7 +122,7 @@ describe("chutes-models", () => {
if (!model) {
throw new Error(`expected ${id}`);
}
expect(buildChutesModelDefinition(model).input).toContain("image");
expect(model.input).toContain("image");
}
});
+15 -13
View File
@@ -6,7 +6,7 @@ import {
getCachedLiveProviderModelRows,
LiveModelCatalogHttpError,
} from "openclaw/plugin-sdk/provider-catalog-live-runtime";
import { buildManifestModelProviderConfig } from "openclaw/plugin-sdk/provider-catalog-shared";
import { buildManifestModelDefinition } 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 {
@@ -23,22 +23,15 @@ import manifest from "./openclaw.plugin.json" with { type: "json" };
const log = createSubsystemLogger("chutes-models");
const CHUTES_MANIFEST_PROVIDER = buildManifestModelProviderConfig({
providerId: "chutes",
catalog: manifest.modelCatalog.providers.chutes,
});
const CHUTES_MANIFEST_CATALOG = manifest.modelCatalog.providers.chutes;
/** Base URL for Chutes OpenAI-compatible inference. */
export const CHUTES_BASE_URL = CHUTES_MANIFEST_PROVIDER.baseUrl;
export const CHUTES_BASE_URL = CHUTES_MANIFEST_CATALOG.baseUrl;
const CHUTES_DEFAULT_CONTEXT_WINDOW = 128000;
const CHUTES_DEFAULT_MAX_TOKENS = 4096;
/** Bundled fallback Chutes model catalog, normalized from the plugin manifest. */
export const CHUTES_MODEL_CATALOG = CHUTES_MANIFEST_PROVIDER.models;
/** Adds Chutes provider compat metadata to one model catalog entry. */
export function buildChutesModelDefinition(model: ModelDefinitionConfig): ModelDefinitionConfig {
function decorateChutesModelDefinition(model: ModelDefinitionConfig): ModelDefinitionConfig {
return {
...model,
compat: {
@@ -48,6 +41,15 @@ export function buildChutesModelDefinition(model: ModelDefinitionConfig): ModelD
};
}
/** Bundled fallback Chutes model catalog, normalized from the plugin manifest. */
export const CHUTES_MODEL_CATALOG: ModelDefinitionConfig[] = CHUTES_MANIFEST_CATALOG.models.map(
buildManifestModelDefinition({
providerId: "chutes",
catalog: CHUTES_MANIFEST_CATALOG,
decorate: decorateChutesModelDefinition,
}),
);
interface ChutesModelEntry {
id: string;
name?: string;
@@ -87,10 +89,10 @@ export async function discoverChutesModels(accessToken?: string): Promise<ModelD
const trimmedKey = normalizeOptionalString(accessToken) ?? "";
if (isChutesModelDiscoveryTestEnvironment()) {
return CHUTES_MODEL_CATALOG.map(buildChutesModelDefinition);
return structuredClone(CHUTES_MODEL_CATALOG);
}
const staticCatalog = () => CHUTES_MODEL_CATALOG.map(buildChutesModelDefinition);
const staticCatalog = () => structuredClone(CHUTES_MODEL_CATALOG);
try {
const data = await fetchChutesModelRows(trimmedKey || undefined);
+2 -2
View File
@@ -4,7 +4,7 @@ import {
createModelCatalogPresetAppliers,
type OpenClawConfig,
} from "openclaw/plugin-sdk/provider-onboard";
import { CHUTES_BASE_URL, CHUTES_MODEL_CATALOG, buildChutesModelDefinition } from "./models.js";
import { CHUTES_BASE_URL, CHUTES_MODEL_CATALOG } from "./models.js";
import manifest from "./openclaw.plugin.json" with { type: "json" };
export const CHUTES_DEFAULT_MODEL_ID = manifest.modelCatalog.providers.chutes.defaultModel;
@@ -16,7 +16,7 @@ const chutesPresetAppliers = createModelCatalogPresetAppliers({
providerId: "chutes",
api: "openai-completions",
baseUrl: CHUTES_BASE_URL,
catalogModels: CHUTES_MODEL_CATALOG.map(buildChutesModelDefinition),
catalogModels: structuredClone(CHUTES_MODEL_CATALOG),
aliases: [
...CHUTES_MODEL_CATALOG.map((model) => `chutes/${model.id}`),
{
+3 -8
View File
@@ -2,19 +2,14 @@
* Chutes provider builders for static and dynamically discovered catalogs.
*/
import type { ModelProviderConfig } from "openclaw/plugin-sdk/provider-model-shared";
import {
CHUTES_BASE_URL,
CHUTES_MODEL_CATALOG,
buildChutesModelDefinition,
discoverChutesModels,
} from "./models.js";
import { CHUTES_BASE_URL, CHUTES_MODEL_CATALOG, discoverChutesModels } from "./models.js";
/** Builds the static Chutes provider catalog from bundled model metadata. */
export function buildStaticChutesProvider(): ModelProviderConfig {
return {
baseUrl: CHUTES_BASE_URL,
api: "openai-completions",
models: CHUTES_MODEL_CATALOG.map(buildChutesModelDefinition),
models: structuredClone(CHUTES_MODEL_CATALOG),
};
}
@@ -28,6 +23,6 @@ export async function buildChutesProvider(accessToken?: string): Promise<ModelPr
return {
baseUrl: CHUTES_BASE_URL,
api: "openai-completions",
models: models.length > 0 ? models : CHUTES_MODEL_CATALOG.map(buildChutesModelDefinition),
models: models.length > 0 ? models : structuredClone(CHUTES_MODEL_CATALOG),
};
}
+5 -17
View File
@@ -1,15 +1,14 @@
/**
* Cohere model catalog helpers derived from the plugin manifest.
*/
import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
import { buildManifestModelProviderConfig } from "openclaw/plugin-sdk/provider-catalog-shared";
import { buildManifestModelDefinition } from "openclaw/plugin-sdk/provider-catalog-shared";
import type { ModelDefinitionConfig } from "openclaw/plugin-sdk/provider-model-shared";
import manifest from "./openclaw.plugin.json" with { type: "json" };
const COHERE_MANIFEST_CATALOG = manifest.modelCatalog.providers.cohere;
export const COHERE_BASE_URL = COHERE_MANIFEST_CATALOG.baseUrl;
export const COHERE_MODEL_CATALOG = COHERE_MANIFEST_CATALOG.models;
const COHERE_MODEL_CATALOG = COHERE_MANIFEST_CATALOG.models;
const COHERE_COMMAND_A_PLUS_MODEL_ID = "command-a-plus-05-2026";
const COHERE_COMMAND_A_REASONING_MODEL_ID = "command-a-reasoning-08-2025";
const COHERE_NORTH_MINI_CODE_MODEL_ID = "north-mini-code-1-0";
@@ -26,18 +25,7 @@ export function isModernCohereModelId(modelId: string): boolean {
}
export function buildCohereCatalogModels(): ModelDefinitionConfig[] {
return buildManifestModelProviderConfig({
providerId: "cohere",
catalog: COHERE_MANIFEST_CATALOG,
}).models;
}
export function buildCohereModelDefinition(
model: (typeof COHERE_MODEL_CATALOG)[number],
): ModelDefinitionConfig {
const providerConfig = buildManifestModelProviderConfig({
providerId: "cohere",
catalog: { ...COHERE_MANIFEST_CATALOG, models: [model] },
});
return expectDefined(providerConfig.models.at(0), "normalized Cohere manifest model");
return COHERE_MODEL_CATALOG.map(
buildManifestModelDefinition({ providerId: "cohere", catalog: COHERE_MANIFEST_CATALOG }),
);
}
+5 -2
View File
@@ -1,8 +1,9 @@
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
import { resolveAgentModelPrimaryValue } from "openclaw/plugin-sdk/provider-onboard";
import { describe, expect, it } from "vitest";
import { buildCohereCatalogModels, COHERE_BASE_URL, COHERE_MODEL_CATALOG } from "./models.js";
import { buildCohereCatalogModels, COHERE_BASE_URL } from "./models.js";
import { applyCohereConfig, COHERE_DEFAULT_MODEL_REF } from "./onboard.js";
import manifest from "./openclaw.plugin.json" with { type: "json" };
const COHERE_DEFAULT_MODEL_ID = "command-a-plus-05-2026";
const COHERE_COMMAND_A_REASONING_MODEL_ID = "command-a-reasoning-08-2025";
@@ -25,7 +26,9 @@ describe("Cohere onboarding", () => {
COHERE_COMMAND_A_VISION_MODEL_ID,
COHERE_NORTH_MINI_CODE_MODEL_ID,
]);
expect(buildCohereCatalogModels()).toHaveLength(COHERE_MODEL_CATALOG.length);
expect(buildCohereCatalogModels()).toHaveLength(
manifest.modelCatalog.providers.cohere.models.length,
);
});
it("sets Cohere only when there is no primary model", () => {
+2 -2
View File
@@ -3,7 +3,7 @@ import {
createModelCatalogPresetAppliers,
type OpenClawConfig,
} from "openclaw/plugin-sdk/provider-onboard";
import { buildCohereModelDefinition, COHERE_BASE_URL, COHERE_MODEL_CATALOG } from "./models.js";
import { buildCohereCatalogModels, COHERE_BASE_URL } from "./models.js";
import manifest from "./openclaw.plugin.json" with { type: "json" };
export const COHERE_DEFAULT_MODEL_REF = readManifestProviderDefaultModelRef(manifest, "cohere")!;
@@ -14,7 +14,7 @@ const coherePresetAppliers = createModelCatalogPresetAppliers({
providerId: "cohere",
api: "openai-completions",
baseUrl: COHERE_BASE_URL,
catalogModels: COHERE_MODEL_CATALOG.map(buildCohereModelDefinition),
catalogModels: buildCohereCatalogModels(),
aliases: [{ modelRef: COHERE_DEFAULT_MODEL_REF, alias: "Cohere Command A+" }],
}),
});
+1 -5
View File
@@ -1,8 +1,4 @@
// Deepseek API module exposes the plugin public contract.
export {
buildDeepSeekModelDefinition,
DEEPSEEK_BASE_URL,
DEEPSEEK_MODEL_CATALOG,
} from "./models.js";
export { DEEPSEEK_BASE_URL, DEEPSEEK_MODEL_CATALOG } from "./models.js";
export { buildDeepSeekProvider } from "./provider-catalog.js";
export { createDeepSeekV4ThinkingWrapper } from "./stream.js";
+10 -17
View File
@@ -1,25 +1,18 @@
// Deepseek plugin module implements models behavior.
import { buildManifestModelProviderConfig } from "openclaw/plugin-sdk/provider-catalog-shared";
import { buildManifestModelDefinition } from "openclaw/plugin-sdk/provider-catalog-shared";
import type { ModelDefinitionConfig } from "openclaw/plugin-sdk/provider-model-shared";
import manifest from "./openclaw.plugin.json" with { type: "json" };
const DEEPSEEK_MANIFEST_PROVIDER = buildManifestModelProviderConfig({
providerId: "deepseek",
catalog: manifest.modelCatalog.providers.deepseek,
});
const DEEPSEEK_MANIFEST_CATALOG = manifest.modelCatalog.providers.deepseek;
export const DEEPSEEK_BASE_URL = DEEPSEEK_MANIFEST_CATALOG.baseUrl;
export const DEEPSEEK_BASE_URL = DEEPSEEK_MANIFEST_PROVIDER.baseUrl;
export const DEEPSEEK_MODEL_CATALOG: ModelDefinitionConfig[] = DEEPSEEK_MANIFEST_PROVIDER.models;
export function buildDeepSeekModelDefinition(
model: (typeof DEEPSEEK_MODEL_CATALOG)[number],
): ModelDefinitionConfig {
return {
...model,
api: "openai-completions",
};
}
export const DEEPSEEK_MODEL_CATALOG: ModelDefinitionConfig[] = DEEPSEEK_MANIFEST_CATALOG.models.map(
buildManifestModelDefinition({
providerId: "deepseek",
catalog: DEEPSEEK_MANIFEST_CATALOG,
decorate: (model) => ({ ...model, api: "openai-completions" }),
}),
);
const DEEPSEEK_V4_MODEL_IDS = new Set(["deepseek-v4-flash", "deepseek-v4-pro"]);
+2 -6
View File
@@ -3,11 +3,7 @@ import {
createModelCatalogPresetAppliers,
type OpenClawConfig,
} from "openclaw/plugin-sdk/provider-onboard";
import {
buildDeepSeekModelDefinition,
DEEPSEEK_BASE_URL,
DEEPSEEK_MODEL_CATALOG,
} from "./models.js";
import { DEEPSEEK_BASE_URL, DEEPSEEK_MODEL_CATALOG } from "./models.js";
import manifest from "./openclaw.plugin.json" with { type: "json" };
export const DEEPSEEK_DEFAULT_MODEL_REF = readManifestProviderDefaultModelRef(
@@ -21,7 +17,7 @@ const deepSeekPresetAppliers = createModelCatalogPresetAppliers({
providerId: "deepseek",
api: "openai-completions",
baseUrl: DEEPSEEK_BASE_URL,
catalogModels: DEEPSEEK_MODEL_CATALOG.map(buildDeepSeekModelDefinition),
catalogModels: structuredClone(DEEPSEEK_MODEL_CATALOG),
aliases: [{ modelRef: DEEPSEEK_DEFAULT_MODEL_REF, alias: "DeepSeek" }],
}),
});
+2 -6
View File
@@ -1,15 +1,11 @@
// Deepseek provider module implements model/runtime integration.
import type { ModelProviderConfig } from "openclaw/plugin-sdk/provider-model-shared";
import {
buildDeepSeekModelDefinition,
DEEPSEEK_BASE_URL,
DEEPSEEK_MODEL_CATALOG,
} from "./models.js";
import { DEEPSEEK_BASE_URL, DEEPSEEK_MODEL_CATALOG } from "./models.js";
export function buildDeepSeekProvider(): ModelProviderConfig {
return {
baseUrl: DEEPSEEK_BASE_URL,
api: "openai-completions",
models: DEEPSEEK_MODEL_CATALOG.map(buildDeepSeekModelDefinition),
models: structuredClone(DEEPSEEK_MODEL_CATALOG),
};
}
+14 -16
View File
@@ -1,20 +1,18 @@
// Gmi plugin module implements models behavior.
import { buildManifestModelProviderConfig } from "openclaw/plugin-sdk/provider-catalog-shared";
import {
buildManifestModelDefinition,
readManifestProviderDefaultModelRef,
} from "openclaw/plugin-sdk/provider-catalog-shared";
import type { ModelDefinitionConfig } from "openclaw/plugin-sdk/provider-model-shared";
import manifest from "./openclaw.plugin.json" with { type: "json" };
const GMI_MANIFEST_PROVIDER = buildManifestModelProviderConfig({
providerId: "gmi",
catalog: manifest.modelCatalog.providers.gmi,
});
export const GMI_BASE_URL = GMI_MANIFEST_PROVIDER.baseUrl;
export const GMI_MODEL_CATALOG: ModelDefinitionConfig[] = GMI_MANIFEST_PROVIDER.models;
export const GMI_DEFAULT_MODEL_REF = "gmi/openai/gpt-5.6-sol";
export function buildGmiModelDefinition(model: ModelDefinitionConfig): ModelDefinitionConfig {
return {
...model,
api: "openai-completions",
};
}
const GMI_MANIFEST_CATALOG = manifest.modelCatalog.providers.gmi;
export const GMI_BASE_URL = GMI_MANIFEST_CATALOG.baseUrl;
export const GMI_MODEL_CATALOG: ModelDefinitionConfig[] = GMI_MANIFEST_CATALOG.models.map(
buildManifestModelDefinition({
providerId: "gmi",
catalog: GMI_MANIFEST_CATALOG,
decorate: (model) => ({ ...model, api: "openai-completions" }),
}),
);
export const GMI_DEFAULT_MODEL_REF = readManifestProviderDefaultModelRef(manifest, "gmi")!;
+1
View File
@@ -73,6 +73,7 @@
"gmi": {
"baseUrl": "https://api.gmi-serving.com/v1",
"api": "openai-completions",
"defaultModel": "openai/gpt-5.6-sol",
"models": [
{
"id": "zai-org/GLM-5.2-FP8",
+2 -2
View File
@@ -1,11 +1,11 @@
// Gmi provider module implements model/runtime integration.
import type { ModelProviderConfig } from "openclaw/plugin-sdk/provider-model-shared";
import { GMI_BASE_URL, GMI_MODEL_CATALOG, buildGmiModelDefinition } from "./models.js";
import { GMI_BASE_URL, GMI_MODEL_CATALOG } from "./models.js";
export function buildGmiProvider(): ModelProviderConfig {
return {
baseUrl: GMI_BASE_URL,
api: "openai-completions",
models: GMI_MODEL_CATALOG.map(buildGmiModelDefinition),
models: structuredClone(GMI_MODEL_CATALOG),
};
}
+1 -6
View File
@@ -1,11 +1,6 @@
/**
* Public Meta provider plugin API exports.
*/
export {
buildMetaCatalogModels,
buildMetaModelDefinition,
META_BASE_URL,
META_MODEL_CATALOG,
} from "./models.js";
export { buildMetaCatalogModels, META_BASE_URL, META_MODEL_CATALOG } from "./models.js";
export { buildMetaProvider } from "./provider-catalog.js";
export { applyMetaConfig, META_DEFAULT_MODEL_REF } from "./onboard.js";
+1
View File
@@ -30,6 +30,7 @@ describe("meta provider", () => {
id: "api-key",
kind: "api_key",
label: "Meta API key",
starterModel: "meta/muse-spark-1.1",
});
});
+4 -17
View File
@@ -1,8 +1,7 @@
/**
* Meta model catalog helpers derived from the plugin manifest.
*/
import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
import { buildManifestModelProviderConfig } from "openclaw/plugin-sdk/provider-catalog-shared";
import { buildManifestModelDefinition } from "openclaw/plugin-sdk/provider-catalog-shared";
import type { ModelDefinitionConfig } from "openclaw/plugin-sdk/provider-model-shared";
import manifest from "./openclaw.plugin.json" with { type: "json" };
@@ -15,19 +14,7 @@ export const META_MODEL_CATALOG = META_MANIFEST_CATALOG.models;
/** Builds normalized Meta catalog model definitions. */
export function buildMetaCatalogModels(): ModelDefinitionConfig[] {
return buildManifestModelProviderConfig({
providerId: "meta",
catalog: META_MANIFEST_CATALOG,
}).models;
}
/** Builds one normalized Meta model definition from a manifest entry. */
export function buildMetaModelDefinition(
model: (typeof META_MODEL_CATALOG)[number],
): ModelDefinitionConfig {
const providerConfig = buildManifestModelProviderConfig({
providerId: "meta",
catalog: { ...META_MANIFEST_CATALOG, models: [model] },
});
return expectDefined(providerConfig.models.at(0), "normalized Meta manifest model");
return META_MODEL_CATALOG.map(
buildManifestModelDefinition({ providerId: "meta", catalog: META_MANIFEST_CATALOG }),
);
}
+5 -3
View File
@@ -1,14 +1,16 @@
/**
* Meta onboarding config helpers.
*/
import { readManifestProviderDefaultModelRef } from "openclaw/plugin-sdk/provider-catalog-shared";
import {
createModelCatalogPresetAppliers,
type OpenClawConfig,
} from "openclaw/plugin-sdk/provider-onboard";
import { buildMetaModelDefinition, META_BASE_URL, META_MODEL_CATALOG } from "./models.js";
import { buildMetaCatalogModels, META_BASE_URL } from "./models.js";
import manifest from "./openclaw.plugin.json" with { type: "json" };
/** Default Meta model reference used after onboarding. */
export const META_DEFAULT_MODEL_REF = "meta/muse-spark-1.1";
export const META_DEFAULT_MODEL_REF = readManifestProviderDefaultModelRef(manifest, "meta")!;
const metaPresetAppliers = createModelCatalogPresetAppliers({
primaryModelRef: META_DEFAULT_MODEL_REF,
@@ -16,7 +18,7 @@ const metaPresetAppliers = createModelCatalogPresetAppliers({
providerId: "meta",
api: "openai-responses",
baseUrl: META_BASE_URL,
catalogModels: META_MODEL_CATALOG.map(buildMetaModelDefinition),
catalogModels: buildMetaCatalogModels(),
aliases: [{ modelRef: META_DEFAULT_MODEL_REF, alias: "Muse Spark 1.1" }],
}),
});
+1
View File
@@ -27,6 +27,7 @@
"meta": {
"baseUrl": "https://api.meta.ai/v1",
"api": "openai-responses",
"defaultModel": "muse-spark-1.1",
"models": [
{
"id": "muse-spark-1.1",
+14 -15
View File
@@ -1,20 +1,19 @@
// Novita plugin module implements models behavior.
import { buildManifestModelProviderConfig } from "openclaw/plugin-sdk/provider-catalog-shared";
import {
buildManifestModelDefinition,
readManifestProviderDefaultModelRef,
} from "openclaw/plugin-sdk/provider-catalog-shared";
import type { ModelDefinitionConfig } from "openclaw/plugin-sdk/provider-model-shared";
import manifest from "./openclaw.plugin.json" with { type: "json" };
const NOVITA_MANIFEST_PROVIDER = buildManifestModelProviderConfig({
providerId: "novita",
catalog: manifest.modelCatalog.providers.novita,
});
const NOVITA_MANIFEST_CATALOG = manifest.modelCatalog.providers.novita;
export const NOVITA_BASE_URL = NOVITA_MANIFEST_PROVIDER.baseUrl;
export const NOVITA_MODEL_CATALOG: ModelDefinitionConfig[] = NOVITA_MANIFEST_PROVIDER.models;
export const NOVITA_DEFAULT_MODEL_REF = "novita/deepseek/deepseek-v4-pro";
export function buildNovitaModelDefinition(model: ModelDefinitionConfig): ModelDefinitionConfig {
return {
...model,
api: "openai-completions",
};
}
export const NOVITA_BASE_URL = NOVITA_MANIFEST_CATALOG.baseUrl;
export const NOVITA_MODEL_CATALOG: ModelDefinitionConfig[] = NOVITA_MANIFEST_CATALOG.models.map(
buildManifestModelDefinition({
providerId: "novita",
catalog: NOVITA_MANIFEST_CATALOG,
decorate: (model) => ({ ...model, api: "openai-completions" }),
}),
);
export const NOVITA_DEFAULT_MODEL_REF = readManifestProviderDefaultModelRef(manifest, "novita")!;
+1
View File
@@ -71,6 +71,7 @@
"novita": {
"baseUrl": "https://api.novita.ai/openai/v1",
"api": "openai-completions",
"defaultModel": "deepseek/deepseek-v4-pro",
"models": [
{
"id": "moonshotai/kimi-k3",
+2 -2
View File
@@ -1,11 +1,11 @@
// Novita provider module implements model/runtime integration.
import type { ModelProviderConfig } from "openclaw/plugin-sdk/provider-model-shared";
import { NOVITA_BASE_URL, NOVITA_MODEL_CATALOG, buildNovitaModelDefinition } from "./models.js";
import { NOVITA_BASE_URL, NOVITA_MODEL_CATALOG } from "./models.js";
export function buildNovitaProvider(): ModelProviderConfig {
return {
baseUrl: NOVITA_BASE_URL,
api: "openai-completions",
models: NOVITA_MODEL_CATALOG.map(buildNovitaModelDefinition),
models: structuredClone(NOVITA_MODEL_CATALOG),
};
}
-2
View File
@@ -1,7 +1,5 @@
// Tencent API module exposes the plugin public contract.
export {
buildTokenHubModelDefinition,
buildTokenPlanModelDefinition,
TOKENHUB_BASE_URL,
TOKENHUB_MODEL_CATALOG,
TOKENHUB_PROVIDER_ID,
+20 -33
View File
@@ -1,5 +1,5 @@
// Tencent plugin module implements models behavior.
import { buildManifestModelProviderConfig } from "openclaw/plugin-sdk/provider-catalog-shared";
import { buildManifestModelDefinition } from "openclaw/plugin-sdk/provider-catalog-shared";
import type { ModelDefinitionConfig } from "openclaw/plugin-sdk/provider-model-shared";
import manifest from "./openclaw.plugin.json" with { type: "json" };
@@ -7,42 +7,29 @@ import manifest from "./openclaw.plugin.json" with { type: "json" };
export const TOKENHUB_PROVIDER_ID = "tencent-tokenhub";
const TOKENHUB_MANIFEST_PROVIDER = buildManifestModelProviderConfig({
providerId: TOKENHUB_PROVIDER_ID,
catalog: manifest.modelCatalog.providers[TOKENHUB_PROVIDER_ID],
});
export const TOKENHUB_BASE_URL = manifest.modelCatalog.providers[TOKENHUB_PROVIDER_ID].baseUrl;
export const TOKENHUB_BASE_URL = TOKENHUB_MANIFEST_PROVIDER.baseUrl;
export const TOKENHUB_MODEL_CATALOG: ModelDefinitionConfig[] = TOKENHUB_MANIFEST_PROVIDER.models;
export function buildTokenHubModelDefinition(
model: (typeof TOKENHUB_MODEL_CATALOG)[number],
): ModelDefinitionConfig {
return {
...model,
api: "openai-completions",
};
}
const TOKENHUB_MANIFEST_CATALOG = manifest.modelCatalog.providers[TOKENHUB_PROVIDER_ID];
export const TOKENHUB_MODEL_CATALOG: ModelDefinitionConfig[] = TOKENHUB_MANIFEST_CATALOG.models.map(
buildManifestModelDefinition({
providerId: TOKENHUB_PROVIDER_ID,
catalog: TOKENHUB_MANIFEST_CATALOG,
decorate: (model) => ({ ...model, api: "openai-completions" }),
}),
);
// ---------- TokenPlan provider ----------
export const TOKENPLAN_PROVIDER_ID = "tencent-tokenplan";
const TOKENPLAN_MANIFEST_PROVIDER = buildManifestModelProviderConfig({
providerId: TOKENPLAN_PROVIDER_ID,
catalog: manifest.modelCatalog.providers[TOKENPLAN_PROVIDER_ID],
});
export const TOKENPLAN_BASE_URL = manifest.modelCatalog.providers[TOKENPLAN_PROVIDER_ID].baseUrl;
export const TOKENPLAN_BASE_URL = TOKENPLAN_MANIFEST_PROVIDER.baseUrl;
export const TOKENPLAN_MODEL_CATALOG: ModelDefinitionConfig[] = TOKENPLAN_MANIFEST_PROVIDER.models;
export function buildTokenPlanModelDefinition(
model: (typeof TOKENPLAN_MODEL_CATALOG)[number],
): ModelDefinitionConfig {
return {
...model,
api: "openai-completions",
};
}
const TOKENPLAN_MANIFEST_CATALOG = manifest.modelCatalog.providers[TOKENPLAN_PROVIDER_ID];
export const TOKENPLAN_MODEL_CATALOG: ModelDefinitionConfig[] =
TOKENPLAN_MANIFEST_CATALOG.models.map(
buildManifestModelDefinition({
providerId: TOKENPLAN_PROVIDER_ID,
catalog: TOKENPLAN_MANIFEST_CATALOG,
decorate: (model) => ({ ...model, api: "openai-completions" }),
}),
);
+2 -4
View File
@@ -4,8 +4,6 @@ import {
type OpenClawConfig,
} from "openclaw/plugin-sdk/provider-onboard";
import {
buildTokenHubModelDefinition,
buildTokenPlanModelDefinition,
TOKENHUB_BASE_URL,
TOKENHUB_MODEL_CATALOG,
TOKENHUB_PROVIDER_ID,
@@ -27,7 +25,7 @@ const tokenHubPresetAppliers = createModelCatalogPresetAppliers({
providerId: TOKENHUB_PROVIDER_ID,
api: "openai-completions",
baseUrl: TOKENHUB_BASE_URL,
catalogModels: TOKENHUB_MODEL_CATALOG.map(buildTokenHubModelDefinition),
catalogModels: structuredClone(TOKENHUB_MODEL_CATALOG),
aliases: [
{ modelRef: TOKENHUB_DEFAULT_MODEL_REF, alias: "Hy3 (TokenHub)" },
{ modelRef: TOKENHUB_PREVIEW_MODEL_REF, alias: "Hy3 preview (TokenHub)" },
@@ -50,7 +48,7 @@ const tokenPlanPresetAppliers = createModelCatalogPresetAppliers({
providerId: TOKENPLAN_PROVIDER_ID,
api: "openai-completions",
baseUrl: TOKENPLAN_BASE_URL,
catalogModels: TOKENPLAN_MODEL_CATALOG.map(buildTokenPlanModelDefinition),
catalogModels: structuredClone(TOKENPLAN_MODEL_CATALOG),
aliases: [{ modelRef: TOKENPLAN_DEFAULT_MODEL_REF, alias: "Hy3 (TokenPlan)" }],
}),
});
+2 -4
View File
@@ -1,8 +1,6 @@
// Tencent provider module implements model/runtime integration.
import type { ModelProviderConfig } from "openclaw/plugin-sdk/provider-model-shared";
import {
buildTokenHubModelDefinition,
buildTokenPlanModelDefinition,
TOKENHUB_BASE_URL,
TOKENHUB_MODEL_CATALOG,
TOKENPLAN_BASE_URL,
@@ -13,7 +11,7 @@ export function buildTokenHubProvider(): ModelProviderConfig {
return {
baseUrl: TOKENHUB_BASE_URL,
api: "openai-completions",
models: TOKENHUB_MODEL_CATALOG.map(buildTokenHubModelDefinition),
models: structuredClone(TOKENHUB_MODEL_CATALOG),
};
}
@@ -21,6 +19,6 @@ export function buildTokenPlanProvider(): ModelProviderConfig {
return {
baseUrl: TOKENPLAN_BASE_URL,
api: "openai-completions",
models: TOKENPLAN_MODEL_CATALOG.map(buildTokenPlanModelDefinition),
models: structuredClone(TOKENPLAN_MODEL_CATALOG),
};
}
+1 -5
View File
@@ -1,8 +1,4 @@
// Together API module exposes the plugin public contract.
export {
buildTogetherModelDefinition,
TOGETHER_BASE_URL,
TOGETHER_MODEL_CATALOG,
} from "./models.js";
export { TOGETHER_BASE_URL, TOGETHER_MODEL_CATALOG } from "./models.js";
export { buildTogetherProvider } from "./provider-catalog.js";
export { applyTogetherConfig, TOGETHER_DEFAULT_MODEL_REF } from "./onboard.js";
+10 -19
View File
@@ -1,24 +1,15 @@
// Together plugin module implements models behavior.
import { buildManifestModelProviderConfig } from "openclaw/plugin-sdk/provider-catalog-shared";
import { buildManifestModelDefinition } from "openclaw/plugin-sdk/provider-catalog-shared";
import type { ModelDefinitionConfig } from "openclaw/plugin-sdk/provider-model-shared";
import manifest from "./openclaw.plugin.json" with { type: "json" };
const TOGETHER_MANIFEST_PROVIDER = buildManifestModelProviderConfig({
providerId: "together",
catalog: manifest.modelCatalog.providers.together,
});
const TOGETHER_MANIFEST_CATALOG = manifest.modelCatalog.providers.together;
export const TOGETHER_BASE_URL = TOGETHER_MANIFEST_CATALOG.baseUrl;
export const TOGETHER_BASE_URL = TOGETHER_MANIFEST_PROVIDER.baseUrl;
export const TOGETHER_MODEL_CATALOG: ModelDefinitionConfig[] = TOGETHER_MANIFEST_PROVIDER.models;
export function buildTogetherModelDefinition(
model: (typeof TOGETHER_MODEL_CATALOG)[number],
): ModelDefinitionConfig {
return {
...model,
api: "openai-completions",
input: [...model.input],
cost: { ...model.cost },
};
}
export const TOGETHER_MODEL_CATALOG: ModelDefinitionConfig[] = TOGETHER_MANIFEST_CATALOG.models.map(
buildManifestModelDefinition({
providerId: "together",
catalog: TOGETHER_MANIFEST_CATALOG,
decorate: (model) => ({ ...model, api: "openai-completions" }),
}),
);
+24
View File
@@ -0,0 +1,24 @@
import { resolveAgentModelPrimaryValue } from "openclaw/plugin-sdk/provider-onboard";
import { describe, expect, it } from "vitest";
import { TOGETHER_MODEL_CATALOG } from "./models.js";
import { applyTogetherConfig, TOGETHER_DEFAULT_MODEL_REF } from "./onboard.js";
import manifest from "./openclaw.plugin.json" with { type: "json" };
describe("Together onboarding", () => {
it("applies the manifest catalog, default, and alias", () => {
const config = applyTogetherConfig({});
expect(config.models?.providers?.together?.models.map((model) => model.id)).toEqual(
TOGETHER_MODEL_CATALOG.map((model) => model.id),
);
expect(resolveAgentModelPrimaryValue(config.agents?.defaults?.model)).toBe(
TOGETHER_DEFAULT_MODEL_REF,
);
expect(TOGETHER_DEFAULT_MODEL_REF).toBe(
`together/${manifest.modelCatalog.providers.together.defaultModel}`,
);
expect(config.agents?.defaults?.models?.[TOGETHER_DEFAULT_MODEL_REF]).toEqual({
alias: "Together AI",
});
});
});
+8 -7
View File
@@ -1,15 +1,16 @@
// Together setup module handles plugin onboarding behavior.
import { readManifestProviderDefaultModelRef } from "openclaw/plugin-sdk/provider-catalog-shared";
import {
createModelCatalogPresetAppliers,
type OpenClawConfig,
} from "openclaw/plugin-sdk/provider-onboard";
import {
buildTogetherModelDefinition,
TOGETHER_BASE_URL,
TOGETHER_MODEL_CATALOG,
} from "./models.js";
import { TOGETHER_BASE_URL, TOGETHER_MODEL_CATALOG } from "./models.js";
import manifest from "./openclaw.plugin.json" with { type: "json" };
export const TOGETHER_DEFAULT_MODEL_REF = "together/meta-llama/Llama-3.3-70B-Instruct-Turbo";
export const TOGETHER_DEFAULT_MODEL_REF = readManifestProviderDefaultModelRef(
manifest,
"together",
)!;
const togetherPresetAppliers = createModelCatalogPresetAppliers({
primaryModelRef: TOGETHER_DEFAULT_MODEL_REF,
@@ -17,7 +18,7 @@ const togetherPresetAppliers = createModelCatalogPresetAppliers({
providerId: "together",
api: "openai-completions",
baseUrl: TOGETHER_BASE_URL,
catalogModels: TOGETHER_MODEL_CATALOG.map(buildTogetherModelDefinition),
catalogModels: structuredClone(TOGETHER_MODEL_CATALOG),
aliases: [{ modelRef: TOGETHER_DEFAULT_MODEL_REF, alias: "Together AI" }],
}),
});
+1
View File
@@ -44,6 +44,7 @@
"together": {
"baseUrl": "https://api.together.xyz/v1",
"api": "openai-completions",
"defaultModel": "meta-llama/Llama-3.3-70B-Instruct-Turbo",
"models": [
{
"id": "moonshotai/Kimi-K2.6",
-1
View File
@@ -1,6 +1,5 @@
// Venice API module exposes the plugin public contract.
export {
buildVeniceModelDefinition,
discoverVeniceModels,
VENICE_BASE_URL,
VENICE_DEFAULT_MODEL_REF,
+4 -10
View File
@@ -2,11 +2,7 @@
import { expectDefined } from "@openclaw/normalization-core";
import { clearLiveCatalogCacheForTests } from "openclaw/plugin-sdk/provider-catalog-live-runtime";
import { afterEach, describe, expect, it, vi } from "vitest";
import {
buildVeniceModelDefinition,
discoverVeniceModels,
VENICE_MODEL_CATALOG,
} from "./models.js";
import { discoverVeniceModels, VENICE_MODEL_CATALOG } from "./models.js";
import manifest from "./openclaw.plugin.json" with { type: "json" };
const ORIGINAL_NODE_ENV = process.env.NODE_ENV;
@@ -119,9 +115,9 @@ describe("venice-models", () => {
restoreDiscoveryEnv();
});
it("buildVeniceModelDefinition returns config with required fields", () => {
it("builds static definitions with required fields", () => {
const entry = expectDefined(VENICE_MODEL_CATALOG[0], "first Venice catalog model");
const def = buildVeniceModelDefinition(entry);
const def = entry;
expect(def.id).toBe(entry.id);
expect(def.name).toBe(entry.name);
expect(def.reasoning).toBe(entry.reasoning);
@@ -241,9 +237,7 @@ describe("venice-models", () => {
});
it("keeps tools enabled for DeepSeek V3.2", () => {
const model = buildVeniceModelDefinition(
VENICE_MODEL_CATALOG.find((entry) => entry.id === "deepseek-v3.2")!,
);
const model = VENICE_MODEL_CATALOG.find((entry) => entry.id === "deepseek-v3.2")!;
expect(model.compat?.supportsTools).toBeUndefined();
});
+26 -24
View File
@@ -3,7 +3,7 @@ import {
LiveModelCatalogHttpError,
} from "openclaw/plugin-sdk/provider-catalog-live-runtime";
import {
buildManifestModelProviderConfig,
buildManifestModelDefinition,
readManifestProviderDefaultModelRef,
} from "openclaw/plugin-sdk/provider-catalog-shared";
import type { ModelDefinitionConfig } from "openclaw/plugin-sdk/provider-model-shared";
@@ -12,13 +12,9 @@ import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coer
import manifest from "./openclaw.plugin.json" with { type: "json" };
const log = createSubsystemLogger("venice-models");
const VENICE_MANIFEST_CATALOG = manifest.modelCatalog.providers.venice;
const VENICE_MANIFEST_PROVIDER = buildManifestModelProviderConfig({
providerId: "venice",
catalog: manifest.modelCatalog.providers.venice,
});
export const VENICE_BASE_URL = VENICE_MANIFEST_PROVIDER.baseUrl;
export const VENICE_BASE_URL = VENICE_MANIFEST_CATALOG.baseUrl;
export const VENICE_DEFAULT_MODEL_REF = readManifestProviderDefaultModelRef(manifest, "venice")!;
const VENICE_ALLOWED_HOSTNAMES = ["api.venice.ai"];
@@ -51,11 +47,7 @@ const VENICE_DISCOVERY_RETRYABLE_NETWORK_CODES = new Set([
"UND_ERR_SOCKET",
]);
export const VENICE_MODEL_CATALOG: ModelDefinitionConfig[] = VENICE_MANIFEST_PROVIDER.models;
type VeniceCatalogEntry = ModelDefinitionConfig;
export function buildVeniceModelDefinition(entry: VeniceCatalogEntry): ModelDefinitionConfig {
function decorateVeniceModelDefinition(entry: ModelDefinitionConfig): ModelDefinitionConfig {
return {
id: entry.id,
name: entry.name,
@@ -71,6 +63,15 @@ export function buildVeniceModelDefinition(entry: VeniceCatalogEntry): ModelDefi
};
}
/** Venice's decorated network-free fallback catalog. */
export const VENICE_MODEL_CATALOG: ModelDefinitionConfig[] = VENICE_MANIFEST_CATALOG.models.map(
buildManifestModelDefinition({
providerId: "venice",
catalog: VENICE_MANIFEST_CATALOG,
decorate: decorateVeniceModelDefinition,
}),
);
interface VeniceModelSpec {
name: string;
privacy: "private" | "anonymized";
@@ -88,10 +89,6 @@ interface VeniceModel {
model_spec?: VeniceModelSpec;
}
function staticVeniceModelDefinitions(): ModelDefinitionConfig[] {
return VENICE_MODEL_CATALOG.map(buildVeniceModelDefinition);
}
function hasRetryableNetworkCode(err: unknown): boolean {
const queue: unknown[] = [err];
const seen = new Set<unknown>();
@@ -177,7 +174,7 @@ export async function discoverVeniceModels(
options: VeniceModelDiscoveryOptions = {},
): Promise<ModelDefinitionConfig[]> {
if (process.env.NODE_ENV === "test" || process.env.VITEST) {
return staticVeniceModelDefinitions();
return structuredClone(VENICE_MODEL_CATALOG);
}
try {
@@ -203,11 +200,11 @@ export async function discoverVeniceModels(
if (data.length === 0) {
log.warn("No models found from API, using static catalog");
return staticVeniceModelDefinitions();
return structuredClone(VENICE_MODEL_CATALOG);
}
const catalogById = new Map<string, VeniceCatalogEntry>(
VENICE_MODEL_CATALOG.map((m) => [m.id, m]),
const catalogById = new Map<string, ModelDefinitionConfig>(
structuredClone(VENICE_MODEL_CATALOG).map((model) => [model.id, model]),
);
const models: ModelDefinitionConfig[] = [];
@@ -219,7 +216,12 @@ export async function discoverVeniceModels(
});
const apiSupportsTools = resolveApiSupportsTools(apiModel);
if (catalogEntry) {
const definition = buildVeniceModelDefinition(catalogEntry);
const definition: ModelDefinitionConfig = {
...catalogEntry,
input: [...catalogEntry.input],
cost: { ...catalogEntry.cost },
...(catalogEntry.compat ? { compat: { ...catalogEntry.compat } } : {}),
};
if (apiMaxTokens !== undefined) {
definition.maxTokens = apiMaxTokens;
}
@@ -258,13 +260,13 @@ export async function discoverVeniceModels(
}
}
return models.length > 0 ? models : staticVeniceModelDefinitions();
return models.length > 0 ? models : structuredClone(VENICE_MODEL_CATALOG);
} catch (error) {
if (error instanceof LiveModelCatalogHttpError) {
log.warn(`Failed to discover models: HTTP ${error.status}, using static catalog`);
return staticVeniceModelDefinitions();
return structuredClone(VENICE_MODEL_CATALOG);
}
log.warn(`Discovery failed: ${String(error)}, using static catalog`);
return staticVeniceModelDefinitions();
return structuredClone(VENICE_MODEL_CATALOG);
}
}
+2 -7
View File
@@ -3,12 +3,7 @@ import {
createModelCatalogPresetAppliers,
type OpenClawConfig,
} from "openclaw/plugin-sdk/provider-onboard";
import {
buildVeniceModelDefinition,
VENICE_BASE_URL,
VENICE_DEFAULT_MODEL_REF,
VENICE_MODEL_CATALOG,
} from "./api.js";
import { VENICE_BASE_URL, VENICE_DEFAULT_MODEL_REF, VENICE_MODEL_CATALOG } from "./api.js";
const venicePresetAppliers = createModelCatalogPresetAppliers({
primaryModelRef: VENICE_DEFAULT_MODEL_REF,
@@ -16,7 +11,7 @@ const venicePresetAppliers = createModelCatalogPresetAppliers({
providerId: "venice",
api: "openai-completions",
baseUrl: VENICE_BASE_URL,
catalogModels: VENICE_MODEL_CATALOG.map(buildVeniceModelDefinition),
catalogModels: structuredClone(VENICE_MODEL_CATALOG),
aliases: [{ modelRef: VENICE_DEFAULT_MODEL_REF, alias: "Kimi K2.6" }],
}),
});
-1
View File
@@ -50,7 +50,6 @@ export function applyVolcengineToolSchemaCompat<T extends { compat?: ModelCompat
export { buildDoubaoCodingProvider, buildDoubaoProvider } from "./provider-catalog.js";
export {
buildDoubaoModelDefinition,
DOUBAO_BASE_URL,
DOUBAO_CODING_BASE_URL,
DOUBAO_CODING_MODEL_CATALOG,
+1
View File
@@ -13,6 +13,7 @@ import { DOUBAO_CODING_MODEL_CATALOG, DOUBAO_MODEL_CATALOG } from "./models.js";
describe("volcengine plugin", () => {
it("augments the catalog with bundled standard and plan models", async () => {
const provider = await registerSingleProviderPlugin(plugin);
expect(provider.auth?.[0]?.starterModel).toBe("volcengine-plan/ark-code-latest");
const entries = await provider.augmentModelCatalog?.({
env: process.env,
entries: [],
+6 -1
View File
@@ -2,13 +2,18 @@
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth-api-key";
import { buildOpenAICompatibleLiveModelProviderConfig } from "openclaw/plugin-sdk/provider-catalog-live-runtime";
import { readManifestProviderDefaultModelRef } from "openclaw/plugin-sdk/provider-catalog-shared";
import { ensureModelAllowlistEntry } from "openclaw/plugin-sdk/provider-onboard";
import { applyVolcengineToolSchemaCompat } from "./api.js";
import manifest from "./openclaw.plugin.json" with { type: "json" };
import { VOLCENGINE_PROVIDER_CATALOG_ENTRIES } from "./provider-catalog.js";
import { buildVolcengineSpeechProvider } from "./speech-provider.js";
const PROVIDER_ID = "volcengine";
const VOLCENGINE_DEFAULT_MODEL_REF = "volcengine-plan/ark-code-latest";
const VOLCENGINE_DEFAULT_MODEL_REF = readManifestProviderDefaultModelRef(
manifest,
"volcengine-plan",
)!;
export default definePluginEntry({
id: PROVIDER_ID,
-8
View File
@@ -19,11 +19,3 @@ export const DOUBAO_CODING_BASE_URL = DOUBAO_CODING_MANIFEST_PROVIDER.baseUrl;
export const DOUBAO_MODEL_CATALOG: ModelDefinitionConfig[] = DOUBAO_MANIFEST_PROVIDER.models;
export const DOUBAO_CODING_MODEL_CATALOG: ModelDefinitionConfig[] =
DOUBAO_CODING_MANIFEST_PROVIDER.models;
export function buildDoubaoModelDefinition(entry: ModelDefinitionConfig): ModelDefinitionConfig {
return {
...entry,
input: [...entry.input],
cost: { ...entry.cost },
};
}
@@ -230,6 +230,7 @@
"volcengine-plan": {
"baseUrl": "https://ark.cn-beijing.volces.com/api/coding/v3",
"api": "openai-completions",
"defaultModel": "ark-code-latest",
"models": [
{
"id": "ark-code-latest",
@@ -3,6 +3,7 @@ import type { ModelCatalogProvider } from "@openclaw/model-catalog-core/model-ca
import { beforeEach, describe, expect, it, vi } from "vitest";
import {
applyProviderNativeStreamingUsageCompat,
buildManifestModelDefinition,
buildManifestModelProviderConfig,
clearLiveCatalogCacheForTests,
getCachedLiveCatalogValue,
@@ -376,6 +377,20 @@ describe("provider-catalog-shared manifest provider configs", () => {
"example",
),
).toBe("example/example-model");
expect(
buildManifestModelDefinition({
providerId: "example",
catalog,
decorate: (model) => ({
...model,
compat: { ...model.compat, supportsUsageInStreaming: false },
}),
})(catalog.models[0]),
).toEqual({
...buildManifestModelProviderConfig({ providerId: "example", catalog }).models[0],
compat: { supportsUsageInStreaming: false },
});
});
it("normalizes retired nested Gemini ids before emitting manifest provider config", () => {
+26
View File
@@ -246,6 +246,32 @@ export function buildManifestModelProviderConfig(params: {
};
}
/** Builds one normalized runtime model row from a provider manifest catalog entry. */
export function buildManifestModelDefinition(params: {
/** Provider id that owns the manifest catalog row. */
providerId: string;
/** Raw manifest modelCatalog provider block that contains the row. */
catalog: unknown;
/** Optional provider policy applied after manifest normalization. */
decorate?: (model: ModelDefinitionConfig) => ModelDefinitionConfig;
}): (model: unknown) => ModelDefinitionConfig {
if (!params.catalog || typeof params.catalog !== "object" || Array.isArray(params.catalog)) {
throw new Error(`Missing modelCatalog.providers.${params.providerId}`);
}
const catalog = params.catalog;
return (rawModel) => {
const provider = buildManifestModelProviderConfig({
providerId: params.providerId,
catalog: { ...catalog, models: [rawModel] },
});
const model = provider.models[0];
if (!model) {
throw new Error(`Missing modelCatalog.providers.${params.providerId}.models[0]`);
}
return params.decorate?.(model) ?? model;
};
}
function normalizeConfiguredCatalogModelInput(
input: unknown,
): ConfiguredProviderCatalogEntry["input"] | undefined {