mirror of
https://github.com/openclaw/openclaw.git
synced 2026-10-03 09:39:25 +00:00
fix(google): bundled google/* model refs no longer flagged unknown-model by doctor (#139243)
* fix(google): declare bundled Gemini models in the plugin manifest model catalog * fix(google): scope manifest catalog mirror to canonical provider and declare shared-model tiers Address ClawSweeper review on #139243: - drop google-gemini-cli / google-vertex manifest mirrors so legacy CLI refs keep their Doctor migration hint and CLI/Vertex rows stay out of runtime catalog planning (neither declares runtime discovery) - declare explicit capable code-mode tiers on the five sibling rows newly matched by the shared-upstream-model contract (opencode, github-copilot, gmi) - strengthen the drift guard to full-row equality against the runtime static catalog and pin the CLI/Vertex no-mirror boundary - add regression test keeping the google-gemini-cli migration hint alive * fix(google): preserve native Gemini transport in the manifest catalog mirror * fix(google): derive catalog from manifest * fix(google): tighten manifest catalog validation --------- Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
This commit is contained in:
parent
3b53aee5ee
commit
132da0ae7b
10 changed files with 331 additions and 50 deletions
|
|
@ -150,7 +150,11 @@ describe("resolveCopilotForwardCompatModel", () => {
|
|||
it("creates synthetic Gemini models with Chat Completions compatibility", () => {
|
||||
const result = requireResolvedModel(createMockCtx("gemini-3.1-pro-preview"));
|
||||
expect((result as unknown as Record<string, unknown>).api).toBe("openai-completions");
|
||||
// The manifest row now declares its conservative code-mode tier explicitly
|
||||
// (shared-upstream-model contract), and the static override passes the full
|
||||
// manifest compat through to the resolved model.
|
||||
expect((result as unknown as Record<string, unknown>).compat).toEqual({
|
||||
codeMode: "capable",
|
||||
supportsStore: false,
|
||||
supportsDeveloperRole: false,
|
||||
supportsUsageInStreaming: false,
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@
|
|||
"name": "Gemini 3.6 Flash",
|
||||
"api": "openai-completions",
|
||||
"compat": {
|
||||
"codeMode": "capable",
|
||||
"supportsStore": false,
|
||||
"supportsDeveloperRole": false,
|
||||
"supportsUsageInStreaming": false,
|
||||
|
|
@ -114,6 +115,7 @@
|
|||
"name": "Gemini 3.1 Pro Preview",
|
||||
"api": "openai-completions",
|
||||
"compat": {
|
||||
"codeMode": "capable",
|
||||
"supportsStore": false,
|
||||
"supportsDeveloperRole": false,
|
||||
"supportsUsageInStreaming": false,
|
||||
|
|
@ -129,6 +131,7 @@
|
|||
"name": "Gemini 3.5 Flash",
|
||||
"api": "openai-completions",
|
||||
"compat": {
|
||||
"codeMode": "capable",
|
||||
"supportsStore": false,
|
||||
"supportsDeveloperRole": false,
|
||||
"supportsUsageInStreaming": false,
|
||||
|
|
|
|||
|
|
@ -153,6 +153,9 @@
|
|||
"output": 2.5,
|
||||
"cacheRead": 0.03,
|
||||
"cacheWrite": 0
|
||||
},
|
||||
"compat": {
|
||||
"codeMode": "capable"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -30,11 +30,23 @@ type GoogleManifest = {
|
|||
>;
|
||||
};
|
||||
modelCatalog?: {
|
||||
discovery?: Record<string, string>;
|
||||
suppressions?: Array<{
|
||||
provider?: string;
|
||||
model?: string;
|
||||
reason?: string;
|
||||
}>;
|
||||
providers?: Record<
|
||||
string,
|
||||
{
|
||||
api?: string;
|
||||
baseUrl?: string;
|
||||
models?: Array<{
|
||||
id?: string;
|
||||
name?: string;
|
||||
}>;
|
||||
}
|
||||
>;
|
||||
};
|
||||
configSchema?: JsonSchemaObject;
|
||||
configContracts?: {
|
||||
|
|
@ -112,6 +124,32 @@ describe("google manifest model catalog", () => {
|
|||
]);
|
||||
});
|
||||
|
||||
it("owns the canonical Google model catalog and runtime discovery declaration", () => {
|
||||
const catalog = loadManifest().modelCatalog;
|
||||
const provider = catalog?.providers?.google;
|
||||
const modelIds = provider?.models?.map((model) => model.id) ?? [];
|
||||
|
||||
expect(catalog?.discovery?.google).toBe("runtime");
|
||||
expect(provider).toMatchObject({
|
||||
api: "google-generative-ai",
|
||||
baseUrl: "https://generativelanguage.googleapis.com/v1beta",
|
||||
});
|
||||
expect(modelIds).toHaveLength(10);
|
||||
expect(modelIds).not.toContain(undefined);
|
||||
expect(new Set(modelIds).size).toBe(modelIds.length);
|
||||
});
|
||||
|
||||
it("keeps legacy Google chat providers out of the canonical manifest catalog", () => {
|
||||
const manifest = loadManifest();
|
||||
|
||||
// google-gemini-cli references must stay unknown-model so Doctor keeps
|
||||
// emitting its migration hint, and neither CLI nor Vertex declares runtime
|
||||
// discovery, so manifest rows for them would leak into runtime catalog
|
||||
// planning. Only the canonical google provider owns manifest rows.
|
||||
expect(manifest.modelCatalog?.providers?.["google-gemini-cli"]).toBeUndefined();
|
||||
expect(manifest.modelCatalog?.providers?.["google-vertex"]).toBeUndefined();
|
||||
});
|
||||
|
||||
it("offers Google AI Studio API keys without consumer CLI OAuth", () => {
|
||||
const choices = loadManifest().providerAuthChoices ?? [];
|
||||
|
||||
|
|
|
|||
|
|
@ -66,6 +66,208 @@
|
|||
"discovery": {
|
||||
"google": "runtime"
|
||||
},
|
||||
"providers": {
|
||||
"google": {
|
||||
"api": "google-generative-ai",
|
||||
"baseUrl": "https://generativelanguage.googleapis.com/v1beta",
|
||||
"models": [
|
||||
{
|
||||
"id": "gemini-2.5-pro",
|
||||
"name": "Gemini 2.5 Pro",
|
||||
"reasoning": true,
|
||||
"input": [
|
||||
"text",
|
||||
"image"
|
||||
],
|
||||
"cost": {
|
||||
"input": 0,
|
||||
"output": 0,
|
||||
"cacheRead": 0,
|
||||
"cacheWrite": 0
|
||||
},
|
||||
"contextWindow": 1048576,
|
||||
"maxTokens": 65536
|
||||
},
|
||||
{
|
||||
"id": "gemini-2.5-flash",
|
||||
"name": "Gemini 2.5 Flash",
|
||||
"reasoning": true,
|
||||
"input": [
|
||||
"text",
|
||||
"image"
|
||||
],
|
||||
"cost": {
|
||||
"input": 0,
|
||||
"output": 0,
|
||||
"cacheRead": 0,
|
||||
"cacheWrite": 0
|
||||
},
|
||||
"contextWindow": 1048576,
|
||||
"maxTokens": 65536
|
||||
},
|
||||
{
|
||||
"id": "gemini-2.5-flash-lite",
|
||||
"name": "Gemini 2.5 Flash-Lite",
|
||||
"reasoning": true,
|
||||
"input": [
|
||||
"text",
|
||||
"image"
|
||||
],
|
||||
"cost": {
|
||||
"input": 0,
|
||||
"output": 0,
|
||||
"cacheRead": 0,
|
||||
"cacheWrite": 0
|
||||
},
|
||||
"contextWindow": 1048576,
|
||||
"maxTokens": 65536
|
||||
},
|
||||
{
|
||||
"id": "gemini-3.5-flash",
|
||||
"name": "Gemini 3.5 Flash",
|
||||
"reasoning": true,
|
||||
"input": [
|
||||
"text",
|
||||
"image"
|
||||
],
|
||||
"cost": {
|
||||
"input": 0,
|
||||
"output": 0,
|
||||
"cacheRead": 0,
|
||||
"cacheWrite": 0
|
||||
},
|
||||
"contextWindow": 1048576,
|
||||
"maxTokens": 65536,
|
||||
"compat": {
|
||||
"codeMode": "preferred"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "gemini-3.6-flash",
|
||||
"name": "Gemini 3.6 Flash",
|
||||
"reasoning": true,
|
||||
"input": [
|
||||
"text",
|
||||
"image"
|
||||
],
|
||||
"cost": {
|
||||
"input": 0,
|
||||
"output": 0,
|
||||
"cacheRead": 0,
|
||||
"cacheWrite": 0
|
||||
},
|
||||
"contextWindow": 1048576,
|
||||
"maxTokens": 65536,
|
||||
"compat": {
|
||||
"codeMode": "preferred"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "gemini-3.7-flash",
|
||||
"name": "Gemini 3.7 Flash",
|
||||
"reasoning": true,
|
||||
"input": [
|
||||
"text",
|
||||
"image"
|
||||
],
|
||||
"cost": {
|
||||
"input": 0,
|
||||
"output": 0,
|
||||
"cacheRead": 0,
|
||||
"cacheWrite": 0
|
||||
},
|
||||
"contextWindow": 1048576,
|
||||
"maxTokens": 65536,
|
||||
"thinkingLevelMap": {
|
||||
"minimal": null
|
||||
},
|
||||
"compat": {
|
||||
"codeMode": "preferred"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "gemini-3.5-flash-lite",
|
||||
"name": "Gemini 3.5 Flash-Lite",
|
||||
"reasoning": true,
|
||||
"input": [
|
||||
"text",
|
||||
"image"
|
||||
],
|
||||
"cost": {
|
||||
"input": 0,
|
||||
"output": 0,
|
||||
"cacheRead": 0,
|
||||
"cacheWrite": 0
|
||||
},
|
||||
"contextWindow": 1048576,
|
||||
"maxTokens": 65536,
|
||||
"compat": {
|
||||
"codeMode": "preferred"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "gemini-3.1-pro-preview",
|
||||
"name": "Gemini 3.1 Pro Preview",
|
||||
"reasoning": true,
|
||||
"input": [
|
||||
"text",
|
||||
"image"
|
||||
],
|
||||
"cost": {
|
||||
"input": 0,
|
||||
"output": 0,
|
||||
"cacheRead": 0,
|
||||
"cacheWrite": 0
|
||||
},
|
||||
"contextWindow": 1048576,
|
||||
"maxTokens": 65536,
|
||||
"compat": {
|
||||
"codeMode": "preferred"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "gemini-3.1-flash-lite",
|
||||
"name": "Gemini 3.1 Flash Lite",
|
||||
"reasoning": true,
|
||||
"input": [
|
||||
"text",
|
||||
"image"
|
||||
],
|
||||
"cost": {
|
||||
"input": 0,
|
||||
"output": 0,
|
||||
"cacheRead": 0,
|
||||
"cacheWrite": 0
|
||||
},
|
||||
"contextWindow": 1048576,
|
||||
"maxTokens": 65536,
|
||||
"compat": {
|
||||
"codeMode": "preferred"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "gemini-3-flash-preview",
|
||||
"name": "Gemini 3 Flash Preview",
|
||||
"reasoning": true,
|
||||
"input": [
|
||||
"text",
|
||||
"image"
|
||||
],
|
||||
"cost": {
|
||||
"input": 0,
|
||||
"output": 0,
|
||||
"cacheRead": 0,
|
||||
"cacheWrite": 0
|
||||
},
|
||||
"contextWindow": 1048576,
|
||||
"maxTokens": 65536,
|
||||
"compat": {
|
||||
"codeMode": "preferred"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"suppressions": [
|
||||
{
|
||||
"provider": "google",
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import {
|
|||
buildLiveModelProviderConfig,
|
||||
type LiveModelCatalogFetchGuard,
|
||||
} from "openclaw/plugin-sdk/provider-catalog-live-runtime";
|
||||
import { buildManifestModelProviderConfig } from "openclaw/plugin-sdk/provider-catalog-shared";
|
||||
import type {
|
||||
ModelDefinitionConfig,
|
||||
ModelProviderConfig,
|
||||
|
|
@ -11,52 +12,17 @@ import {
|
|||
asPositiveSafeInteger,
|
||||
normalizeOptionalString,
|
||||
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||
import manifest from "./openclaw.plugin.json" with { type: "json" };
|
||||
import { isGoogleTextGenerationModelId, resolveGoogleStaticModelId } from "./provider-models.js";
|
||||
|
||||
const GOOGLE_GEMINI_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
|
||||
const GOOGLE_GEMINI_MODELS_ENDPOINT = `${GOOGLE_GEMINI_BASE_URL}/models?pageSize=1000`;
|
||||
const GOOGLE_VERTEX_BASE_URL = "https://{location}-aiplatform.googleapis.com";
|
||||
const GOOGLE_GEMINI_MODELS_CACHE_TTL_MS = 60_000;
|
||||
const GOOGLE_GEMINI_COST = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 } as const;
|
||||
const GOOGLE_GEMINI_TEXT_MODEL_ROWS: ReadonlyArray<
|
||||
readonly [
|
||||
id: string,
|
||||
name: string,
|
||||
prefersCodeMode: boolean,
|
||||
thinkingLevelMap?: ModelDefinitionConfig["thinkingLevelMap"],
|
||||
]
|
||||
> = [
|
||||
["gemini-2.5-pro", "Gemini 2.5 Pro", false],
|
||||
["gemini-2.5-flash", "Gemini 2.5 Flash", false],
|
||||
["gemini-2.5-flash-lite", "Gemini 2.5 Flash-Lite", false],
|
||||
["gemini-3.5-flash", "Gemini 3.5 Flash", true],
|
||||
["gemini-3.6-flash", "Gemini 3.6 Flash", true],
|
||||
["gemini-3.7-flash", "Gemini 3.7 Flash", true, { minimal: null }],
|
||||
["gemini-3.5-flash-lite", "Gemini 3.5 Flash-Lite", true],
|
||||
["gemini-3.1-pro-preview", "Gemini 3.1 Pro Preview", true],
|
||||
["gemini-3.1-flash-lite", "Gemini 3.1 Flash Lite", true],
|
||||
["gemini-3-flash-preview", "Gemini 3 Flash Preview", true],
|
||||
];
|
||||
const GOOGLE_GEMINI_TEXT_MODELS: ModelDefinitionConfig[] = GOOGLE_GEMINI_TEXT_MODEL_ROWS.map(
|
||||
([id, name, prefersCodeMode, thinkingLevelMap]): ModelDefinitionConfig => {
|
||||
const model: ModelDefinitionConfig = {
|
||||
id,
|
||||
name,
|
||||
reasoning: true,
|
||||
input: ["text", "image"],
|
||||
cost: GOOGLE_GEMINI_COST,
|
||||
contextWindow: 1_048_576,
|
||||
maxTokens: 65_536,
|
||||
};
|
||||
if (thinkingLevelMap) {
|
||||
model.thinkingLevelMap = thinkingLevelMap;
|
||||
}
|
||||
if (prefersCodeMode) {
|
||||
model.compat = { codeMode: "preferred" };
|
||||
}
|
||||
return model;
|
||||
},
|
||||
);
|
||||
const GOOGLE_GEMINI_MANIFEST_PROVIDER = buildManifestModelProviderConfig({
|
||||
providerId: "google",
|
||||
catalog: manifest.modelCatalog.providers.google,
|
||||
});
|
||||
const GOOGLE_GEMINI_MODELS_ENDPOINT = `${GOOGLE_GEMINI_MANIFEST_PROVIDER.baseUrl}/models?pageSize=1000`;
|
||||
const GOOGLE_GEMINI_TEXT_MODELS = GOOGLE_GEMINI_MANIFEST_PROVIDER.models;
|
||||
const GOOGLE_GEMINI_TEXT_MODEL_BY_ID = new Map(
|
||||
GOOGLE_GEMINI_TEXT_MODELS.map((model) => [model.id, model]),
|
||||
);
|
||||
|
|
@ -64,10 +30,19 @@ const GOOGLE_GEMINI_TEXT_MODEL_IDS: ReadonlySet<string> = new Set(
|
|||
GOOGLE_GEMINI_TEXT_MODEL_BY_ID.keys(),
|
||||
);
|
||||
|
||||
function requireGoogleManifestCost(): NonNullable<ModelDefinitionConfig["cost"]> {
|
||||
const cost = GOOGLE_GEMINI_TEXT_MODELS[0]?.cost;
|
||||
if (!cost) {
|
||||
throw new Error("Google manifest model catalog must declare a cost for its first model");
|
||||
}
|
||||
return cost;
|
||||
}
|
||||
|
||||
const GOOGLE_GEMINI_COST = requireGoogleManifestCost();
|
||||
|
||||
export function buildGoogleStaticCatalogProvider(): ModelProviderConfig {
|
||||
return {
|
||||
baseUrl: GOOGLE_GEMINI_BASE_URL,
|
||||
api: "google-generative-ai",
|
||||
...GOOGLE_GEMINI_MANIFEST_PROVIDER,
|
||||
models: GOOGLE_GEMINI_TEXT_MODELS.map((model) => ({
|
||||
...model,
|
||||
input: [...model.input, "video"],
|
||||
|
|
|
|||
|
|
@ -111,6 +111,7 @@
|
|||
"contextWindow": 1048576,
|
||||
"maxTokens": 65536,
|
||||
"compat": {
|
||||
"codeMode": "capable",
|
||||
"supportsUsageInStreaming": true,
|
||||
"supportsReasoningEffort": true,
|
||||
"supportedReasoningEfforts": ["minimal", "low", "medium", "high"],
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { ModelDefinitionConfig } from "../../config/types.models.js";
|
||||
import { createManifestRecord } from "./model.static-catalog.test-helpers.js";
|
||||
|
||||
const repoRoot = path.resolve(import.meta.dirname, "../../..");
|
||||
|
||||
const manifestMocks = vi.hoisted(() => ({
|
||||
getCurrentPluginMetadataSnapshot: vi.fn(),
|
||||
listOpenClawPluginManifestMetadata: vi.fn(),
|
||||
|
|
@ -300,6 +304,47 @@ describe("resolveBundledStaticCatalogModel", () => {
|
|||
expect(model?.maxTokens).toBe(8192);
|
||||
});
|
||||
|
||||
it("keeps the native Gemini transport when Google manifest rows back static fallback", () => {
|
||||
// The bundled google plugin mirrors its runtime static catalog into
|
||||
// modelCatalog.providers.google so Doctor recognizes the ids offline.
|
||||
// Those same rows win over the runtime static provider in bundled
|
||||
// fallback resolution, so the mirror must preserve the provider-level
|
||||
// api/baseUrl or rows normalize to openai-responses with an empty
|
||||
// endpoint (breaking Google completion/compaction fallbacks).
|
||||
const manifest = JSON.parse(
|
||||
fs.readFileSync(path.join(repoRoot, "extensions/google/openclaw.plugin.json"), "utf8"),
|
||||
) as {
|
||||
id: string;
|
||||
providers: string[];
|
||||
modelCatalog?: {
|
||||
providers?: Record<string, { api?: string; baseUrl?: string }>;
|
||||
};
|
||||
};
|
||||
setManifestPlugins([{ origin: "bundled", ...manifest }]);
|
||||
|
||||
const resolved = resolveBundledStaticCatalogModel({
|
||||
provider: "google",
|
||||
modelId: "gemini-2.5-flash",
|
||||
cfg: {},
|
||||
includeRuntimeDiscovery: true,
|
||||
});
|
||||
|
||||
expect(resolved?.provider).toBe("google");
|
||||
expect(resolved?.api).toBe("google-generative-ai");
|
||||
expect(resolved?.baseUrl).toBe("https://generativelanguage.googleapis.com/v1beta");
|
||||
|
||||
// Runtime-discovery rows stay out of the plain bundled fallback path;
|
||||
// only callers that opt in via includeRuntimeDiscovery reach the mirror,
|
||||
// so the manifest addition does not widen default fallback visibility.
|
||||
expect(
|
||||
resolveBundledStaticCatalogModel({
|
||||
provider: "google",
|
||||
modelId: "gemini-2.5-flash",
|
||||
cfg: {},
|
||||
}),
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
it("requires an exact provider and model match", () => {
|
||||
setManifestPlugins([createMistralManifestPlugin()]);
|
||||
|
||||
|
|
|
|||
|
|
@ -998,6 +998,9 @@ describe("CORE_HEALTH_CHECKS", () => {
|
|||
"groq/llama3-70b-8192",
|
||||
"groq/llama-3.3-70b-versatile",
|
||||
"openai/not-in-the-local-catalog",
|
||||
"google/gemini-2.5-flash",
|
||||
"google/gemini-3.8-flash",
|
||||
"google-gemini-cli/gemini-2.5-pro",
|
||||
],
|
||||
},
|
||||
imageModel: { primary: "no-such-provider/no-such-model" },
|
||||
|
|
@ -1010,6 +1013,7 @@ describe("CORE_HEALTH_CHECKS", () => {
|
|||
["openai-codex/gpt-5.6-sol", "openai/gpt-5.6-sol", "warning"],
|
||||
["codex-cli/gpt-5.6-sol", "openai/gpt-5.6-sol", "warning"],
|
||||
["groq/llama3-70b-8192", "groq/llama-3.3-70b-versatile", "info"],
|
||||
["google-gemini-cli/gemini-2.5-pro", "google/gemini-2.5-pro", "info"],
|
||||
] as const) {
|
||||
expect(findings).toContainEqual(
|
||||
expect.objectContaining({
|
||||
|
|
@ -1028,6 +1032,12 @@ describe("CORE_HEALTH_CHECKS", () => {
|
|||
fixHint:
|
||||
"Verify the model id with the provider, or rerun with --severity-min info after refreshing the local catalog.",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
severity: "info",
|
||||
target: "google/gemini-3.8-flash",
|
||||
fixHint:
|
||||
"Verify the model id with the provider, or rerun with --severity-min info after refreshing the local catalog.",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
severity: "warning",
|
||||
target: "no-such-provider/no-such-model",
|
||||
|
|
@ -1039,5 +1049,8 @@ describe("CORE_HEALTH_CHECKS", () => {
|
|||
expect(findings).not.toContainEqual(
|
||||
expect.objectContaining({ target: "groq/llama-3.3-70b-versatile" }),
|
||||
);
|
||||
expect(findings).not.toContainEqual(
|
||||
expect.objectContaining({ target: "google/gemini-2.5-flash" }),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -8,12 +8,9 @@ import { listGitTrackedFiles } from "../../test-utils/repo-files.js";
|
|||
const repoRoot = path.resolve(import.meta.dirname, "../../..");
|
||||
const MANIFEST_BASENAME = "openclaw.plugin.json";
|
||||
const CODE_MODE_TIER_LITERAL = /codeMode:\s*"(?:preferred|capable)"/;
|
||||
// Catalogs still built in plugin source instead of `modelCatalog` manifest rows,
|
||||
// so the manifest scan below cannot see their tiers. Moving them is not free:
|
||||
// `google` rows would newly feed model visibility and pre-discovery thinking
|
||||
// metadata through `loadManifestModelCatalog`, and `minimax` resolves cost per
|
||||
// provider surface, so its rows cannot live in one manifest catalog.
|
||||
const UNCONVERTED_SOURCE_CATALOG_PLUGINS = ["google", "minimax"];
|
||||
// Minimax resolves cost per provider surface, so its rows cannot live in one
|
||||
// manifest catalog and remain invisible to the manifest scan below.
|
||||
const UNCONVERTED_SOURCE_CATALOG_PLUGINS = ["minimax"];
|
||||
|
||||
type CatalogEntry = {
|
||||
/** `provider/model` ref used in failure output. */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue