mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-21 10:16:44 +00:00
refactor(agents): privatize models config test seams (#108275)
* refactor(agents): privatize models config test seams * chore(deadcode): shrink models config export baseline
This commit is contained in:
@@ -48,14 +48,6 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [
|
||||
"src/agents/mcp-ui-resource.ts: testing",
|
||||
"src/agents/media-generation-task-status-shared.ts: resetRecentMediaGenerationDuplicateGuardsForTests",
|
||||
"src/agents/model-fallback.ts: testing",
|
||||
"src/agents/models-config-state.ts: resetModelsJsonReadyCacheForTest",
|
||||
"src/agents/models-config.plan.ts: planOpenClawModelsJsonWithDeps",
|
||||
"src/agents/models-config.plan.ts: resolveProvidersForModelsJsonWithDeps",
|
||||
"src/agents/models-config.providers.implicit.ts: resolvePluginMetadataProviderOwnersForTest",
|
||||
"src/agents/models-config.providers.implicit.ts: resolveProviderDiscoveryFilterForTest",
|
||||
"src/agents/models-config.ts: ensureModelsFileModeForModelsJson",
|
||||
"src/agents/models-config.ts: resetModelsJsonReadyCacheForTest",
|
||||
"src/agents/models-config.ts: writeModelsFileAtomicForModelsJson",
|
||||
"src/agents/modes/interactive/theme/theme.ts: setTheme",
|
||||
"src/agents/openai-completions-compat.ts: resolveOpenAICompletionsCompatDefaults",
|
||||
"src/agents/openai-transport-stream.ts: testing",
|
||||
|
||||
@@ -5,10 +5,8 @@ import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import { monitorEventLoopDelay, performance } from "node:perf_hooks";
|
||||
import { resolveModelAsync } from "../../src/agents/embedded-agent-runner/model.js";
|
||||
import {
|
||||
ensureOpenClawModelsJson,
|
||||
resetModelsJsonReadyCacheForTest,
|
||||
} from "../../src/agents/models-config.js";
|
||||
import { resetModelsJsonReadyCacheForTest } from "../../src/agents/models-config-state.test-support.js";
|
||||
import { ensureOpenClawModelsJson } from "../../src/agents/models-config.js";
|
||||
import type { OpenClawConfig } from "../../src/config/types.openclaw.js";
|
||||
import {
|
||||
Issue78851CliArgumentError,
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
|
||||
import { MODELS_JSON_STATE } from "./models-config-state.js";
|
||||
|
||||
export function resetModelsJsonReadyCacheForTest(): void {
|
||||
MODELS_JSON_STATE.writeQueue = new KeyedAsyncQueue();
|
||||
MODELS_JSON_STATE.readyCache.clear();
|
||||
}
|
||||
@@ -31,9 +31,3 @@ export const MODELS_JSON_STATE = (() => {
|
||||
}
|
||||
return globalState[MODELS_JSON_STATE_KEY];
|
||||
})();
|
||||
|
||||
/** Clear models.json write/ready caches for tests. */
|
||||
export function resetModelsJsonReadyCacheForTest(): void {
|
||||
MODELS_JSON_STATE.writeQueue = new KeyedAsyncQueue();
|
||||
MODELS_JSON_STATE.readyCache.clear();
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import { unsetEnv, withTempEnv } from "./models-config.e2e-harness.js";
|
||||
import {
|
||||
planOpenClawModelsJsonWithDeps,
|
||||
resolveProvidersForModelsJsonWithDeps,
|
||||
} from "./models-config.plan.js";
|
||||
} from "./models-config.plan.test-support.js";
|
||||
import type { ProviderConfig } from "./models-config.providers.secrets.js";
|
||||
import { encodePluginModelCatalogRelativePath } from "./plugin-model-catalog.js";
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import { clearConfigCache, clearRuntimeConfigSnapshot } from "../config/config.j
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { withTempHome as withTempHomeBase } from "../plugin-sdk/test-helpers/temp-home.js";
|
||||
import { resetPluginLoaderTestStateForTest } from "../plugins/loader.test-fixtures.js";
|
||||
import { resetModelsJsonReadyCacheForTest } from "./models-config-state.js";
|
||||
import { resetModelsJsonReadyCacheForTest } from "./models-config-state.test-support.js";
|
||||
|
||||
/** Runs a models-config test with an isolated temp HOME and no session cleanup. */
|
||||
export function withModelsTempHome<T>(fn: (home: string) => Promise<T>): Promise<T> {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { cleanupTempDirs, makeTempDir } from "../../test/helpers/temp-dir.js";
|
||||
import {
|
||||
ensureModelsFileModeForModelsJson,
|
||||
writeModelsFileAtomicForModelsJson,
|
||||
} from "./models-config.js";
|
||||
} from "./models-config.test-support.js";
|
||||
|
||||
const tempDirs = new Set<string>();
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import type { PluginMetadataSnapshot } from "../plugins/plugin-metadata-snapshot.js";
|
||||
import "./models-config.plan.js";
|
||||
import type { ProviderConfig } from "./models-config.providers.secrets.js";
|
||||
|
||||
type ResolveImplicitProvidersForModelsJson = (params: {
|
||||
agentDir: string;
|
||||
config: OpenClawConfig;
|
||||
env: NodeJS.ProcessEnv;
|
||||
workspaceDir?: string;
|
||||
explicitProviders: Record<string, ProviderConfig>;
|
||||
pluginMetadataSnapshot?: Pick<PluginMetadataSnapshot, "index" | "manifestRegistry" | "owners">;
|
||||
providerDiscoveryProviderIds?: readonly string[];
|
||||
providerDiscoveryTimeoutMs?: number;
|
||||
providerDiscoveryEntriesOnly?: boolean;
|
||||
}) => Promise<Record<string, ProviderConfig>>;
|
||||
|
||||
type PlanParams = Parameters<typeof import("./models-config.plan.js").planOpenClawModelsJson>[0];
|
||||
type PlanResult = Awaited<
|
||||
ReturnType<typeof import("./models-config.plan.js").planOpenClawModelsJson>
|
||||
>;
|
||||
type ResolveProvidersParams = {
|
||||
cfg: OpenClawConfig;
|
||||
agentDir: string;
|
||||
env: NodeJS.ProcessEnv;
|
||||
workspaceDir?: string;
|
||||
pluginMetadataSnapshot?: Pick<PluginMetadataSnapshot, "index" | "manifestRegistry" | "owners">;
|
||||
providerDiscoveryProviderIds?: readonly string[];
|
||||
providerDiscoveryTimeoutMs?: number;
|
||||
providerDiscoveryEntriesOnly?: boolean;
|
||||
};
|
||||
type PlanDeps = { resolveImplicitProviders?: ResolveImplicitProvidersForModelsJson };
|
||||
|
||||
type ModelsConfigPlanTestApi = {
|
||||
planOpenClawModelsJsonWithDeps(params: PlanParams, deps?: PlanDeps): Promise<PlanResult>;
|
||||
resolveProvidersForModelsJsonWithDeps(
|
||||
params: ResolveProvidersParams,
|
||||
deps?: PlanDeps,
|
||||
): Promise<Record<string, ProviderConfig>>;
|
||||
};
|
||||
|
||||
function getTestApi(): ModelsConfigPlanTestApi {
|
||||
return (globalThis as Record<PropertyKey, unknown>)[
|
||||
Symbol.for("openclaw.modelsConfigPlanTestApi")
|
||||
] as ModelsConfigPlanTestApi;
|
||||
}
|
||||
|
||||
export const planOpenClawModelsJsonWithDeps = async (
|
||||
params: PlanParams,
|
||||
deps?: PlanDeps,
|
||||
): Promise<PlanResult> => await getTestApi().planOpenClawModelsJsonWithDeps(params, deps);
|
||||
|
||||
export const resolveProvidersForModelsJsonWithDeps = async (
|
||||
params: ResolveProvidersParams,
|
||||
deps?: PlanDeps,
|
||||
): Promise<Record<string, ProviderConfig>> =>
|
||||
await getTestApi().resolveProvidersForModelsJsonWithDeps(params, deps);
|
||||
@@ -92,7 +92,7 @@ function buildPluginCatalogWrites(
|
||||
}
|
||||
|
||||
/** Resolves providers for models.json with injectable implicit-provider discovery. */
|
||||
export async function resolveProvidersForModelsJsonWithDeps(
|
||||
async function resolveProvidersForModelsJsonWithDeps(
|
||||
params: {
|
||||
cfg: OpenClawConfig;
|
||||
agentDir: string;
|
||||
@@ -200,7 +200,7 @@ function filterWritableProviders(
|
||||
}
|
||||
|
||||
/** Plans root and plugin-owned model catalog writes with injectable provider discovery. */
|
||||
export async function planOpenClawModelsJsonWithDeps(
|
||||
async function planOpenClawModelsJsonWithDeps(
|
||||
params: {
|
||||
cfg: OpenClawConfig;
|
||||
sourceConfigForSecrets?: OpenClawConfig;
|
||||
@@ -316,3 +316,10 @@ export async function planOpenClawModelsJson(
|
||||
): Promise<ModelsJsonPlan> {
|
||||
return planOpenClawModelsJsonWithDeps(params);
|
||||
}
|
||||
|
||||
if (process.env.VITEST || process.env.NODE_ENV === "test") {
|
||||
(globalThis as Record<PropertyKey, unknown>)[Symbol.for("openclaw.modelsConfigPlanTestApi")] = {
|
||||
planOpenClawModelsJsonWithDeps,
|
||||
resolveProvidersForModelsJsonWithDeps,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import type { PluginMetadataSnapshot } from "../plugins/plugin-metadata-snapshot.js";
|
||||
import "./models-config.providers.implicit.js";
|
||||
|
||||
type ResolveProviderDiscoveryFilterParams = {
|
||||
config?: OpenClawConfig;
|
||||
workspaceDir?: string;
|
||||
env: NodeJS.ProcessEnv;
|
||||
resolveOwners?: (provider: string) => readonly string[] | undefined;
|
||||
providerIds?: readonly string[];
|
||||
};
|
||||
|
||||
type ModelsConfigImplicitProvidersTestApi = {
|
||||
resolveProviderDiscoveryFilterForTest(
|
||||
params: ResolveProviderDiscoveryFilterParams,
|
||||
): string[] | undefined;
|
||||
resolvePluginMetadataProviderOwnersForTest(
|
||||
pluginMetadataSnapshot: Pick<PluginMetadataSnapshot, "owners"> | undefined,
|
||||
provider: string,
|
||||
): readonly string[] | undefined;
|
||||
};
|
||||
|
||||
function getTestApi(): ModelsConfigImplicitProvidersTestApi {
|
||||
return (globalThis as Record<PropertyKey, unknown>)[
|
||||
Symbol.for("openclaw.modelsConfigImplicitProvidersTestApi")
|
||||
] as ModelsConfigImplicitProvidersTestApi;
|
||||
}
|
||||
|
||||
export const resolveProviderDiscoveryFilterForTest = (
|
||||
params: ResolveProviderDiscoveryFilterParams,
|
||||
): string[] | undefined => getTestApi().resolveProviderDiscoveryFilterForTest(params);
|
||||
|
||||
export const resolvePluginMetadataProviderOwnersForTest = (
|
||||
pluginMetadataSnapshot: Pick<PluginMetadataSnapshot, "owners"> | undefined,
|
||||
provider: string,
|
||||
): readonly string[] | undefined =>
|
||||
getTestApi().resolvePluginMetadataProviderOwnersForTest(pluginMetadataSnapshot, provider);
|
||||
@@ -235,7 +235,7 @@ function appendNormalizedPluginMetadataOwners(
|
||||
}
|
||||
|
||||
/** Resolve the plugin discovery filter used by implicit provider discovery tests. */
|
||||
export function resolveProviderDiscoveryFilterForTest(params: {
|
||||
function resolveProviderDiscoveryFilterForTest(params: {
|
||||
config?: OpenClawConfig;
|
||||
workspaceDir?: string;
|
||||
env: NodeJS.ProcessEnv;
|
||||
@@ -246,13 +246,22 @@ export function resolveProviderDiscoveryFilterForTest(params: {
|
||||
}
|
||||
|
||||
/** Resolve provider owner plugin IDs from a preloaded metadata snapshot for tests. */
|
||||
export function resolvePluginMetadataProviderOwnersForTest(
|
||||
function resolvePluginMetadataProviderOwnersForTest(
|
||||
pluginMetadataSnapshot: Pick<PluginMetadataSnapshot, "owners"> | undefined,
|
||||
provider: string,
|
||||
): readonly string[] | undefined {
|
||||
return resolvePluginMetadataProviderOwners(pluginMetadataSnapshot, provider);
|
||||
}
|
||||
|
||||
if (process.env.VITEST || process.env.NODE_ENV === "test") {
|
||||
(globalThis as Record<PropertyKey, unknown>)[
|
||||
Symbol.for("openclaw.modelsConfigImplicitProvidersTestApi")
|
||||
] = {
|
||||
resolvePluginMetadataProviderOwnersForTest,
|
||||
resolveProviderDiscoveryFilterForTest,
|
||||
};
|
||||
}
|
||||
|
||||
function mergeImplicitProviderSet(
|
||||
target: Record<string, ProviderConfig>,
|
||||
additions: Record<string, ProviderConfig> | undefined,
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { PluginMetadataSnapshotOwnerMaps } from "../plugins/plugin-metadata
|
||||
import {
|
||||
resolvePluginMetadataProviderOwnersForTest,
|
||||
resolveProviderDiscoveryFilterForTest,
|
||||
} from "./models-config.providers.implicit.js";
|
||||
} from "./models-config.providers.implicit.test-support.js";
|
||||
|
||||
function liveFilterEnv(overrides: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
|
||||
// VITEST enables the live-filter parsing path without requiring real live creds.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../config/types.js";
|
||||
import { resolveProvidersForModelsJsonWithDeps } from "./models-config.plan.js";
|
||||
import { resolveProvidersForModelsJsonWithDeps } from "./models-config.plan.test-support.js";
|
||||
import type { ProviderConfig } from "./models-config.providers.secrets.js";
|
||||
|
||||
function createExplicitProvider(): ProviderConfig {
|
||||
|
||||
@@ -49,8 +49,8 @@ let clearConfigCache: typeof import("../config/io.js").clearConfigCache;
|
||||
let clearRuntimeConfigSnapshot: typeof import("../config/io.js").clearRuntimeConfigSnapshot;
|
||||
let setRuntimeConfigSnapshot: typeof import("../config/io.js").setRuntimeConfigSnapshot;
|
||||
let ensureOpenClawModelsJson: typeof import("./models-config.js").ensureOpenClawModelsJson;
|
||||
let resetModelsJsonReadyCacheForTest: typeof import("./models-config.js").resetModelsJsonReadyCacheForTest;
|
||||
let planOpenClawModelsJsonWithDeps: typeof import("./models-config.plan.js").planOpenClawModelsJsonWithDeps;
|
||||
let resetModelsJsonReadyCacheForTest: typeof import("./models-config-state.test-support.js").resetModelsJsonReadyCacheForTest;
|
||||
let planOpenClawModelsJsonWithDeps: typeof import("./models-config.plan.test-support.js").planOpenClawModelsJsonWithDeps;
|
||||
let readGeneratedModelsJson: typeof import("./models-config.test-utils.js").readGeneratedModelsJson;
|
||||
const fixtureSuite = createFixtureSuite("openclaw-models-runtime-source-");
|
||||
|
||||
@@ -58,9 +58,9 @@ beforeAll(async () => {
|
||||
await fixtureSuite.setup();
|
||||
({ clearConfigCache, clearRuntimeConfigSnapshot, setRuntimeConfigSnapshot } =
|
||||
await import("../config/io.js"));
|
||||
({ ensureOpenClawModelsJson, resetModelsJsonReadyCacheForTest } =
|
||||
await import("./models-config.js"));
|
||||
({ planOpenClawModelsJsonWithDeps } = await import("./models-config.plan.js"));
|
||||
({ ensureOpenClawModelsJson } = await import("./models-config.js"));
|
||||
({ resetModelsJsonReadyCacheForTest } = await import("./models-config-state.test-support.js"));
|
||||
({ planOpenClawModelsJsonWithDeps } = await import("./models-config.plan.test-support.js"));
|
||||
({ readGeneratedModelsJson } = await import("./models-config.test-utils.js"));
|
||||
});
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ let clearConfigCache: typeof import("../config/config.js").clearConfigCache;
|
||||
let clearRuntimeConfigSnapshot: typeof import("../config/config.js").clearRuntimeConfigSnapshot;
|
||||
let clearRuntimeAuthProfileStoreSnapshots: typeof import("./auth-profiles/store.js").clearRuntimeAuthProfileStoreSnapshots;
|
||||
let ensureOpenClawModelsJson: typeof import("./models-config.js").ensureOpenClawModelsJson;
|
||||
let resetModelsJsonReadyCacheForTest: typeof import("./models-config.js").resetModelsJsonReadyCacheForTest;
|
||||
let resetModelsJsonReadyCacheForTest: typeof import("./models-config-state.test-support.js").resetModelsJsonReadyCacheForTest;
|
||||
|
||||
type ParsedProviderConfig = {
|
||||
baseUrl?: string;
|
||||
@@ -163,8 +163,8 @@ describe("models-config", () => {
|
||||
vi.resetModules();
|
||||
({ clearConfigCache, clearRuntimeConfigSnapshot } = await import("../config/config.js"));
|
||||
({ clearRuntimeAuthProfileStoreSnapshots } = await import("./auth-profiles/store.js"));
|
||||
({ ensureOpenClawModelsJson, resetModelsJsonReadyCacheForTest } =
|
||||
await import("./models-config.js"));
|
||||
({ ensureOpenClawModelsJson } = await import("./models-config.js"));
|
||||
({ resetModelsJsonReadyCacheForTest } = await import("./models-config-state.test-support.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import "./models-config.js";
|
||||
|
||||
type ModelsConfigTestApi = {
|
||||
ensureModelsFileModeForModelsJson(pathname: string): Promise<void>;
|
||||
writeModelsFileAtomicForModelsJson(targetPath: string, contents: string): Promise<void>;
|
||||
};
|
||||
|
||||
function getTestApi(): ModelsConfigTestApi {
|
||||
return (globalThis as Record<PropertyKey, unknown>)[
|
||||
Symbol.for("openclaw.modelsConfigTestApi")
|
||||
] as ModelsConfigTestApi;
|
||||
}
|
||||
|
||||
export const ensureModelsFileModeForModelsJson = async (pathname: string): Promise<void> =>
|
||||
await getTestApi().ensureModelsFileModeForModelsJson(pathname);
|
||||
|
||||
export const writeModelsFileAtomicForModelsJson = async (
|
||||
targetPath: string,
|
||||
contents: string,
|
||||
): Promise<void> => await getTestApi().writeModelsFileAtomicForModelsJson(targetPath, contents);
|
||||
@@ -39,8 +39,6 @@ import {
|
||||
} from "./plugin-model-catalog.js";
|
||||
import { stableStringify } from "./stable-stringify.js";
|
||||
|
||||
export { resetModelsJsonReadyCacheForTest } from "./models-config-state.js";
|
||||
|
||||
type PreparedOpenClawModelsJsonSource = ModelsJsonReadyResult & {
|
||||
fingerprint: string;
|
||||
workspaceDir?: string;
|
||||
@@ -141,20 +139,27 @@ async function readExistingModelsFile(pathname: string): Promise<{
|
||||
}
|
||||
|
||||
/** Best-effort chmod for generated models.json and plugin catalog files. */
|
||||
export async function ensureModelsFileModeForModelsJson(pathname: string): Promise<void> {
|
||||
async function ensureModelsFileModeForModelsJson(pathname: string): Promise<void> {
|
||||
await fs.chmod(pathname, 0o600).catch(() => {
|
||||
// best-effort
|
||||
});
|
||||
}
|
||||
|
||||
/** Atomic private-file-store write used by models.json generation. */
|
||||
export async function writeModelsFileAtomicForModelsJson(
|
||||
async function writeModelsFileAtomicForModelsJson(
|
||||
targetPath: string,
|
||||
contents: string,
|
||||
): Promise<void> {
|
||||
await privateFileStore(path.dirname(targetPath)).writeText(path.basename(targetPath), contents);
|
||||
}
|
||||
|
||||
if (process.env.VITEST || process.env.NODE_ENV === "test") {
|
||||
(globalThis as Record<PropertyKey, unknown>)[Symbol.for("openclaw.modelsConfigTestApi")] = {
|
||||
ensureModelsFileModeForModelsJson,
|
||||
writeModelsFileAtomicForModelsJson,
|
||||
};
|
||||
}
|
||||
|
||||
async function isGeneratedPluginCatalogFile(targetPath: string): Promise<boolean> {
|
||||
return (await readGeneratedPluginCatalog(targetPath)) !== undefined;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Verifies GitHub Copilot profile token fallback and implicit provider planning.
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { planOpenClawModelsJson, planOpenClawModelsJsonWithDeps } from "./models-config.plan.js";
|
||||
import { planOpenClawModelsJson } from "./models-config.plan.js";
|
||||
import { planOpenClawModelsJsonWithDeps } from "./models-config.plan.test-support.js";
|
||||
import type { ProviderConfig } from "./models-config.providers.secrets.js";
|
||||
import { createProviderAuthResolver } from "./models-config.providers.secrets.js";
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ type WorkerCleanupHelpers = {
|
||||
drainSessionWriteLockStateForTest: typeof import("../src/agents/session-write-lock.js").drainSessionWriteLockStateForTest;
|
||||
resetContextWindowCacheForTest: typeof import("../src/agents/context-runtime-state.js").resetContextWindowCacheForTest;
|
||||
resetFileLockStateForTest: typeof import("../src/infra/file-lock.js").resetFileLockStateForTest;
|
||||
resetModelsJsonReadyCacheForTest: typeof import("../src/agents/models-config-state.js").resetModelsJsonReadyCacheForTest;
|
||||
resetModelsJsonReadyCacheForTest: typeof import("../src/agents/models-config-state.test-support.js").resetModelsJsonReadyCacheForTest;
|
||||
resetSessionWriteLockStateForTest: typeof import("../src/agents/session-write-lock.js").resetSessionWriteLockStateForTest;
|
||||
};
|
||||
|
||||
@@ -73,8 +73,8 @@ function loadWorkerCleanupHelpers(): Promise<WorkerCleanupHelpers> {
|
||||
vi.importActual<typeof import("../src/agents/context-runtime-state.js")>(
|
||||
"../src/agents/context-runtime-state.js",
|
||||
),
|
||||
vi.importActual<typeof import("../src/agents/models-config-state.js")>(
|
||||
"../src/agents/models-config-state.js",
|
||||
vi.importActual<typeof import("../src/agents/models-config-state.test-support.js")>(
|
||||
"../src/agents/models-config-state.test-support.js",
|
||||
),
|
||||
vi.importActual<typeof import("../src/agents/session-write-lock.js")>(
|
||||
"../src/agents/session-write-lock.js",
|
||||
|
||||
Reference in New Issue
Block a user