diff --git a/extensions/mistral/index.ts b/extensions/mistral/index.ts index d3c919f785f..27e58726685 100644 --- a/extensions/mistral/index.ts +++ b/extensions/mistral/index.ts @@ -1,7 +1,9 @@ import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry"; import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth"; +import { buildSingleProviderApiKeyCatalog } from "openclaw/plugin-sdk/provider-catalog"; import { mistralMediaUnderstandingProvider } from "./media-understanding-provider.js"; import { applyMistralConfig, MISTRAL_DEFAULT_MODEL_REF } from "./onboard.js"; +import { buildMistralProvider } from "./provider-catalog.js"; const PROVIDER_ID = "mistral"; @@ -37,6 +39,16 @@ export default definePluginEntry({ }, }), ], + catalog: { + order: "simple", + run: (ctx) => + buildSingleProviderApiKeyCatalog({ + ctx, + providerId: PROVIDER_ID, + buildProvider: buildMistralProvider, + allowExplicitBaseUrl: true, + }), + }, capabilities: { transcriptToolCallIdMode: "strict9", transcriptToolCallIdModelHints: [ diff --git a/extensions/mistral/model-definitions.test.ts b/extensions/mistral/model-definitions.test.ts index 09d0f62f014..f468d05ad25 100644 --- a/extensions/mistral/model-definitions.test.ts +++ b/extensions/mistral/model-definitions.test.ts @@ -1,5 +1,6 @@ import { describe, expect, it } from "vitest"; import { + buildMistralCatalogModels, buildMistralModelDefinition, MISTRAL_DEFAULT_CONTEXT_WINDOW, MISTRAL_DEFAULT_COST, @@ -23,4 +24,30 @@ describe("mistral model definitions", () => { cacheWrite: 0, }); }); + + it("publishes a curated set of current Mistral catalog models", () => { + expect(buildMistralCatalogModels()).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: "codestral-latest", + input: ["text"], + contextWindow: 256000, + maxTokens: 4096, + }), + expect.objectContaining({ + id: "magistral-small", + reasoning: true, + input: ["text"], + contextWindow: 128000, + maxTokens: 128000, + }), + expect.objectContaining({ + id: "pixtral-large-latest", + input: ["text", "image"], + contextWindow: 128000, + maxTokens: 128000, + }), + ]), + ); + }); }); diff --git a/extensions/mistral/model-definitions.ts b/extensions/mistral/model-definitions.ts index 05eecf54044..3ce19c3af5f 100644 --- a/extensions/mistral/model-definitions.ts +++ b/extensions/mistral/model-definitions.ts @@ -12,14 +12,86 @@ export const MISTRAL_DEFAULT_COST = { cacheWrite: 0, }; -export function buildMistralModelDefinition(): ModelDefinitionConfig { - return { - id: MISTRAL_DEFAULT_MODEL_ID, - name: "Mistral Large", +const MISTRAL_MODEL_CATALOG = [ + { + id: "codestral-latest", + name: "Codestral (latest)", + reasoning: false, + input: ["text"], + cost: { input: 0.3, output: 0.9, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 256000, + maxTokens: 4096, + }, + { + id: "devstral-medium-latest", + name: "Devstral 2 (latest)", + reasoning: false, + input: ["text"], + cost: { input: 0.4, output: 2, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 262144, + maxTokens: 262144, + }, + { + id: "magistral-small", + name: "Magistral Small", + reasoning: true, + input: ["text"], + cost: { input: 0.5, output: 1.5, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 128000, + maxTokens: 128000, + }, + { + id: "mistral-large-latest", + name: "Mistral Large (latest)", reasoning: false, input: ["text", "image"], cost: MISTRAL_DEFAULT_COST, contextWindow: MISTRAL_DEFAULT_CONTEXT_WINDOW, maxTokens: MISTRAL_DEFAULT_MAX_TOKENS, - }; + }, + { + id: "mistral-medium-2508", + name: "Mistral Medium 3.1", + reasoning: false, + input: ["text", "image"], + cost: { input: 0.4, output: 2, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 262144, + maxTokens: 262144, + }, + { + id: "mistral-small-latest", + name: "Mistral Small (latest)", + reasoning: false, + input: ["text", "image"], + cost: { input: 0.1, output: 0.3, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 128000, + maxTokens: 16384, + }, + { + id: "pixtral-large-latest", + name: "Pixtral Large (latest)", + reasoning: false, + input: ["text", "image"], + cost: { input: 2, output: 6, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 128000, + maxTokens: 128000, + }, +] as const satisfies readonly ModelDefinitionConfig[]; + +export function buildMistralModelDefinition(): ModelDefinitionConfig { + return ( + MISTRAL_MODEL_CATALOG.find((model) => model.id === MISTRAL_DEFAULT_MODEL_ID) ?? { + id: MISTRAL_DEFAULT_MODEL_ID, + name: "Mistral Large", + reasoning: false, + input: ["text", "image"], + cost: MISTRAL_DEFAULT_COST, + contextWindow: MISTRAL_DEFAULT_CONTEXT_WINDOW, + maxTokens: MISTRAL_DEFAULT_MAX_TOKENS, + } + ); +} + +export function buildMistralCatalogModels(): ModelDefinitionConfig[] { + return MISTRAL_MODEL_CATALOG.map((model) => ({ ...model, input: [...model.input] })); } diff --git a/extensions/mistral/provider-catalog.ts b/extensions/mistral/provider-catalog.ts new file mode 100644 index 00000000000..9ae61466337 --- /dev/null +++ b/extensions/mistral/provider-catalog.ts @@ -0,0 +1,10 @@ +import type { ModelProviderConfig } from "openclaw/plugin-sdk/provider-models"; +import { buildMistralCatalogModels, MISTRAL_BASE_URL } from "./model-definitions.js"; + +export function buildMistralProvider(): ModelProviderConfig { + return { + baseUrl: MISTRAL_BASE_URL, + api: "openai-completions", + models: buildMistralCatalogModels(), + }; +}