mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-16 11:41:08 +00:00
refactor(plugins): move provider seams to owning extensions
This commit is contained in:
@@ -34,6 +34,21 @@ vi.mock("../agents/model-auth.js", () => ({
|
||||
hasUsableCustomProviderApiKey,
|
||||
}));
|
||||
|
||||
const resolveOwningPluginIdsForProvider = vi.hoisted(() =>
|
||||
vi.fn(({ provider }: { provider: string }) => {
|
||||
if (provider === "byteplus" || provider === "byteplus-plan") {
|
||||
return ["byteplus"];
|
||||
}
|
||||
if (provider === "volcengine" || provider === "volcengine-plan") {
|
||||
return ["volcengine"];
|
||||
}
|
||||
return undefined;
|
||||
}),
|
||||
);
|
||||
vi.mock("../plugins/providers.js", () => ({
|
||||
resolveOwningPluginIdsForProvider,
|
||||
}));
|
||||
|
||||
const providerModelPickerContributionRuntime = vi.hoisted(() => ({
|
||||
enabled: false,
|
||||
resolve: vi.fn(() => []),
|
||||
@@ -85,6 +100,15 @@ function createSelectAllMultiselect() {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
providerModelPickerContributionRuntime.enabled = false;
|
||||
resolveOwningPluginIdsForProvider.mockImplementation(({ provider }: { provider: string }) => {
|
||||
if (provider === "byteplus" || provider === "byteplus-plan") {
|
||||
return ["byteplus"];
|
||||
}
|
||||
if (provider === "volcengine" || provider === "volcengine-plan") {
|
||||
return ["volcengine"];
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
});
|
||||
|
||||
describe("promptDefaultModel", () => {
|
||||
@@ -167,6 +191,12 @@ describe("promptDefaultModel", () => {
|
||||
expect(optionValues[1]).toBe("byteplus-plan/ark-code-latest");
|
||||
expect(select.mock.calls[0]?.[0]?.initialValue).toBe("byteplus-plan/ark-code-latest");
|
||||
expect(result.model).toBe("byteplus-plan/ark-code-latest");
|
||||
expect(resolveOwningPluginIdsForProvider).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ provider: "byteplus" }),
|
||||
);
|
||||
expect(resolveOwningPluginIdsForProvider).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ provider: "byteplus-plan" }),
|
||||
);
|
||||
});
|
||||
|
||||
it("supports configuring vLLM during setup", async () => {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import {
|
||||
applyOpencodeZenModelDefault,
|
||||
OPENCODE_ZEN_DEFAULT_MODEL,
|
||||
} from "../plugins/provider-model-defaults.js";
|
||||
} from "../../extensions/opencode/api.js";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import type { WizardPrompter } from "../wizard/prompts.js";
|
||||
import { applyDefaultModelChoice } from "./auth-choice.default-model.js";
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import { formatTokenK } from "../commands/models/shared.js";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { resolveAgentModelPrimaryValue } from "../config/model-input.js";
|
||||
import { applyPrimaryModel } from "../plugins/provider-model-primary.js";
|
||||
import { resolveOwningPluginIdsForProvider } from "../plugins/providers.js";
|
||||
import type { ProviderPlugin } from "../plugins/types.js";
|
||||
import type { RuntimeEnv } from "../runtime.js";
|
||||
import { createLazyRuntimeSurface } from "../shared/lazy-runtime.js";
|
||||
@@ -172,14 +173,43 @@ function addModelSelectOption(params: {
|
||||
params.seen.add(key);
|
||||
}
|
||||
|
||||
function matchesPreferredProvider(entryProvider: string, preferredProvider: string): boolean {
|
||||
if (preferredProvider === "volcengine") {
|
||||
return entryProvider === "volcengine" || entryProvider === "volcengine-plan";
|
||||
}
|
||||
if (preferredProvider === "byteplus") {
|
||||
return entryProvider === "byteplus" || entryProvider === "byteplus-plan";
|
||||
}
|
||||
return entryProvider === preferredProvider;
|
||||
function createPreferredProviderMatcher(params: {
|
||||
preferredProvider: string;
|
||||
cfg: OpenClawConfig;
|
||||
workspaceDir?: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
}): (entryProvider: string) => boolean {
|
||||
const normalizedPreferredProvider = normalizeProviderId(params.preferredProvider);
|
||||
const preferredOwnerPluginIds = resolveOwningPluginIdsForProvider({
|
||||
provider: normalizedPreferredProvider,
|
||||
config: params.cfg,
|
||||
workspaceDir: params.workspaceDir,
|
||||
env: params.env,
|
||||
});
|
||||
const preferredOwnerPluginIdSet = preferredOwnerPluginIds
|
||||
? new Set(preferredOwnerPluginIds)
|
||||
: undefined;
|
||||
const entryProviderCache = new Map<string, boolean>();
|
||||
return (entryProvider: string) => {
|
||||
const normalizedEntryProvider = normalizeProviderId(entryProvider);
|
||||
if (normalizedEntryProvider === normalizedPreferredProvider) {
|
||||
return true;
|
||||
}
|
||||
const cached = entryProviderCache.get(normalizedEntryProvider);
|
||||
if (cached !== undefined) {
|
||||
return cached;
|
||||
}
|
||||
const value =
|
||||
!!preferredOwnerPluginIdSet &&
|
||||
!!resolveOwningPluginIdsForProvider({
|
||||
provider: normalizedEntryProvider,
|
||||
config: params.cfg,
|
||||
workspaceDir: params.workspaceDir,
|
||||
env: params.env,
|
||||
})?.some((pluginId) => preferredOwnerPluginIdSet.has(pluginId));
|
||||
entryProviderCache.set(normalizedEntryProvider, value);
|
||||
return value;
|
||||
};
|
||||
}
|
||||
|
||||
async function promptManualModel(params: {
|
||||
@@ -226,6 +256,9 @@ async function maybeFilterModelsByProvider(params: {
|
||||
}>;
|
||||
preferredProvider?: string;
|
||||
prompter: WizardPrompter;
|
||||
cfg: OpenClawConfig;
|
||||
workspaceDir?: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
}): Promise<typeof params.models> {
|
||||
const providerIds = Array.from(new Set(params.models.map((entry) => entry.provider))).toSorted(
|
||||
(a, b) => a.localeCompare(b),
|
||||
@@ -236,6 +269,14 @@ async function maybeFilterModelsByProvider(params: {
|
||||
providerIds.length > 1 &&
|
||||
params.models.length > PROVIDER_FILTER_THRESHOLD;
|
||||
let next = params.models;
|
||||
const matchesPreferredProvider = params.preferredProvider
|
||||
? createPreferredProviderMatcher({
|
||||
preferredProvider: params.preferredProvider,
|
||||
cfg: params.cfg,
|
||||
workspaceDir: params.workspaceDir,
|
||||
env: params.env,
|
||||
})
|
||||
: undefined;
|
||||
if (shouldPromptProvider) {
|
||||
const selection = await params.prompter.select({
|
||||
message: "Filter models by provider",
|
||||
@@ -246,9 +287,7 @@ async function maybeFilterModelsByProvider(params: {
|
||||
}
|
||||
}
|
||||
if (hasPreferredProvider && params.preferredProvider) {
|
||||
const filtered = next.filter((entry) =>
|
||||
matchesPreferredProvider(entry.provider, params.preferredProvider!),
|
||||
);
|
||||
const filtered = next.filter((entry) => matchesPreferredProvider?.(entry.provider));
|
||||
if (filtered.length > 0) {
|
||||
next = filtered;
|
||||
}
|
||||
@@ -418,9 +457,20 @@ export async function promptDefaultModel(
|
||||
models,
|
||||
preferredProvider,
|
||||
prompter: params.prompter,
|
||||
cfg,
|
||||
workspaceDir: params.workspaceDir,
|
||||
env: params.env,
|
||||
});
|
||||
const matchesPreferredProvider = preferredProvider
|
||||
? createPreferredProviderMatcher({
|
||||
preferredProvider,
|
||||
cfg,
|
||||
workspaceDir: params.workspaceDir,
|
||||
env: params.env,
|
||||
})
|
||||
: undefined;
|
||||
const hasPreferredProvider = preferredProvider
|
||||
? filteredModels.some((entry) => matchesPreferredProvider(entry.provider, preferredProvider))
|
||||
? filteredModels.some((entry) => matchesPreferredProvider?.(entry.provider))
|
||||
: false;
|
||||
const hasAuth = createProviderAuthChecker({ cfg, agentDir: params.agentDir });
|
||||
|
||||
@@ -465,7 +515,7 @@ export async function promptDefaultModel(
|
||||
allowKeep &&
|
||||
hasPreferredProvider &&
|
||||
preferredProvider &&
|
||||
!matchesPreferredProvider(resolved.provider, preferredProvider)
|
||||
!matchesPreferredProvider?.(resolved.provider)
|
||||
) {
|
||||
const firstModel = filteredModels[0];
|
||||
if (firstModel) {
|
||||
@@ -570,6 +620,12 @@ export async function promptModelAllowlist(params: {
|
||||
defaultProvider: DEFAULT_PROVIDER,
|
||||
});
|
||||
const hasAuth = createProviderAuthChecker({ cfg, agentDir: params.agentDir });
|
||||
const matchesPreferredProvider = preferredProvider
|
||||
? createPreferredProviderMatcher({
|
||||
preferredProvider,
|
||||
cfg,
|
||||
})
|
||||
: undefined;
|
||||
|
||||
const options: WizardSelectOption[] = [];
|
||||
const seen = new Set<string>();
|
||||
@@ -577,11 +633,8 @@ export async function promptModelAllowlist(params: {
|
||||
? catalog.filter((entry) => allowedKeySet.has(modelKey(entry.provider, entry.id)))
|
||||
: catalog;
|
||||
const filteredCatalog =
|
||||
preferredProvider &&
|
||||
allowedCatalog.some((entry) => matchesPreferredProvider(entry.provider, preferredProvider))
|
||||
? allowedCatalog.filter((entry) =>
|
||||
matchesPreferredProvider(entry.provider, preferredProvider),
|
||||
)
|
||||
preferredProvider && allowedCatalog.some((entry) => matchesPreferredProvider?.(entry.provider))
|
||||
? allowedCatalog.filter((entry) => matchesPreferredProvider?.(entry.provider))
|
||||
: allowedCatalog;
|
||||
|
||||
for (const entry of filteredCatalog) {
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { ensureModelAllowlistEntry } from "./provider-model-allowlist.js";
|
||||
import { applyAgentDefaultPrimaryModel } from "./provider-model-primary.js";
|
||||
export {
|
||||
applyOpencodeZenModelDefault,
|
||||
OPENCODE_ZEN_DEFAULT_MODEL,
|
||||
} from "../../extensions/opencode/api.js";
|
||||
|
||||
export const OPENAI_DEFAULT_MODEL = "openai/gpt-5.4";
|
||||
export const OPENAI_CODEX_DEFAULT_MODEL = "openai-codex/gpt-5.4";
|
||||
@@ -12,12 +16,6 @@ export const OPENAI_DEFAULT_EMBEDDING_MODEL = "text-embedding-3-small";
|
||||
export const GOOGLE_GEMINI_DEFAULT_MODEL = "google/gemini-3.1-pro-preview";
|
||||
export const OLLAMA_DEFAULT_BASE_URL = "http://127.0.0.1:11434";
|
||||
export const OPENCODE_GO_DEFAULT_MODEL_REF = "opencode-go/kimi-k2.5";
|
||||
export const OPENCODE_ZEN_DEFAULT_MODEL = "opencode/claude-opus-4-6";
|
||||
|
||||
const LEGACY_OPENCODE_ZEN_DEFAULT_MODELS = new Set([
|
||||
"opencode/claude-opus-4-5",
|
||||
"opencode-zen/claude-opus-4-5",
|
||||
]);
|
||||
|
||||
export function applyGoogleGeminiModelDefault(cfg: OpenClawConfig): {
|
||||
next: OpenClawConfig;
|
||||
@@ -75,14 +73,3 @@ export function applyOpencodeGoModelDefault(cfg: OpenClawConfig): {
|
||||
} {
|
||||
return applyAgentDefaultPrimaryModel({ cfg, model: OPENCODE_GO_DEFAULT_MODEL_REF });
|
||||
}
|
||||
|
||||
export function applyOpencodeZenModelDefault(cfg: OpenClawConfig): {
|
||||
next: OpenClawConfig;
|
||||
changed: boolean;
|
||||
} {
|
||||
return applyAgentDefaultPrimaryModel({
|
||||
cfg,
|
||||
model: OPENCODE_ZEN_DEFAULT_MODEL,
|
||||
legacyModels: LEGACY_OPENCODE_ZEN_DEFAULT_MODELS,
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user