Vendor OpenClaw source as Adolf fork baseline
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Adolf is a fork/vendored clone of github.com/openclaw/openclaw (v2026.6.11), free to diverge. Tree copied sans upstream .git; upstream remote added for future syncs. Node pinned to 24 (.nvmrc); engines already require >=22.19. Preserves docs/ARCHITECTURE.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
This commit is contained in:
89
packages/media-generation-core/src/capability-model-ref.ts
Normal file
89
packages/media-generation-core/src/capability-model-ref.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
// Media Generation Core module implements capability model ref behavior.
|
||||
import { normalizeOptionalString } from "./string.js";
|
||||
|
||||
/** Provider catalog entry shape used when resolving capability-scoped model references. */
|
||||
export type CapabilityModelProviderCandidate = {
|
||||
id: string;
|
||||
aliases?: readonly string[];
|
||||
defaultModel?: string | null;
|
||||
models?: readonly string[];
|
||||
};
|
||||
|
||||
/** Normalized provider/model reference selected for a media capability. */
|
||||
export type CapabilityModelRef = {
|
||||
provider: string;
|
||||
model: string;
|
||||
};
|
||||
|
||||
type ProviderIdNormalizer = (value: string) => string | undefined;
|
||||
|
||||
function normalizeProviderForMatch(
|
||||
value: string | undefined,
|
||||
normalizeProviderId: ProviderIdNormalizer | undefined,
|
||||
): string | undefined {
|
||||
const normalized = normalizeOptionalString(value);
|
||||
return normalized && normalizeProviderId ? normalizeProviderId(normalized) : normalized;
|
||||
}
|
||||
|
||||
/** Finds a provider by id or alias using the caller's provider-id normalization rules. */
|
||||
export function findCapabilityProviderById<T extends CapabilityModelProviderCandidate>(params: {
|
||||
providers: readonly T[];
|
||||
providerId?: string;
|
||||
normalizeProviderId?: ProviderIdNormalizer;
|
||||
}): T | undefined {
|
||||
const selectedProvider = normalizeProviderForMatch(params.providerId, params.normalizeProviderId);
|
||||
if (!selectedProvider) {
|
||||
return undefined;
|
||||
}
|
||||
return params.providers.find((provider) => {
|
||||
const providerId = normalizeProviderForMatch(provider.id, params.normalizeProviderId);
|
||||
return (
|
||||
providerId === selectedProvider ||
|
||||
(provider.aliases ?? []).some(
|
||||
(alias) =>
|
||||
normalizeProviderForMatch(alias, params.normalizeProviderId) === selectedProvider,
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/** Resolves a bare model name to the provider that advertises it for this capability. */
|
||||
export function resolveCapabilityProviderModelOnlyRef(params: {
|
||||
providers: readonly CapabilityModelProviderCandidate[];
|
||||
raw?: string;
|
||||
}): CapabilityModelRef | null {
|
||||
const model = normalizeOptionalString(params.raw);
|
||||
if (!model) {
|
||||
return null;
|
||||
}
|
||||
const provider = params.providers.find((candidate) => {
|
||||
const models = [candidate.defaultModel, ...(candidate.models ?? [])];
|
||||
return models.some((entry) => normalizeOptionalString(entry) === model);
|
||||
});
|
||||
return provider ? { provider: provider.id, model } : null;
|
||||
}
|
||||
|
||||
/** Resolves provider/model refs first, then falls back to model-only catalog matching. */
|
||||
export function resolveCapabilityModelRefForProviders(params: {
|
||||
providers: readonly CapabilityModelProviderCandidate[];
|
||||
raw?: string;
|
||||
parseModelRef: (raw: string | undefined) => CapabilityModelRef | null;
|
||||
normalizeProviderId?: ProviderIdNormalizer;
|
||||
}): CapabilityModelRef | null {
|
||||
const raw = normalizeOptionalString(params.raw);
|
||||
if (!raw) {
|
||||
return null;
|
||||
}
|
||||
const parsed = params.parseModelRef(raw);
|
||||
if (
|
||||
parsed &&
|
||||
findCapabilityProviderById({
|
||||
providers: params.providers,
|
||||
providerId: parsed.provider,
|
||||
normalizeProviderId: params.normalizeProviderId,
|
||||
})
|
||||
) {
|
||||
return parsed;
|
||||
}
|
||||
return resolveCapabilityProviderModelOnlyRef({ providers: params.providers, raw }) ?? parsed;
|
||||
}
|
||||
81
packages/media-generation-core/src/catalog.test.ts
Normal file
81
packages/media-generation-core/src/catalog.test.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
// Media Generation Core tests cover catalog behavior.
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
listMediaGenerationProviderModels,
|
||||
synthesizeMediaGenerationCatalogEntries,
|
||||
} from "./catalog.js";
|
||||
|
||||
describe("media-generation catalog", () => {
|
||||
it("synthesizes stable static rows from provider defaults and models", () => {
|
||||
const capabilities = {
|
||||
generate: { enabled: true },
|
||||
edit: { enabled: true, maxInputImages: 2 },
|
||||
};
|
||||
|
||||
const rows = synthesizeMediaGenerationCatalogEntries({
|
||||
kind: "image_generation",
|
||||
provider: {
|
||||
id: "example",
|
||||
label: "Example",
|
||||
defaultModel: "default-image",
|
||||
models: ["default-image", "alternate-image", " ", "alternate-image"],
|
||||
capabilities,
|
||||
},
|
||||
modes: ["generate", "edit"],
|
||||
});
|
||||
|
||||
expect(rows).toEqual([
|
||||
{
|
||||
kind: "image_generation",
|
||||
provider: "example",
|
||||
model: "default-image",
|
||||
label: "Example",
|
||||
source: "static",
|
||||
default: true,
|
||||
capabilities,
|
||||
modes: ["generate", "edit"],
|
||||
},
|
||||
{
|
||||
kind: "image_generation",
|
||||
provider: "example",
|
||||
model: "alternate-image",
|
||||
label: "Example",
|
||||
source: "static",
|
||||
capabilities,
|
||||
modes: ["generate", "edit"],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("lists unique provider models in display order", () => {
|
||||
expect(
|
||||
listMediaGenerationProviderModels({
|
||||
defaultModel: "video-default",
|
||||
models: ["video-default", "video-pro"],
|
||||
}),
|
||||
).toEqual(["video-default", "video-pro"]);
|
||||
});
|
||||
|
||||
it("marks a trimmed default model as the catalog default", () => {
|
||||
expect(
|
||||
synthesizeMediaGenerationCatalogEntries({
|
||||
kind: "video_generation",
|
||||
provider: {
|
||||
id: "example",
|
||||
defaultModel: " video-default ",
|
||||
models: ["video-default"],
|
||||
capabilities: {},
|
||||
},
|
||||
}),
|
||||
).toEqual([
|
||||
{
|
||||
kind: "video_generation",
|
||||
provider: "example",
|
||||
model: "video-default",
|
||||
source: "static",
|
||||
default: true,
|
||||
capabilities: {},
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
82
packages/media-generation-core/src/catalog.ts
Normal file
82
packages/media-generation-core/src/catalog.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
// Media Generation Core module implements catalog behavior.
|
||||
import { uniqueTrimmedStrings } from "./string.js";
|
||||
|
||||
// Shared media-generation catalog contracts and static entry synthesis.
|
||||
|
||||
/** Catalog kind for generated media model entries. */
|
||||
export type MediaGenerationCatalogKind =
|
||||
| "image_generation"
|
||||
| "video_generation"
|
||||
| "music_generation";
|
||||
|
||||
/** Source for a media generation catalog entry. */
|
||||
export type MediaGenerationCatalogSource = "static" | "live" | "cache" | "configured";
|
||||
|
||||
/** Media generation model catalog entry. */
|
||||
export type MediaGenerationCatalogEntry<TCapabilities = unknown> = {
|
||||
kind: MediaGenerationCatalogKind;
|
||||
provider: string;
|
||||
model: string;
|
||||
label?: string;
|
||||
source: MediaGenerationCatalogSource;
|
||||
default?: boolean;
|
||||
configured?: boolean;
|
||||
capabilities?: TCapabilities;
|
||||
modes?: readonly string[];
|
||||
authEnvVars?: readonly string[];
|
||||
docsPath?: string;
|
||||
fetchedAt?: number;
|
||||
expiresAt?: number;
|
||||
warnings?: readonly string[];
|
||||
};
|
||||
|
||||
/** Provider metadata used to synthesize static media generation catalog entries. */
|
||||
export type MediaGenerationCatalogProvider<TCapabilities = unknown> = {
|
||||
id: string;
|
||||
aliases?: readonly string[];
|
||||
label?: string;
|
||||
defaultModel?: string;
|
||||
models?: readonly string[];
|
||||
capabilities: TCapabilities;
|
||||
};
|
||||
|
||||
/** Return unique configured models with default model first when present. */
|
||||
function uniqueModels(provider: { defaultModel?: string; models?: readonly string[] }): string[] {
|
||||
return uniqueTrimmedStrings([provider.defaultModel, ...(provider.models ?? [])]);
|
||||
}
|
||||
|
||||
/** Synthesize static catalog entries from provider metadata. */
|
||||
export function synthesizeMediaGenerationCatalogEntries<TCapabilities>(params: {
|
||||
kind: MediaGenerationCatalogKind;
|
||||
provider: MediaGenerationCatalogProvider<TCapabilities>;
|
||||
modes?: readonly string[];
|
||||
}): Array<MediaGenerationCatalogEntry<TCapabilities>> {
|
||||
const defaultModel = uniqueTrimmedStrings([params.provider.defaultModel])[0];
|
||||
return uniqueModels(params.provider).map((model) => {
|
||||
const entry: MediaGenerationCatalogEntry<TCapabilities> = {
|
||||
kind: params.kind,
|
||||
provider: params.provider.id,
|
||||
model,
|
||||
source: "static",
|
||||
capabilities: params.provider.capabilities,
|
||||
};
|
||||
if (params.provider.label) {
|
||||
entry.label = params.provider.label;
|
||||
}
|
||||
if (model === defaultModel) {
|
||||
entry.default = true;
|
||||
}
|
||||
if (params.modes) {
|
||||
entry.modes = params.modes;
|
||||
}
|
||||
return entry;
|
||||
});
|
||||
}
|
||||
|
||||
/** Return unique model ids exposed by a media generation provider. */
|
||||
export function listMediaGenerationProviderModels(provider: {
|
||||
defaultModel?: string;
|
||||
models?: readonly string[];
|
||||
}): string[] {
|
||||
return uniqueModels(provider);
|
||||
}
|
||||
6
packages/media-generation-core/src/index.ts
Normal file
6
packages/media-generation-core/src/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
// Public barrel for media-generation shared model refs and catalog helpers.
|
||||
|
||||
export * from "./capability-model-ref.js";
|
||||
export * from "./catalog.js";
|
||||
export * from "./model-ref.js";
|
||||
export * from "./normalization.js";
|
||||
75
packages/media-generation-core/src/model-ref.test.ts
Normal file
75
packages/media-generation-core/src/model-ref.test.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
// Media Generation Core tests cover model ref behavior.
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
resolveCapabilityModelRefForProviders,
|
||||
resolveCapabilityProviderModelOnlyRef,
|
||||
} from "./capability-model-ref.js";
|
||||
import { parseGenerationModelRef } from "./model-ref.js";
|
||||
|
||||
describe("media-generation model refs", () => {
|
||||
it("parses provider/model refs without splitting slash-containing model ids", () => {
|
||||
expect(parseGenerationModelRef("fal/fal-ai/flux/dev")).toEqual({
|
||||
provider: "fal",
|
||||
model: "fal-ai/flux/dev",
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects incomplete provider/model refs", () => {
|
||||
expect(parseGenerationModelRef(undefined)).toBeNull();
|
||||
expect(parseGenerationModelRef("openai")).toBeNull();
|
||||
expect(parseGenerationModelRef("/gpt-image-2")).toBeNull();
|
||||
expect(parseGenerationModelRef("openai/")).toBeNull();
|
||||
});
|
||||
|
||||
it("resolves model-only refs from provider metadata", () => {
|
||||
expect(
|
||||
resolveCapabilityProviderModelOnlyRef({
|
||||
raw: "fal-ai/flux/dev",
|
||||
providers: [
|
||||
{
|
||||
id: "fal",
|
||||
defaultModel: "fal-ai/flux/dev",
|
||||
models: ["fal-ai/flux/dev/image-to-image"],
|
||||
},
|
||||
],
|
||||
}),
|
||||
).toEqual({ provider: "fal", model: "fal-ai/flux/dev" });
|
||||
});
|
||||
|
||||
it("keeps explicit provider refs ahead of colliding model-only refs", () => {
|
||||
expect(
|
||||
resolveCapabilityModelRefForProviders({
|
||||
raw: "google/lyria-3-pro-preview",
|
||||
parseModelRef: parseGenerationModelRef,
|
||||
providers: [
|
||||
{
|
||||
id: "google",
|
||||
defaultModel: "lyria-3-clip-preview",
|
||||
models: ["lyria-3-pro-preview"],
|
||||
},
|
||||
{
|
||||
id: "openrouter",
|
||||
defaultModel: "google/lyria-3-pro-preview",
|
||||
},
|
||||
],
|
||||
}),
|
||||
).toEqual({ provider: "google", model: "lyria-3-pro-preview" });
|
||||
});
|
||||
|
||||
it("matches provider aliases through a caller-supplied normalizer", () => {
|
||||
expect(
|
||||
resolveCapabilityModelRefForProviders({
|
||||
raw: "openai/gpt-image-2",
|
||||
parseModelRef: parseGenerationModelRef,
|
||||
normalizeProviderId: (value) => value.toLowerCase(),
|
||||
providers: [
|
||||
{
|
||||
id: "openai",
|
||||
aliases: ["openai"],
|
||||
defaultModel: "gpt-image-2",
|
||||
},
|
||||
],
|
||||
}),
|
||||
).toEqual({ provider: "openai", model: "gpt-image-2" });
|
||||
});
|
||||
});
|
||||
13
packages/media-generation-core/src/model-ref.ts
Normal file
13
packages/media-generation-core/src/model-ref.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
// Media Generation Core module implements model ref behavior.
|
||||
import {
|
||||
parseProviderModelRef,
|
||||
type ProviderModelRef,
|
||||
} from "@openclaw/model-catalog-core/model-catalog-refs";
|
||||
|
||||
/** Provider/model pair parsed from a generation model reference like `provider/model`. */
|
||||
export type ParsedGenerationModelRef = ProviderModelRef;
|
||||
|
||||
/** Parses strict generation model refs and rejects missing provider or model segments. */
|
||||
export function parseGenerationModelRef(raw: string | undefined): ProviderModelRef | null {
|
||||
return raw === undefined ? null : parseProviderModelRef(raw);
|
||||
}
|
||||
31
packages/media-generation-core/src/normalization.ts
Normal file
31
packages/media-generation-core/src/normalization.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
/** Primitive value types reported in media generation normalization metadata. */
|
||||
export type MediaNormalizationValue = string | number | boolean;
|
||||
|
||||
/** Requested/applied value pair plus provenance for a normalized media option. */
|
||||
export type MediaNormalizationEntry<TValue extends MediaNormalizationValue> = {
|
||||
requested?: TValue;
|
||||
applied?: TValue;
|
||||
derivedFrom?: string;
|
||||
supportedValues?: readonly TValue[];
|
||||
};
|
||||
|
||||
/** Normalization metadata shared by media generation responses. */
|
||||
export type MediaGenerationNormalizationMetadataInput = {
|
||||
size?: MediaNormalizationEntry<string>;
|
||||
aspectRatio?: MediaNormalizationEntry<string>;
|
||||
resolution?: MediaNormalizationEntry<string>;
|
||||
durationSeconds?: MediaNormalizationEntry<number>;
|
||||
};
|
||||
|
||||
/** True when a normalization entry contains any user-visible normalization metadata. */
|
||||
export function hasMediaNormalizationEntry<TValue extends MediaNormalizationValue>(
|
||||
entry: MediaNormalizationEntry<TValue> | undefined,
|
||||
): entry is MediaNormalizationEntry<TValue> {
|
||||
return Boolean(
|
||||
entry &&
|
||||
(entry.requested !== undefined ||
|
||||
entry.applied !== undefined ||
|
||||
entry.derivedFrom !== undefined ||
|
||||
(entry.supportedValues?.length ?? 0) > 0),
|
||||
);
|
||||
}
|
||||
25
packages/media-generation-core/src/string.ts
Normal file
25
packages/media-generation-core/src/string.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
// Shared string normalization helpers for media-generation packages.
|
||||
|
||||
/** Normalize optional strings, returning undefined for non-strings or empty values. */
|
||||
export function normalizeOptionalString(value: unknown): string | undefined {
|
||||
if (typeof value !== "string") {
|
||||
return undefined;
|
||||
}
|
||||
const trimmed = value.trim();
|
||||
return trimmed ? trimmed : undefined;
|
||||
}
|
||||
|
||||
/** Return unique trimmed strings while preserving first-seen order. */
|
||||
export function uniqueTrimmedStrings(values: readonly unknown[]): string[] {
|
||||
const seen = new Set<string>();
|
||||
const result: string[] = [];
|
||||
for (const value of values) {
|
||||
const normalized = normalizeOptionalString(value);
|
||||
if (!normalized || seen.has(normalized)) {
|
||||
continue;
|
||||
}
|
||||
seen.add(normalized);
|
||||
result.push(normalized);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
Reference in New Issue
Block a user