refactor(deadcode): tighten provider extension roots (#108497)

This commit is contained in:
Peter Steinberger
2026-07-15 16:23:47 -07:00
committed by GitHub
parent 9e64fbb257
commit 370b80b74d
30 changed files with 177 additions and 179 deletions
+32
View File
@@ -52,12 +52,23 @@ const bundledPluginEntries = [
"*.ts!",
"index.ts!",
"setup-entry.ts!",
// Core resolves these public plugin artifacts by basename rather than by a
// static import from the plugin entry module.
"*-api.ts!",
"cli-metadata.ts!",
"channel-entry.ts!",
// Provider catalogs and web tools resolve these manifest/convention-owned
// modules from the plugin root at runtime.
"provider-discovery.ts!",
"{web-search,web-fetch}-provider.ts!",
"{api,contract-api,helper-api,runtime-api,light-runtime-api,update-offset-runtime-api,channel-plugin-api,provider-plugin-api,setup-api}.ts!",
"subagent-hooks-api.ts!",
"src/{api,runtime-api,light-runtime-api,update-offset-runtime-api,channel-plugin-api,provider-plugin-api,doctor-contract,setup-surface,mcp-serve}.ts!",
"src/subagent-hooks-api.ts!",
] as const;
const strictBundledPluginEntries = bundledPluginEntries.filter((entry) => entry !== "*.ts!");
const bundledPluginIgnoredRuntimeDependencies = [
"@agentclientprotocol/claude-agent-acp",
"@a2ui/lit",
@@ -102,6 +113,14 @@ const rootBundledPluginRuntimeDependencies = [
"tokenjuice",
] as const;
function strictBundledPluginWorkspace() {
return {
entry: strictBundledPluginEntries,
project: ["*.ts!", "src/**/*.{js,mjs,ts}!"],
ignoreDependencies: bundledPluginIgnoredRuntimeDependencies,
} as const;
}
// These files are test infrastructure, so their exports are intentionally
// available to tests without becoming part of the production dead-code scan.
const ignoredTestSupportFiles = [
@@ -353,6 +372,19 @@ const config = {
entry: ["index.js!", "scripts/postinstall.js!"],
project: ["index.js!", "scripts/**/*.js!"],
},
[`${BUNDLED_PLUGIN_ROOT_DIR}/amazon-bedrock-mantle`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/cohere`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/featherless`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/fireworks`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/huggingface`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/kilocode`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/nvidia`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/qianfan`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/qwen`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/senseaudio`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/tencent`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/vllm`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/xiaomi`]: strictBundledPluginWorkspace(),
[`${BUNDLED_PLUGIN_ROOT_DIR}/llama-cpp`]: {
entry: bundledPluginEntries,
project: ["index.ts!", "src/**/*.{js,mjs,ts}!"],
@@ -1,10 +1,7 @@
// Amazon Bedrock Mantle tests cover mantle anthropic plugin behavior.
import type { Model } from "openclaw/plugin-sdk/llm";
import { describe, expect, it, vi } from "vitest";
import {
createMantleAnthropicStreamFn,
resolveMantleAnthropicBaseUrl,
} from "./mantle-anthropic.runtime.js";
import { createMantleAnthropicStreamFn } from "./mantle-anthropic.runtime.js";
function createTestModel(overrides: Partial<Model> = {}): Model {
return {
@@ -267,13 +264,4 @@ describe("createMantleAnthropicStreamFn", () => {
expect(streamOptions).not.toHaveProperty("thinkingBudgetTokens");
expect(streamOptions.temperature).toBeUndefined();
});
it("normalizes Mantle provider URLs to the Anthropic endpoint", () => {
expect(resolveMantleAnthropicBaseUrl("https://bedrock-mantle.us-east-1.api.aws/v1")).toBe(
"https://bedrock-mantle.us-east-1.api.aws/anthropic",
);
expect(
resolveMantleAnthropicBaseUrl("https://bedrock-mantle.us-east-1.api.aws/anthropic/"),
).toBe("https://bedrock-mantle.us-east-1.api.aws/anthropic");
});
});
@@ -22,7 +22,7 @@ type AnthropicOptions = ConstructorParameters<typeof Anthropic>[0];
type MantleAnthropicStream = typeof stream;
/** Resolve the Anthropic-compatible Mantle base URL from a provider base URL. */
export function resolveMantleAnthropicBaseUrl(baseUrl: string): string {
function resolveMantleAnthropicBaseUrl(baseUrl: string): string {
const trimmed = baseUrl.replace(/\/+$/, "");
if (trimmed.endsWith("/anthropic")) {
return trimmed;
+5 -6
View File
@@ -5,15 +5,14 @@ import { registerSingleProviderPlugin } from "openclaw/plugin-sdk/plugin-test-ru
import { buildOpenAICompletionsParams } from "openclaw/plugin-sdk/provider-transport-runtime";
import { describe, expect, it } from "vitest";
import plugin from "./index.js";
import {
COHERE_COMMAND_A_PLUS_MODEL_ID,
COHERE_COMMAND_A_REASONING_MODEL_ID,
COHERE_COMMAND_A_VISION_MODEL_ID,
COHERE_NORTH_MINI_CODE_MODEL_ID,
} from "./models.js";
import { COHERE_COMMAND_A_PLUS_MODEL_ID } from "./models.js";
import { buildCohereProvider } from "./provider-catalog.js";
import { createCohereCompletionsWrapper } from "./stream.js";
const COHERE_COMMAND_A_REASONING_MODEL_ID = "command-a-reasoning-08-2025";
const COHERE_COMMAND_A_VISION_MODEL_ID = "command-a-vision-07-2025";
const COHERE_NORTH_MINI_CODE_MODEL_ID = "north-mini-code-1-0";
function readManifest() {
return JSON.parse(readFileSync(new URL("./openclaw.plugin.json", import.meta.url), "utf8")) as {
providerAuthChoices?: Array<{ choiceId?: string; optionKey?: string; cliFlag?: string }>;
+2 -3
View File
@@ -11,9 +11,8 @@ const COHERE_MANIFEST_CATALOG = manifest.modelCatalog.providers.cohere;
export const COHERE_BASE_URL = COHERE_MANIFEST_CATALOG.baseUrl;
export const COHERE_MODEL_CATALOG = COHERE_MANIFEST_CATALOG.models;
export const COHERE_COMMAND_A_PLUS_MODEL_ID = "command-a-plus-05-2026";
export const COHERE_COMMAND_A_REASONING_MODEL_ID = "command-a-reasoning-08-2025";
export const COHERE_COMMAND_A_VISION_MODEL_ID = "command-a-vision-07-2025";
export const COHERE_NORTH_MINI_CODE_MODEL_ID = "north-mini-code-1-0";
const COHERE_COMMAND_A_REASONING_MODEL_ID = "command-a-reasoning-08-2025";
const COHERE_NORTH_MINI_CODE_MODEL_ID = "north-mini-code-1-0";
const COHERE_MODERN_MODEL_IDS = new Set([
COHERE_COMMAND_A_PLUS_MODEL_ID,
+7 -9
View File
@@ -1,15 +1,13 @@
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
import { resolveAgentModelPrimaryValue } from "openclaw/plugin-sdk/provider-onboard";
import { describe, expect, it } from "vitest";
import {
buildCohereCatalogModels,
COHERE_BASE_URL,
COHERE_COMMAND_A_REASONING_MODEL_ID,
COHERE_COMMAND_A_VISION_MODEL_ID,
COHERE_MODEL_CATALOG,
COHERE_NORTH_MINI_CODE_MODEL_ID,
} from "./models.js";
import { applyCohereConfig, COHERE_DEFAULT_MODEL_ID, COHERE_DEFAULT_MODEL_REF } from "./onboard.js";
import { buildCohereCatalogModels, COHERE_BASE_URL, COHERE_MODEL_CATALOG } from "./models.js";
import { applyCohereConfig, COHERE_DEFAULT_MODEL_REF } from "./onboard.js";
const COHERE_DEFAULT_MODEL_ID = "command-a-plus-05-2026";
const COHERE_COMMAND_A_REASONING_MODEL_ID = "command-a-reasoning-08-2025";
const COHERE_COMMAND_A_VISION_MODEL_ID = "command-a-vision-07-2025";
const COHERE_NORTH_MINI_CODE_MODEL_ID = "north-mini-code-1-0";
describe("Cohere onboarding", () => {
it("registers the manifest catalog through the onboarding preset", () => {
+1 -1
View File
@@ -9,7 +9,7 @@ import {
COHERE_MODEL_CATALOG,
} from "./models.js";
export const COHERE_DEFAULT_MODEL_ID = COHERE_COMMAND_A_PLUS_MODEL_ID;
const COHERE_DEFAULT_MODEL_ID = COHERE_COMMAND_A_PLUS_MODEL_ID;
export const COHERE_DEFAULT_MODEL_REF = `cohere/${COHERE_DEFAULT_MODEL_ID}`;
const coherePresetAppliers = createModelCatalogPresetAppliers({
@@ -5,8 +5,6 @@ import manifest from "./openclaw.plugin.json" with { type: "json" };
export {
FEATHERLESS_BASE_URL,
FEATHERLESS_DEFAULT_CONTEXT_WINDOW,
FEATHERLESS_DEFAULT_MAX_TOKENS,
FEATHERLESS_DEFAULT_MODEL_ID,
FEATHERLESS_DYNAMIC_COMPAT,
FEATHERLESS_DYNAMIC_CONTEXT_WINDOW,
+34 -27
View File
@@ -2,10 +2,7 @@
import type { StreamFn } from "openclaw/plugin-sdk/agent-core";
import type { Context, Model } from "openclaw/plugin-sdk/llm";
import { describe, expect, it } from "vitest";
import {
createFireworksKimiThinkingDisabledWrapper,
wrapFireworksProviderStream,
} from "./stream.js";
import { wrapFireworksProviderStream } from "./stream.js";
function capturePayload(params: {
provider: string;
@@ -21,16 +18,21 @@ function capturePayload(params: {
return {} as ReturnType<StreamFn>;
};
const wrapped = createFireworksKimiThinkingDisabledWrapper(baseStreamFn);
void wrapped(
{
api: params.api,
provider: params.provider,
id: params.modelId,
} as Model<"openai-completions">,
{ messages: [] } as Context,
{},
);
const model = {
api: params.api,
provider: params.provider,
id: params.modelId,
} as Model<"openai-completions">;
const wrapped = wrapFireworksProviderStream({
provider: params.provider,
modelId: params.modelId,
model,
streamFn: baseStreamFn,
} as never);
if (!wrapped) {
throw new Error("expected Fireworks stream wrapper");
}
void wrapped(model, { messages: [] } as Context, {});
return captured;
}
@@ -111,20 +113,25 @@ describe("createFireworksKimiThinkingDisabledWrapper", () => {
return {} as ReturnType<StreamFn>;
};
const wrapped = createFireworksKimiThinkingDisabledWrapper(baseStreamFn);
void wrapped(
{
api: "openai-completions",
provider: "fireworks",
id: "accounts/fireworks/routers/kimi-k2p5-turbo",
} as Model<"openai-completions">,
{ messages: [] } as Context,
{
onPayload: (payload) => {
callbackPayload = payload as Record<string, unknown>;
},
const model = {
api: "openai-completions",
provider: "fireworks",
id: "accounts/fireworks/routers/kimi-k2p5-turbo",
} as Model<"openai-completions">;
const wrapped = wrapFireworksProviderStream({
provider: "fireworks",
modelId: model.id,
model,
streamFn: baseStreamFn,
} as never);
if (!wrapped) {
throw new Error("expected Fireworks stream wrapper");
}
void wrapped(model, { messages: [] } as Context, {
onPayload: (payload) => {
callbackPayload = payload as Record<string, unknown>;
},
);
});
expect(callbackPayload).toEqual({ thinking: { type: "disabled" } });
});
+1 -3
View File
@@ -11,9 +11,7 @@ function isFireworksProviderId(providerId: string): boolean {
return normalized === "fireworks" || normalized === "fireworks-ai";
}
export function createFireworksKimiThinkingDisabledWrapper(
baseStreamFn: StreamFn | undefined,
): StreamFn {
function createFireworksKimiThinkingDisabledWrapper(baseStreamFn: StreamFn | undefined): StreamFn {
const underlying = baseStreamFn ?? streamSimple;
return (model, context, options) =>
streamWithPayloadPatch(underlying, model, context, options, (payloadObj) => {
+1 -2
View File
@@ -8,7 +8,6 @@ import {
HUGGINGFACE_MODEL_CATALOG,
isHuggingfacePolicyLocked,
} from "./api.js";
import { HUGGINGFACE_DISCOVERY_TIMEOUT_MS } from "./models.js";
const ORIGINAL_VITEST = process.env.VITEST;
const ORIGINAL_NODE_ENV = process.env.NODE_ENV;
@@ -81,7 +80,7 @@ describe("huggingface models", () => {
await discoverHuggingfaceModels("hf_test_token");
expect(timeoutSpy).toHaveBeenCalledWith(HUGGINGFACE_DISCOVERY_TIMEOUT_MS);
expect(timeoutSpy).toHaveBeenCalledWith(30_000);
});
it("accepts a custom discovery timeout override", async () => {
+1 -1
View File
@@ -11,7 +11,7 @@ import { isHuggingfaceModelDiscoveryTestEnvironment } from "./model-discovery-en
export const HUGGINGFACE_BASE_URL = "https://router.huggingface.co/v1";
export const HUGGINGFACE_POLICY_SUFFIXES = ["cheapest", "fastest"] as const;
export const HUGGINGFACE_DISCOVERY_TIMEOUT_MS = 30_000;
const HUGGINGFACE_DISCOVERY_TIMEOUT_MS = 30_000;
const HUGGINGFACE_DEFAULT_COST = {
input: 0,
+2 -1
View File
@@ -10,7 +10,8 @@ import {
KILOCODE_DEFAULT_COST,
KILOCODE_DEFAULT_MODEL_ID,
} from "./api.js";
import { applyKilocodeConfig, KILOCODE_BASE_URL, KILOCODE_DEFAULT_MODEL_REF } from "./onboard.js";
import { applyKilocodeConfig, KILOCODE_DEFAULT_MODEL_REF } from "./onboard.js";
import { KILOCODE_BASE_URL } from "./provider-models.js";
const emptyCfg: OpenClawConfig = {};
const KILOCODE_MODEL_IDS = ["kilo/auto"];
+1 -1
View File
@@ -6,7 +6,7 @@ import {
import { buildKilocodeProvider } from "./provider-catalog.js";
import { KILOCODE_BASE_URL, KILOCODE_DEFAULT_MODEL_REF } from "./provider-models.js";
export { KILOCODE_BASE_URL, KILOCODE_DEFAULT_MODEL_REF };
export { KILOCODE_DEFAULT_MODEL_REF };
const kilocodePresetAppliers = createModelCatalogPresetAppliers({
primaryModelRef: KILOCODE_DEFAULT_MODEL_REF,
+5 -5
View File
@@ -5,12 +5,12 @@ import {
registerSingleProviderPlugin,
resolveProviderPluginChoice,
} from "openclaw/plugin-sdk/plugin-test-runtime";
import { clearLiveCatalogCacheForTests } from "openclaw/plugin-sdk/provider-catalog-live-runtime";
import { afterEach, describe, expect, it, vi } from "vitest";
import plugin from "./index.js";
import {
clearNvidiaFeaturedModelCacheForTests,
NVIDIA_FEATURED_MODELS_URL,
} from "./provider-catalog.js";
const NVIDIA_FEATURED_MODELS_URL =
"https://assets.ngc.nvidia.com/products/api-catalog/featured-models.json";
const ssrfRuntimeMocks = vi.hoisted(() => ({
fetchWithSsrFGuard: vi.fn(),
@@ -39,7 +39,7 @@ async function registerNvidiaProvider() {
}
afterEach(() => {
clearNvidiaFeaturedModelCacheForTests();
clearLiveCatalogCacheForTests();
ssrfRuntimeMocks.fetchWithSsrFGuard.mockReset();
ssrfRuntimeMocks.ssrfPolicyFromHttpBaseUrlAllowedHostname.mockClear();
});
+5 -3
View File
@@ -1,3 +1,4 @@
import { clearLiveCatalogCacheForTests } from "openclaw/plugin-sdk/provider-catalog-live-runtime";
// Nvidia tests cover provider catalog plugin behavior.
import { afterEach, describe, expect, it, vi } from "vitest";
import manifest from "./openclaw.plugin.json" with { type: "json" };
@@ -6,10 +7,11 @@ import {
buildNvidiaProvider,
buildSelectableNvidiaProvider,
buildSelectableLiveNvidiaProvider,
clearNvidiaFeaturedModelCacheForTests,
NVIDIA_FEATURED_MODELS_URL,
} from "./provider-catalog.js";
const NVIDIA_FEATURED_MODELS_URL =
"https://assets.ngc.nvidia.com/products/api-catalog/featured-models.json";
const EXPECTED_FEATURED_MODELS = [
{
id: "nvidia/nemotron-3-ultra-550b-a55b",
@@ -94,7 +96,7 @@ vi.mock("openclaw/plugin-sdk/ssrf-runtime", () => ssrfRuntimeMocks);
afterEach(() => {
vi.useRealTimers();
clearNvidiaFeaturedModelCacheForTests();
clearLiveCatalogCacheForTests();
ssrfRuntimeMocks.fetchWithSsrFGuard.mockReset();
ssrfRuntimeMocks.ssrfPolicyFromHttpBaseUrlAllowedHostname.mockClear();
});
+2 -9
View File
@@ -1,9 +1,6 @@
// Nvidia provider module implements model/runtime integration.
import { lookup as dnsLookup } from "node:dns/promises";
import {
clearLiveCatalogCacheForTests,
getCachedLiveProviderModelRows,
} from "openclaw/plugin-sdk/provider-catalog-live-runtime";
import { getCachedLiveProviderModelRows } from "openclaw/plugin-sdk/provider-catalog-live-runtime";
import { buildManifestModelProviderConfig } from "openclaw/plugin-sdk/provider-catalog-shared";
import type {
ModelDefinitionConfig,
@@ -17,7 +14,7 @@ import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
import manifest from "./openclaw.plugin.json" with { type: "json" };
export const NVIDIA_DEFAULT_MODEL_ID = "nvidia/nemotron-3-ultra-550b-a55b";
export const NVIDIA_FEATURED_MODELS_URL =
const NVIDIA_FEATURED_MODELS_URL =
"https://assets.ngc.nvidia.com/products/api-catalog/featured-models.json";
const FEATURED_MODEL_CACHE_TTL_MS = 24 * 60 * 60 * 1000;
@@ -119,10 +116,6 @@ export async function buildSelectableLiveNvidiaProvider(): Promise<ModelProvider
};
}
export function clearNvidiaFeaturedModelCacheForTests() {
clearLiveCatalogCacheForTests();
}
async function loadNvidiaFeaturedModels(): Promise<ModelDefinitionConfig[] | null> {
try {
const rows = await getCachedLiveProviderModelRows({
+1 -35
View File
@@ -7,11 +7,7 @@ import { resolveAgentModelPrimaryValue } from "openclaw/plugin-sdk/provider-onbo
import { describe, expect, it } from "vitest";
import { runSingleProviderCatalog } from "../test-support/provider-model-test-helpers.js";
import qianfanPlugin from "./index.js";
import {
applyQianfanConfig,
applyQianfanProviderConfig,
QIANFAN_DEFAULT_MODEL_REF,
} from "./onboard.js";
import { applyQianfanConfig, QIANFAN_DEFAULT_MODEL_REF } from "./onboard.js";
function expectRecord<T>(value: T | null | undefined, label: string): NonNullable<T> {
if (!value) {
@@ -94,36 +90,6 @@ describe("qianfan provider plugin", () => {
});
});
it("adds Qianfan provider defaults without changing primary model in provider-only mode", () => {
const cfg = applyQianfanProviderConfig({
agents: {
defaults: {
model: { primary: "anthropic/claude-opus-4-6" },
},
},
});
const modelsConfig = expectRecord(cfg.models, "models config");
const providers = expectRecord(modelsConfig.providers, "model providers");
const providerConfig = expectRecord(providers.qianfan, "Qianfan provider config");
expect(providerConfig.api).toBe("openai-completions");
expect(providerConfig.baseUrl).toBe("https://qianfan.baidubce.com/v2");
const providerModels = expectRecord(providerConfig.models, "Qianfan provider models");
expect(providerModels.map((model) => model.id)).toEqual([
"deepseek-v3.2",
"ernie-5.0-thinking-preview",
]);
const agentsConfig = expectRecord(cfg.agents, "agents config");
const agentDefaults = expectRecord(agentsConfig.defaults, "agent defaults");
const agentModelAliases = expectRecord(agentDefaults.models, "agent model aliases");
const qianfanAlias = expectRecord(
agentModelAliases[QIANFAN_DEFAULT_MODEL_REF],
"Qianfan model alias",
);
expect(qianfanAlias.alias).toBe("QIANFAN");
expect(resolveAgentModelPrimaryValue(agentDefaults.model)).toBe("anthropic/claude-opus-4-6");
});
it("sets Qianfan as the agent primary model in full onboarding mode", () => {
const cfg = applyQianfanConfig({});
-4
View File
@@ -53,10 +53,6 @@ const qianfanPresetAppliers = createDefaultModelsPresetAppliers({
},
});
export function applyQianfanProviderConfig(cfg: OpenClawConfig): OpenClawConfig {
return qianfanPresetAppliers.applyProviderConfig(cfg);
}
export function applyQianfanConfig(cfg: OpenClawConfig): OpenClawConfig {
return qianfanPresetAppliers.applyConfig(cfg);
}
@@ -4,10 +4,16 @@ import {
installPinnedHostnameTestHooks,
} from "openclaw/plugin-sdk/test-env";
import { describe, expect, it } from "vitest";
import { describeQwenVideo } from "./media-understanding-provider.js";
import { buildQwenMediaUnderstandingProvider } from "./media-understanding-provider.js";
installPinnedHostnameTestHooks();
const qwenProvider = buildQwenMediaUnderstandingProvider();
const describeQwenVideo = qwenProvider.describeVideo;
if (!describeQwenVideo) {
throw new Error("expected Qwen video description capability");
}
function oversizedJsonResponse(params: { chunkCount: number; chunkSize: number }): {
response: Response;
getReadCount: () => number;
@@ -21,9 +21,7 @@ import { QWEN_STANDARD_GLOBAL_BASE_URL } from "./models.js";
const DEFAULT_QWEN_VIDEO_MODEL = "qwen-vl-max-latest";
const DEFAULT_QWEN_VIDEO_PROMPT = "Describe the video in detail.";
export async function describeQwenVideo(
params: VideoDescriptionRequest,
): Promise<VideoDescriptionResult> {
async function describeQwenVideo(params: VideoDescriptionRequest): Promise<VideoDescriptionResult> {
const fetchFn = params.fetchFn ?? fetch;
const model = resolveMediaUnderstandingString(params.model, DEFAULT_QWEN_VIDEO_MODEL);
const mime = resolveMediaUnderstandingString(params.mime, "video/mp4");
@@ -10,10 +10,15 @@ import {
installPinnedHostnameTestHooks,
} from "openclaw/plugin-sdk/test-env";
import { describe, expect, it } from "vitest";
import { transcribeSenseAudioAudio } from "./media-understanding-provider.js";
import { senseaudioMediaUnderstandingProvider } from "./media-understanding-provider.js";
installPinnedHostnameTestHooks();
const transcribeSenseAudioAudio = senseaudioMediaUnderstandingProvider.transcribeAudio;
if (!transcribeSenseAudioAudio) {
throw new Error("expected SenseAudio transcription capability");
}
describe("transcribeSenseAudioAudio", () => {
it("uses SenseAudio base URL by default", async () => {
const { fetchFn, getRequest } = createRequestCaptureJsonFetch({ text: "ok" });
@@ -8,7 +8,7 @@ import {
const DEFAULT_SENSEAUDIO_AUDIO_BASE_URL = "https://api.senseaudio.cn/v1";
const DEFAULT_SENSEAUDIO_AUDIO_MODEL = "senseaudio-asr-pro-1.5-260319";
export async function transcribeSenseAudioAudio(params: AudioTranscriptionRequest) {
async function transcribeSenseAudioAudio(params: AudioTranscriptionRequest) {
return await transcribeOpenAiCompatibleAudio({
...params,
provider: "senseaudio",
+4 -5
View File
@@ -1,11 +1,10 @@
// Tencent tests cover config compatibility repair behavior.
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
import { describe, expect, it } from "vitest";
import {
migrateTencentTokenHubModelDefaults,
TENCENT_TOKENHUB_DEFAULT_MODEL_REF,
TENCENT_TOKENHUB_PREVIEW_MODEL_REF,
} from "./config-compat.js";
import { migrateTencentTokenHubModelDefaults } from "./config-compat.js";
const TENCENT_TOKENHUB_DEFAULT_MODEL_REF = "tencent-tokenhub/hy3";
const TENCENT_TOKENHUB_PREVIEW_MODEL_REF = "tencent-tokenhub/hy3-preview";
describe("Tencent config compatibility", () => {
it("adds the stable TokenHub model and makes it primary for old preview defaults", () => {
+2 -9
View File
@@ -1,8 +1,8 @@
// Tencent config compatibility repairs shipped TokenHub model allowlists.
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
export const TENCENT_TOKENHUB_DEFAULT_MODEL_REF = "tencent-tokenhub/hy3";
export const TENCENT_TOKENHUB_PREVIEW_MODEL_REF = "tencent-tokenhub/hy3-preview";
const TENCENT_TOKENHUB_DEFAULT_MODEL_REF = "tencent-tokenhub/hy3";
const TENCENT_TOKENHUB_PREVIEW_MODEL_REF = "tencent-tokenhub/hy3-preview";
const TOKENHUB_DEFAULT_ALIAS = "Hy3 (TokenHub)";
const TOKENHUB_PREVIEW_ALIAS = "Hy3 preview (TokenHub)";
@@ -111,10 +111,3 @@ export function migrateTencentTokenHubModelDefaults(cfg: OpenClawConfig): {
return { config: nextConfig, changes };
}
export function normalizeCompatibilityConfig({ cfg }: { cfg: OpenClawConfig }): {
config: OpenClawConfig;
changes: string[];
} {
return migrateTencentTokenHubModelDefaults(cfg);
}
+15 -19
View File
@@ -2,11 +2,7 @@
import type { StreamFn } from "openclaw/plugin-sdk/agent-core";
import type { Context, Model } from "openclaw/plugin-sdk/llm";
import { describe, expect, it } from "vitest";
import {
createVllmProviderThinkingWrapper,
createVllmQwenThinkingWrapper,
wrapVllmProviderStream,
} from "./stream.js";
import { createVllmQwenThinkingWrapper, wrapVllmProviderStream } from "./stream.js";
function capturePayload(params: {
format: "chat-template" | "top-level";
@@ -135,21 +131,21 @@ describe("createVllmProviderThinkingWrapper", () => {
return {} as ReturnType<StreamFn>;
};
const wrapped = createVllmProviderThinkingWrapper({
baseStreamFn,
const model = {
api: "openai-completions",
provider: "vllm",
id: "nemotron-3-super",
reasoning: true,
...params.model,
} as Model<"openai-completions">;
const wrapped = wrapVllmProviderStream({
provider: "vllm",
modelId: model.id,
model,
thinkingLevel: params.thinkingLevel ?? "high",
});
void wrapped(
{
api: "openai-completions",
provider: "vllm",
id: "nemotron-3-super",
reasoning: true,
...params.model,
} as Model<"openai-completions">,
{ messages: [] } as Context,
{},
);
streamFn: baseStreamFn,
} as never);
void wrapped?.(model, { messages: [] } as Context, {});
return captured;
}
+1 -1
View File
@@ -76,7 +76,7 @@ export function createVllmQwenThinkingWrapper(params: {
);
}
export function createVllmProviderThinkingWrapper(params: {
function createVllmProviderThinkingWrapper(params: {
baseStreamFn: StreamFn | undefined;
qwenFormat?: VllmQwenThinkingFormat;
thinkingLevel: VllmThinkingLevel;
+1 -2
View File
@@ -8,7 +8,6 @@ import {
applyXiaomiConfig,
applyXiaomiProviderConfig,
applyXiaomiTokenPlanConfig,
applyXiaomiTokenPlanProviderConfig,
} from "./onboard.js";
import { buildXiaomiProvider, buildXiaomiTokenPlanProvider } from "./provider-catalog.js";
@@ -68,7 +67,7 @@ describe("xiaomi onboard", () => {
it("merges Xiaomi Token Plan models and rewrites the selected regional base URL", () => {
const provider = expectProviderOnboardMergedLegacyConfig({
applyProviderConfig: (config) => applyXiaomiTokenPlanProviderConfig(config, "sgp"),
applyProviderConfig: (config) => applyXiaomiTokenPlanConfig(config, "sgp"),
providerId: "xiaomi-token-plan",
providerApi: "openai-completions",
baseUrl: "https://token-plan-sgp.xiaomimimo.com/v1",
-11
View File
@@ -86,17 +86,6 @@ export function applyXiaomiConfig(cfg: OpenClawConfig): OpenClawConfig {
return xiaomiPresetAppliers.applyConfig(cfg);
}
export function applyXiaomiTokenPlanProviderConfig(
cfg: OpenClawConfig,
region: XiaomiTokenPlanRegion,
): OpenClawConfig {
return withProviderBaseUrl(
xiaomiTokenPlanPresetAppliers.applyProviderConfig(cfg),
XIAOMI_TOKEN_PLAN_PROVIDER_ID,
resolveXiaomiTokenPlanBaseUrl(region),
);
}
export function applyXiaomiTokenPlanConfig(
cfg: OpenClawConfig,
region: XiaomiTokenPlanRegion,
@@ -40,6 +40,43 @@ describe("check-deadcode-exports", () => {
expect(knipConfig.workspaces["."].entry).toContain("src/mcp/openclaw-tools-serve.ts!");
});
it.each([
"amazon-bedrock-mantle",
"cohere",
"featherless",
"fireworks",
"huggingface",
"kilocode",
"nvidia",
"qianfan",
"qwen",
"senseaudio",
"tencent",
"vllm",
"xiaomi",
])("removes the bundled-plugin root catch-all from migrated %s workspace", (pluginId) => {
const workspace = (
knipConfig.workspaces as Record<string, { readonly entry: readonly string[] }>
)[`extensions/${pluginId}`];
if (!workspace) {
throw new Error(`missing Knip workspace for ${pluginId}`);
}
const entries = workspace.entry;
expect(entries).not.toContain("*.ts!");
expect(entries).toEqual(
expect.arrayContaining([
"index.ts!",
"setup-entry.ts!",
"*-api.ts!",
"cli-metadata.ts!",
"channel-entry.ts!",
"provider-discovery.ts!",
"{web-search,web-fetch}-provider.ts!",
]),
);
expect(knipConfig.workspaces["extensions/*"].entry).toContain("*.ts!");
});
it.each([
"packages/agent-core",
"packages/markdown-core",