diff --git a/extensions/tsconfig.package-boundary.paths.json b/extensions/tsconfig.package-boundary.paths.json index 8fbd5f351c2..e7f6cee30a9 100644 --- a/extensions/tsconfig.package-boundary.paths.json +++ b/extensions/tsconfig.package-boundary.paths.json @@ -111,6 +111,12 @@ "@openclaw/model-catalog-core/model-catalog-refs": [ "../dist/plugin-sdk/packages/model-catalog-core/src/model-catalog-refs.d.ts" ], + "@openclaw/model-catalog-core/model-catalog-normalize": [ + "../dist/plugin-sdk/packages/model-catalog-core/src/model-catalog-normalize.d.ts" + ], + "@openclaw/model-catalog-core/model-catalog-types": [ + "../dist/plugin-sdk/packages/model-catalog-core/src/model-catalog-types.d.ts" + ], "@openclaw/model-catalog-core/provider-id": [ "../dist/plugin-sdk/packages/model-catalog-core/src/provider-id.d.ts" ], diff --git a/extensions/xai/tsconfig.json b/extensions/xai/tsconfig.json index f6d78a0aa47..62a0bf97e3d 100644 --- a/extensions/xai/tsconfig.json +++ b/extensions/xai/tsconfig.json @@ -120,6 +120,12 @@ "@openclaw/model-catalog-core/model-catalog-refs": [ "../../dist/plugin-sdk/packages/model-catalog-core/src/model-catalog-refs.d.ts" ], + "@openclaw/model-catalog-core/model-catalog-normalize": [ + "../../dist/plugin-sdk/packages/model-catalog-core/src/model-catalog-normalize.d.ts" + ], + "@openclaw/model-catalog-core/model-catalog-types": [ + "../../dist/plugin-sdk/packages/model-catalog-core/src/model-catalog-types.d.ts" + ], "@openclaw/model-catalog-core/provider-id": [ "../../dist/plugin-sdk/packages/model-catalog-core/src/provider-id.d.ts" ], diff --git a/packages/model-catalog-core/package.json b/packages/model-catalog-core/package.json index a6788dcd8d5..497a7bf3615 100644 --- a/packages/model-catalog-core/package.json +++ b/packages/model-catalog-core/package.json @@ -24,6 +24,16 @@ "import": "./dist/model-catalog-refs.mjs", "default": "./dist/model-catalog-refs.mjs" }, + "./model-catalog-normalize": { + "types": "./dist/model-catalog-normalize.d.mts", + "import": "./dist/model-catalog-normalize.mjs", + "default": "./dist/model-catalog-normalize.mjs" + }, + "./model-catalog-types": { + "types": "./dist/model-catalog-types.d.mts", + "import": "./dist/model-catalog-types.mjs", + "default": "./dist/model-catalog-types.mjs" + }, "./provider-id": { "types": "./dist/provider-id.d.mts", "import": "./dist/provider-id.mjs", diff --git a/packages/model-catalog-core/src/index.ts b/packages/model-catalog-core/src/index.ts index 49158c1eca9..4208e2c0060 100644 --- a/packages/model-catalog-core/src/index.ts +++ b/packages/model-catalog-core/src/index.ts @@ -1,5 +1,7 @@ export * from "./configured-model-refs.js"; +export * from "./model-catalog-normalize.js"; export * from "./model-catalog-refs.js"; +export * from "./model-catalog-types.js"; export * from "./provider-id.js"; export * from "./provider-model-id-normalization.js"; export * from "./provider-model-id-normalize.js"; diff --git a/src/model-catalog/normalize.test.ts b/packages/model-catalog-core/src/model-catalog-normalize.test.ts similarity index 98% rename from src/model-catalog/normalize.test.ts rename to packages/model-catalog-core/src/model-catalog-normalize.test.ts index bc7a4f9ab0b..61f22c8531e 100644 --- a/src/model-catalog/normalize.test.ts +++ b/packages/model-catalog-core/src/model-catalog-normalize.test.ts @@ -1,9 +1,6 @@ -import { - buildModelCatalogMergeKey, - buildModelCatalogRef, -} from "@openclaw/model-catalog-core/model-catalog-refs"; import { describe, expect, it } from "vitest"; import { normalizeModelCatalog, normalizeModelCatalogRows } from "./index.js"; +import { buildModelCatalogMergeKey, buildModelCatalogRef } from "./model-catalog-refs.js"; describe("model catalog normalization", () => { it("normalizes catalog ownership, aliases, suppressions, and row fields", () => { diff --git a/src/model-catalog/normalize.ts b/packages/model-catalog-core/src/model-catalog-normalize.ts similarity index 89% rename from src/model-catalog/normalize.ts rename to packages/model-catalog-core/src/model-catalog-normalize.ts index 8af1c706e65..a7b6d47a893 100644 --- a/src/model-catalog/normalize.ts +++ b/packages/model-catalog-core/src/model-catalog-normalize.ts @@ -2,44 +2,66 @@ import { buildModelCatalogMergeKey, buildModelCatalogRef, normalizeModelCatalogProviderId, -} from "@openclaw/model-catalog-core/model-catalog-refs"; +} from "./model-catalog-refs.js"; import { - MODEL_APIS, - isModelThinkingFormat, - type ModelApi, - type ModelCompatConfig, - type ModelImageInputConfig, - type ModelMediaInputConfig, -} from "../config/types.models.js"; -import { isBlockedObjectKey } from "../infra/prototype-keys.js"; -import { normalizeOptionalString } from "../shared/string-coerce.js"; -import { - normalizeOptionalTrimmedStringList, - normalizeTrimmedStringList, -} from "../shared/string-normalization.js"; -import { isRecord } from "../utils.js"; -import type { - ModelCatalog, - ModelCatalogAlias, - ModelCatalogCost, - ModelCatalogDiscovery, - ModelCatalogInput, - ModelCatalogModel, - ModelCatalogProvider, - ModelCatalogSource, - ModelCatalogStatus, - ModelCatalogSuppression, - ModelCatalogTieredCost, - NormalizedModelCatalogRow, -} from "./types.js"; + MODEL_CATALOG_APIS, + isModelCatalogThinkingFormat, + type ModelCatalog, + type ModelCatalogAlias, + type ModelCatalogApi, + type ModelCatalogCompatConfig, + type ModelCatalogCost, + type ModelCatalogDiscovery, + type ModelCatalogImageInputConfig, + type ModelCatalogInput, + type ModelCatalogMediaInputConfig, + type ModelCatalogModel, + type ModelCatalogProvider, + type ModelCatalogSource, + type ModelCatalogStatus, + type ModelCatalogSuppression, + type ModelCatalogTieredCost, + type NormalizedModelCatalogRow, +} from "./model-catalog-types.js"; const MODEL_CATALOG_INPUTS = new Set(["text", "image", "document"]); const MODEL_CATALOG_DISCOVERY_MODES = new Set(["static", "refreshable", "runtime"]); const MODEL_CATALOG_STATUSES = new Set(["available", "preview", "deprecated", "disabled"]); -const MODEL_CATALOG_APIS = new Set(MODEL_APIS); +const MODEL_CATALOG_API_SET = new Set(MODEL_CATALOG_APIS); const DEFAULT_MODEL_INPUT: ModelCatalogInput[] = ["text"]; const DEFAULT_MODEL_STATUS: ModelCatalogStatus = "available"; +function isRecord(value: unknown): value is Record { + return typeof value === "object" && value !== null && !Array.isArray(value); +} + +function isBlockedObjectKey(key: string): boolean { + return key === "__proto__" || key === "prototype" || key === "constructor"; +} + +function normalizeOptionalString(value: unknown): string | undefined { + if (typeof value !== "string") { + return undefined; + } + const trimmed = value.trim(); + return trimmed ? trimmed : undefined; +} + +function normalizeTrimmedStringList(value: unknown): string[] { + if (!Array.isArray(value)) { + return []; + } + return value.flatMap((entry) => { + const normalized = normalizeOptionalString(entry); + return normalized ? [normalized] : []; + }); +} + +function normalizeOptionalTrimmedStringList(value: unknown): string[] | undefined { + const normalized = normalizeTrimmedStringList(value); + return normalized.length > 0 ? normalized : undefined; +} + function normalizeSafeRecordKey(value: unknown): string { const key = normalizeOptionalString(value) ?? ""; return key && !isBlockedObjectKey(key) ? key : ""; @@ -81,9 +103,9 @@ function mergeStringMaps( return { ...base, ...override }; } -function normalizeModelCatalogApi(value: unknown): ModelApi | undefined { +function normalizeModelCatalogApi(value: unknown): ModelCatalogApi | undefined { const api = normalizeOptionalString(value) ?? ""; - return MODEL_CATALOG_APIS.has(api) ? (api as ModelApi) : undefined; + return MODEL_CATALOG_API_SET.has(api) ? (api as ModelCatalogApi) : undefined; } function normalizeModelCatalogInputs(value: unknown): ModelCatalogInput[] | undefined { @@ -165,7 +187,7 @@ function normalizeModelCatalogCost(value: unknown): ModelCatalogCost | undefined return Object.keys(cost).length > 0 ? cost : undefined; } -function normalizeModelCatalogCompat(value: unknown): ModelCompatConfig | undefined { +function normalizeModelCatalogCompat(value: unknown): ModelCatalogCompatConfig | undefined { if (!isRecord(value)) { return undefined; } @@ -230,11 +252,11 @@ function normalizeModelCatalogCompat(value: unknown): ModelCompatConfig | undefi } const thinkingFormat = normalizeOptionalString(value.thinkingFormat) ?? ""; - if (isModelThinkingFormat(thinkingFormat)) { + if (isModelCatalogThinkingFormat(thinkingFormat)) { compat.thinkingFormat = thinkingFormat; } - return Object.keys(compat).length > 0 ? (compat as ModelCompatConfig) : undefined; + return Object.keys(compat).length > 0 ? (compat as ModelCatalogCompatConfig) : undefined; } function normalizeModelCatalogStatus(value: unknown): ModelCatalogStatus | undefined { @@ -242,7 +264,9 @@ function normalizeModelCatalogStatus(value: unknown): ModelCatalogStatus | undef return MODEL_CATALOG_STATUSES.has(status) ? (status as ModelCatalogStatus) : undefined; } -function normalizeModelCatalogImageTokenMode(value: unknown): ModelImageInputConfig["tokenMode"] { +function normalizeModelCatalogImageTokenMode( + value: unknown, +): ModelCatalogImageInputConfig["tokenMode"] { const tokenMode = normalizeOptionalString(value) ?? ""; if (tokenMode === "tile" || tokenMode === "detail" || tokenMode === "provider") { return tokenMode; @@ -250,7 +274,7 @@ function normalizeModelCatalogImageTokenMode(value: unknown): ModelImageInputCon return undefined; } -function normalizeModelCatalogMediaInput(value: unknown): ModelMediaInputConfig | undefined { +function normalizeModelCatalogMediaInput(value: unknown): ModelCatalogMediaInputConfig | undefined { if (!isRecord(value) || !isRecord(value.image)) { return undefined; } diff --git a/packages/model-catalog-core/src/model-catalog-types.ts b/packages/model-catalog-core/src/model-catalog-types.ts new file mode 100644 index 00000000000..51c23d32390 --- /dev/null +++ b/packages/model-catalog-core/src/model-catalog-types.ts @@ -0,0 +1,204 @@ +export const MODEL_CATALOG_APIS = [ + "openai-completions", + "openai-responses", + "openai-codex-responses", + "anthropic-messages", + "google-generative-ai", + "google-vertex", + "github-copilot", + "bedrock-converse-stream", + "ollama", + "azure-openai-responses", +] as const; + +export type ModelCatalogApi = (typeof MODEL_CATALOG_APIS)[number]; + +export const MODEL_CATALOG_THINKING_FORMATS = [ + "openai", + "openrouter", + "deepseek", + "together", + "qwen", + "qwen-chat-template", + "zai", +] as const; + +export type ModelCatalogThinkingFormat = (typeof MODEL_CATALOG_THINKING_FORMATS)[number]; + +export function isModelCatalogThinkingFormat(value: string): value is ModelCatalogThinkingFormat { + return (MODEL_CATALOG_THINKING_FORMATS as readonly string[]).includes(value); +} + +export type ModelCatalogCompatConfig = { + supportsStore?: boolean; + supportsDeveloperRole?: boolean; + supportsReasoningEffort?: boolean; + supportsUsageInStreaming?: boolean; + supportsStrictMode?: boolean; + maxTokensField?: "max_completion_tokens" | "max_tokens"; + requiresToolResultName?: boolean; + requiresAssistantAfterToolResult?: boolean; + requiresThinkingAsText?: boolean; + supportsPromptCacheKey?: boolean; + supportsTools?: boolean; + requiresStringContent?: boolean; + strictMessageKeys?: boolean; + toolSchemaProfile?: string; + unsupportedToolSchemaKeywords?: string[]; + nativeWebSearchTool?: boolean; + toolCallArgumentsEncoding?: string; + requiresMistralToolIds?: boolean; + requiresOpenAiAnthropicToolPayload?: boolean; + thinkingFormat?: ModelCatalogThinkingFormat; + supportedReasoningEfforts?: string[]; + reasoningEffortMap?: Record; + visibleReasoningDetailTypes?: string[]; +}; + +export type ModelCatalogImageInputConfig = { + maxBytes?: number; + maxPixels?: number; + maxSidePx?: number; + preferredSidePx?: number; + tokenMode?: "tile" | "detail" | "provider"; +}; + +export type ModelCatalogMediaInputConfig = { + image?: ModelCatalogImageInputConfig; +}; + +export type ModelCatalogInput = "text" | "image" | "document"; +export type ModelCatalogDiscovery = "static" | "refreshable" | "runtime"; +export type ModelCatalogStatus = "available" | "preview" | "deprecated" | "disabled"; +export type ModelCatalogSource = + | "manifest" + | "provider-index" + | "cache" + | "config" + | "runtime-refresh"; + +export type UnifiedModelCatalogKind = + | "text" + | "voice" + | "image_generation" + | "video_generation" + | "music_generation"; + +export type UnifiedModelCatalogSource = + | "manifest" + | "provider-index" + | "static" + | "live" + | "cache" + | "configured" + | "runtime-refresh"; + +export type UnifiedModelCatalogEntry = { + kind: UnifiedModelCatalogKind; + provider: string; + model: string; + label?: string; + source: UnifiedModelCatalogSource; + default?: boolean; + configured?: boolean; + capabilities?: TCapabilities; + modes?: readonly string[]; + authEnvVars?: readonly string[]; + docsPath?: string; + fetchedAt?: number; + expiresAt?: number; + warnings?: readonly string[]; +}; + +export type ModelCatalogTieredCost = { + input: number; + output: number; + cacheRead: number; + cacheWrite: number; + range: [number, number] | [number]; +}; + +export type ModelCatalogCost = { + input?: number; + output?: number; + cacheRead?: number; + cacheWrite?: number; + tieredPricing?: ModelCatalogTieredCost[]; +}; + +export type ModelCatalogModel = { + id: string; + name?: string; + api?: ModelCatalogApi; + baseUrl?: string; + headers?: Record; + input?: ModelCatalogInput[]; + reasoning?: boolean; + contextWindow?: number; + contextTokens?: number; + maxTokens?: number; + cost?: ModelCatalogCost; + compat?: ModelCatalogCompatConfig; + mediaInput?: ModelCatalogMediaInputConfig; + status?: ModelCatalogStatus; + statusReason?: string; + replaces?: string[]; + replacedBy?: string; + tags?: string[]; +}; + +export type ModelCatalogProvider = { + baseUrl?: string; + api?: ModelCatalogApi; + headers?: Record; + models: ModelCatalogModel[]; +}; + +export type ModelCatalogAlias = { + provider: string; + api?: ModelCatalogApi; + baseUrl?: string; +}; + +export type ModelCatalogSuppression = { + provider: string; + model: string; + reason?: string; + when?: { + baseUrlHosts?: string[]; + providerConfigApiIn?: string[]; + }; +}; + +export type ModelCatalog = { + providers?: Record; + aliases?: Record; + suppressions?: ModelCatalogSuppression[]; + discovery?: Record; + runtimeAugment?: boolean; +}; + +export type NormalizedModelCatalogRow = { + provider: string; + id: string; + ref: string; + mergeKey: string; + name: string; + source: ModelCatalogSource; + input: ModelCatalogInput[]; + reasoning: boolean; + status: ModelCatalogStatus; + api?: ModelCatalogApi; + baseUrl?: string; + headers?: Record; + contextWindow?: number; + contextTokens?: number; + maxTokens?: number; + cost?: ModelCatalogCost; + compat?: ModelCatalogCompatConfig; + mediaInput?: ModelCatalogMediaInputConfig; + statusReason?: string; + replaces?: string[]; + replacedBy?: string; + tags?: string[]; +}; diff --git a/scripts/lib/extension-package-boundary.ts b/scripts/lib/extension-package-boundary.ts index cc0a03ff245..38855c311a9 100644 --- a/scripts/lib/extension-package-boundary.ts +++ b/scripts/lib/extension-package-boundary.ts @@ -66,6 +66,12 @@ export const EXTENSION_PACKAGE_BOUNDARY_BASE_PATHS = { "@openclaw/model-catalog-core/model-catalog-refs": [ "../dist/plugin-sdk/packages/model-catalog-core/src/model-catalog-refs.d.ts", ], + "@openclaw/model-catalog-core/model-catalog-normalize": [ + "../dist/plugin-sdk/packages/model-catalog-core/src/model-catalog-normalize.d.ts", + ], + "@openclaw/model-catalog-core/model-catalog-types": [ + "../dist/plugin-sdk/packages/model-catalog-core/src/model-catalog-types.d.ts", + ], "@openclaw/model-catalog-core/provider-id": [ "../dist/plugin-sdk/packages/model-catalog-core/src/provider-id.d.ts", ], diff --git a/scripts/prepare-extension-package-boundary-artifacts.mjs b/scripts/prepare-extension-package-boundary-artifacts.mjs index a00de3c138d..c2feea15178 100644 --- a/scripts/prepare-extension-package-boundary-artifacts.mjs +++ b/scripts/prepare-extension-package-boundary-artifacts.mjs @@ -70,7 +70,9 @@ const ROOT_DTS_REQUIRED_OUTPUTS = [ "dist/plugin-sdk/packages/terminal-core/src/terminal-link.d.ts", "dist/plugin-sdk/packages/terminal-core/src/theme.d.ts", "dist/plugin-sdk/packages/model-catalog-core/src/configured-model-refs.d.ts", + "dist/plugin-sdk/packages/model-catalog-core/src/model-catalog-normalize.d.ts", "dist/plugin-sdk/packages/model-catalog-core/src/model-catalog-refs.d.ts", + "dist/plugin-sdk/packages/model-catalog-core/src/model-catalog-types.d.ts", "dist/plugin-sdk/packages/model-catalog-core/src/provider-id.d.ts", "dist/plugin-sdk/packages/model-catalog-core/src/provider-model-id-normalization.d.ts", "dist/plugin-sdk/packages/model-catalog-core/src/provider-model-id-normalize.d.ts", @@ -97,7 +99,9 @@ const PACKAGE_DTS_REQUIRED_OUTPUTS = [ "packages/plugin-sdk/dist/packages/media-generation-core/src/model-ref.d.ts", "packages/plugin-sdk/dist/packages/media-generation-core/src/normalization.d.ts", "packages/plugin-sdk/dist/packages/model-catalog-core/src/configured-model-refs.d.ts", + "packages/plugin-sdk/dist/packages/model-catalog-core/src/model-catalog-normalize.d.ts", "packages/plugin-sdk/dist/packages/model-catalog-core/src/model-catalog-refs.d.ts", + "packages/plugin-sdk/dist/packages/model-catalog-core/src/model-catalog-types.d.ts", "packages/plugin-sdk/dist/packages/model-catalog-core/src/provider-id.d.ts", "packages/plugin-sdk/dist/packages/model-catalog-core/src/provider-model-id-normalization.d.ts", "packages/plugin-sdk/dist/packages/model-catalog-core/src/provider-model-id-normalize.d.ts", diff --git a/src/commands/configure.gateway-auth.prompt-auth-config.test.ts b/src/commands/configure.gateway-auth.prompt-auth-config.test.ts index 5f5e3853177..ba904eb8191 100644 --- a/src/commands/configure.gateway-auth.prompt-auth-config.test.ts +++ b/src/commands/configure.gateway-auth.prompt-auth-config.test.ts @@ -1,6 +1,6 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../config/types.openclaw.js"; -import type { NormalizedModelCatalogRow } from "../model-catalog/index.js"; +import type { NormalizedModelCatalogRow } from "../model-catalog/types.js"; import type { RuntimeEnv } from "../runtime.js"; import type { WizardPrompter } from "../wizard/prompts.js"; diff --git a/src/commands/model-picker.test.ts b/src/commands/model-picker.test.ts index 42c8c30324b..8a160e1874f 100644 --- a/src/commands/model-picker.test.ts +++ b/src/commands/model-picker.test.ts @@ -2,7 +2,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { testing as cliBackendsTesting } from "../agents/cli-backends.js"; import type { ModelCatalogEntry } from "../agents/model-catalog.js"; import type { OpenClawConfig } from "../config/config.js"; -import type { NormalizedModelCatalogRow } from "../model-catalog/index.js"; +import type { NormalizedModelCatalogRow } from "../model-catalog/types.js"; import { applyModelAllowlist, applyModelFallbacksFromSelection, diff --git a/src/commands/models/list.manifest-catalog.ts b/src/commands/models/list.manifest-catalog.ts index 9729f8a343b..71e54bb14d4 100644 --- a/src/commands/models/list.manifest-catalog.ts +++ b/src/commands/models/list.manifest-catalog.ts @@ -1,7 +1,7 @@ import { normalizeModelCatalogProviderId } from "@openclaw/model-catalog-core/model-catalog-refs"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; import { planManifestModelCatalogRows } from "../../model-catalog/index.js"; -import type { NormalizedModelCatalogRow } from "../../model-catalog/index.js"; +import type { NormalizedModelCatalogRow } from "../../model-catalog/types.js"; import { loadManifestMetadataSnapshot } from "../../plugins/manifest-contract-eligibility.js"; import type { PluginManifestRegistry } from "../../plugins/manifest-registry.js"; import type { PluginMetadataSnapshot } from "../../plugins/plugin-metadata-snapshot.types.js"; diff --git a/src/commands/models/list.provider-index-catalog.ts b/src/commands/models/list.provider-index-catalog.ts index 04721149da4..bd54b193fa9 100644 --- a/src/commands/models/list.provider-index-catalog.ts +++ b/src/commands/models/list.provider-index-catalog.ts @@ -4,7 +4,7 @@ import { loadOpenClawProviderIndex, planProviderIndexModelCatalogRows, } from "../../model-catalog/index.js"; -import type { NormalizedModelCatalogRow } from "../../model-catalog/index.js"; +import type { NormalizedModelCatalogRow } from "../../model-catalog/types.js"; import { normalizePluginsConfig, resolveEffectiveEnableState } from "../../plugins/config-state.js"; export function loadProviderIndexCatalogRowsForList(params: { diff --git a/src/commands/models/list.rows.ts b/src/commands/models/list.rows.ts index d70fa0cfe7d..81fac7d1a0a 100644 --- a/src/commands/models/list.rows.ts +++ b/src/commands/models/list.rows.ts @@ -8,7 +8,7 @@ import type { ModelDefinitionConfig, ModelProviderConfig } from "../../config/ty import type { OpenClawConfig } from "../../config/types.openclaw.js"; import type { ModelRegistry } from "../../llm/model-registry.js"; import type { Model } from "../../llm/types.js"; -import type { NormalizedModelCatalogRow } from "../../model-catalog/index.js"; +import type { NormalizedModelCatalogRow } from "../../model-catalog/types.js"; import type { PluginMetadataSnapshot } from "../../plugins/plugin-metadata-snapshot.types.js"; import type { ProviderRuntimeModel } from "../../plugins/provider-runtime-model.types.js"; import { normalizeProviderResolvedModelWithPlugin } from "../../plugins/provider-runtime.js"; diff --git a/src/commands/models/list.source-plan.ts b/src/commands/models/list.source-plan.ts index 2d74bb1da52..56efceeaad4 100644 --- a/src/commands/models/list.source-plan.ts +++ b/src/commands/models/list.source-plan.ts @@ -1,5 +1,5 @@ import type { OpenClawConfig } from "../../config/types.openclaw.js"; -import type { NormalizedModelCatalogRow } from "../../model-catalog/index.js"; +import type { NormalizedModelCatalogRow } from "../../model-catalog/types.js"; import type { PluginMetadataSnapshot } from "../../plugins/plugin-metadata-snapshot.types.js"; import { createLazyImportLoader } from "../../shared/lazy-promise.js"; diff --git a/src/gateway/model-pricing-cache.ts b/src/gateway/model-pricing-cache.ts index c79aa0c1c5b..c3d61ccb35e 100644 --- a/src/gateway/model-pricing-cache.ts +++ b/src/gateway/model-pricing-cache.ts @@ -11,7 +11,8 @@ import { resolvePluginWebSearchConfig } from "../config/plugin-web-search-config import type { ModelDefinitionConfig } from "../config/types.models.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { createSubsystemLogger } from "../logging/subsystem.js"; -import { planManifestModelCatalogRows, type ModelCatalogCost } from "../model-catalog/index.js"; +import { planManifestModelCatalogRows } from "../model-catalog/index.js"; +import type { ModelCatalogCost } from "../model-catalog/types.js"; import { isInstalledPluginEnabled } from "../plugins/installed-plugin-index.js"; import type { PluginManifestRegistry } from "../plugins/manifest-registry.js"; import type { diff --git a/src/model-catalog/index.ts b/src/model-catalog/index.ts index 565afab2931..418b9264123 100644 --- a/src/model-catalog/index.ts +++ b/src/model-catalog/index.ts @@ -1,5 +1,4 @@ export { mergeModelCatalogRowsByAuthority } from "./authority.js"; -export { normalizeModelCatalog, normalizeModelCatalogRows } from "./normalize.js"; export { loadOpenClawProviderIndex } from "./provider-index/index.js"; export { planManifestModelCatalogRows, diff --git a/src/model-catalog/manifest-planner.ts b/src/model-catalog/manifest-planner.ts index 452a39eb0ec..14ef985d5e7 100644 --- a/src/model-catalog/manifest-planner.ts +++ b/src/model-catalog/manifest-planner.ts @@ -1,10 +1,10 @@ +import { normalizeModelCatalogProviderRows } from "@openclaw/model-catalog-core/model-catalog-normalize"; import { buildModelCatalogMergeKey, normalizeModelCatalogProviderId, } from "@openclaw/model-catalog-core/model-catalog-refs"; import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js"; import { normalizeUniqueStringEntries } from "../shared/string-normalization.js"; -import { normalizeModelCatalogProviderRows } from "./normalize.js"; import type { ModelCatalog, ModelCatalogAlias, diff --git a/src/model-catalog/provider-index-planner.ts b/src/model-catalog/provider-index-planner.ts index 0d570580ba6..5e2b616186c 100644 --- a/src/model-catalog/provider-index-planner.ts +++ b/src/model-catalog/provider-index-planner.ts @@ -1,5 +1,5 @@ +import { normalizeModelCatalogProviderRows } from "@openclaw/model-catalog-core/model-catalog-normalize"; import { normalizeModelCatalogProviderId } from "@openclaw/model-catalog-core/model-catalog-refs"; -import { normalizeModelCatalogProviderRows } from "./normalize.js"; import type { OpenClawProviderIndex } from "./provider-index/index.js"; import type { ModelCatalogProvider, NormalizedModelCatalogRow } from "./types.js"; diff --git a/src/model-catalog/provider-index/normalize.ts b/src/model-catalog/provider-index/normalize.ts index c0c478447e4..ba04b634975 100644 --- a/src/model-catalog/provider-index/normalize.ts +++ b/src/model-catalog/provider-index/normalize.ts @@ -1,3 +1,4 @@ +import { normalizeModelCatalog } from "@openclaw/model-catalog-core/model-catalog-normalize"; import { normalizeModelCatalogProviderId } from "@openclaw/model-catalog-core/model-catalog-refs"; import { parseClawHubPluginSpec } from "../../infra/clawhub-spec.js"; import { parseRegistryNpmSpec } from "../../infra/npm-registry-spec.js"; @@ -6,7 +7,6 @@ import { asFiniteNumber } from "../../shared/number-coercion.js"; import { normalizeOptionalString } from "../../shared/string-coerce.js"; import { normalizeUniqueTrimmedStringList } from "../../shared/string-normalization.js"; import { isRecord } from "../../utils.js"; -import { normalizeModelCatalog } from "../normalize.js"; import type { ModelCatalogProvider } from "../types.js"; import type { OpenClawProviderIndex, diff --git a/src/plugin-sdk/provider-catalog-shared.ts b/src/plugin-sdk/provider-catalog-shared.ts index 0b18057a7ab..a39f13e52c4 100644 --- a/src/plugin-sdk/provider-catalog-shared.ts +++ b/src/plugin-sdk/provider-catalog-shared.ts @@ -4,12 +4,12 @@ // without recursing through provider-specific facades. import { createHash } from "node:crypto"; +import { normalizeModelCatalog } from "@openclaw/model-catalog-core/model-catalog-normalize"; import { findNormalizedProviderKey } from "@openclaw/model-catalog-core/provider-id"; import { normalizeConfiguredProviderCatalogModelId } from "../agents/model-ref-shared.js"; import { resolveProviderRequestCapabilities } from "../agents/provider-attribution.js"; import type { ModelDefinitionConfig } from "../config/types.models.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; -import { normalizeModelCatalog } from "../model-catalog/normalize.js"; import type { ModelCatalogCost, ModelCatalogModel, diff --git a/src/plugins/contracts/model-catalog-core-imports.test.ts b/src/plugins/contracts/model-catalog-core-imports.test.ts index 9858311986b..28043697d58 100644 --- a/src/plugins/contracts/model-catalog-core-imports.test.ts +++ b/src/plugins/contracts/model-catalog-core-imports.test.ts @@ -10,6 +10,10 @@ const LEGACY_MODEL_CATALOG_BRIDGES = new Map([ path.join(REPO_ROOT, "src/model-catalog/refs.ts"), "@openclaw/model-catalog-core/model-catalog-refs", ], + [ + path.join(REPO_ROOT, "src/model-catalog/normalize.ts"), + "@openclaw/model-catalog-core/model-catalog-normalize", + ], [ path.join(REPO_ROOT, "src/config/model-refs.ts"), "@openclaw/model-catalog-core/configured-model-refs", diff --git a/src/plugins/manifest.ts b/src/plugins/manifest.ts index ca74d338883..672f6fb63d6 100644 --- a/src/plugins/manifest.ts +++ b/src/plugins/manifest.ts @@ -1,24 +1,24 @@ import fs from "node:fs"; import path from "node:path"; +import { normalizeModelCatalog } from "@openclaw/model-catalog-core/model-catalog-normalize"; import { normalizeModelCatalogProviderId } from "@openclaw/model-catalog-core/model-catalog-refs"; import type { ChannelConfigRuntimeSchema } from "../channels/plugins/types.config.js"; import { MANIFEST_KEY } from "../compat/legacy-names.js"; import { ENV_SECRET_REF_ID_RE } from "../config/types.secrets.js"; import { matchRootFileOpenFailure, openRootFileSync } from "../infra/boundary-file-read.js"; import { isBlockedObjectKey } from "../infra/prototype-keys.js"; -import { - normalizeModelCatalog, - type ModelCatalog, - type ModelCatalogAlias, - type ModelCatalogCost, - type ModelCatalogDiscovery, - type ModelCatalogInput, - type ModelCatalogModel, - type ModelCatalogProvider, - type ModelCatalogStatus, - type ModelCatalogSuppression, - type ModelCatalogTieredCost, -} from "../model-catalog/index.js"; +import type { + ModelCatalog, + ModelCatalogAlias, + ModelCatalogCost, + ModelCatalogDiscovery, + ModelCatalogInput, + ModelCatalogModel, + ModelCatalogProvider, + ModelCatalogStatus, + ModelCatalogSuppression, + ModelCatalogTieredCost, +} from "../model-catalog/types.js"; import type { JsonSchemaObject } from "../shared/json-schema.types.js"; import { normalizeOptionalString } from "../shared/string-coerce.js"; import { normalizeTrimmedStringList } from "../shared/string-normalization.js"; diff --git a/src/plugins/sdk-alias.ts b/src/plugins/sdk-alias.ts index 5d7c84b34e8..cf99fc31f09 100644 --- a/src/plugins/sdk-alias.ts +++ b/src/plugins/sdk-alias.ts @@ -840,6 +840,20 @@ const WORKSPACE_PACKAGE_ALIAS_ENTRIES = [ srcFile: "model-catalog-refs.ts", distFile: "model-catalog-refs.mjs", }, + { + packageName: "@openclaw/model-catalog-core", + packageDir: "model-catalog-core", + subpath: "model-catalog-normalize", + srcFile: "model-catalog-normalize.ts", + distFile: "model-catalog-normalize.mjs", + }, + { + packageName: "@openclaw/model-catalog-core", + packageDir: "model-catalog-core", + subpath: "model-catalog-types", + srcFile: "model-catalog-types.ts", + distFile: "model-catalog-types.mjs", + }, { packageName: "@openclaw/model-catalog-core", packageDir: "model-catalog-core", diff --git a/test/vitest/vitest.shared.config.ts b/test/vitest/vitest.shared.config.ts index e44f6c5cd7d..1b7c19af2af 100644 --- a/test/vitest/vitest.shared.config.ts +++ b/test/vitest/vitest.shared.config.ts @@ -257,6 +257,26 @@ export const sharedVitestConfig = { "model-catalog-refs.ts", ), }, + { + find: "@openclaw/model-catalog-core/model-catalog-normalize", + replacement: path.join( + repoRoot, + "packages", + "model-catalog-core", + "src", + "model-catalog-normalize.ts", + ), + }, + { + find: "@openclaw/model-catalog-core/model-catalog-types", + replacement: path.join( + repoRoot, + "packages", + "model-catalog-core", + "src", + "model-catalog-types.ts", + ), + }, { find: "@openclaw/model-catalog-core/provider-id", replacement: path.join(repoRoot, "packages", "model-catalog-core", "src", "provider-id.ts"), diff --git a/tsconfig.json b/tsconfig.json index 6d3c3e70088..03ac05f83c5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -40,9 +40,15 @@ "@openclaw/model-catalog-core/configured-model-refs": [ "./packages/model-catalog-core/src/configured-model-refs.ts" ], + "@openclaw/model-catalog-core/model-catalog-normalize": [ + "./packages/model-catalog-core/src/model-catalog-normalize.ts" + ], "@openclaw/model-catalog-core/model-catalog-refs": [ "./packages/model-catalog-core/src/model-catalog-refs.ts" ], + "@openclaw/model-catalog-core/model-catalog-types": [ + "./packages/model-catalog-core/src/model-catalog-types.ts" + ], "@openclaw/model-catalog-core/provider-id": [ "./packages/model-catalog-core/src/provider-id.ts" ], diff --git a/tsdown.config.ts b/tsdown.config.ts index 4de0e68ed58..7d126abebe6 100644 --- a/tsdown.config.ts +++ b/tsdown.config.ts @@ -470,7 +470,9 @@ function buildModelCatalogCoreDistEntries(): Record { return { index: "packages/model-catalog-core/src/index.ts", "configured-model-refs": "packages/model-catalog-core/src/configured-model-refs.ts", + "model-catalog-normalize": "packages/model-catalog-core/src/model-catalog-normalize.ts", "model-catalog-refs": "packages/model-catalog-core/src/model-catalog-refs.ts", + "model-catalog-types": "packages/model-catalog-core/src/model-catalog-types.ts", "provider-id": "packages/model-catalog-core/src/provider-id.ts", "provider-model-id-normalization": "packages/model-catalog-core/src/provider-model-id-normalization.ts",