mirror of
https://github.com/moltbot/moltbot.git
synced 2026-05-06 23:55:12 +00:00
fix(providers): preserve defaults during auth setup
This commit is contained in:
@@ -22,6 +22,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Heartbeat: strip legacy `[TOOL_CALL]...[/TOOL_CALL]` and `[TOOL_RESULT]...[/TOOL_RESULT]` pseudo-call blocks from heartbeat replies before channel delivery. Fixes #54138. Thanks @Deniable9570.
|
||||
- macOS/Voice Wake: send wake-word and Push-to-Talk transcripts through the selected macOS session target instead of always falling back to main WebChat. Fixes #51040. Thanks @carl-jeffrolc.
|
||||
- Providers/xAI: give Grok `web_search` a 60s default timeout, harden malformed xAI Responses parsing, and return structured timeout errors instead of aborting the tool call. Fixes #58063 and #58733. Thanks @dnishimura, @marvcasasola-svg, and @Nanako0129.
|
||||
- Providers/configure: preserve the existing default model when adding or reauthing a provider whose plugin returns a default-model config patch. Fixes #50268. Thanks @rixcorp-oc.
|
||||
- Heartbeat: strip legacy `[TOOL_CALL]...[/TOOL_CALL]` and `[TOOL_RESULT]...[/TOOL_RESULT]` pseudo-call blocks from heartbeat replies before channel delivery. Fixes #54138. Thanks @Deniable9570.
|
||||
- macOS/Voice Wake: send wake-word and Push-to-Talk transcripts through the selected macOS session target instead of always falling back to main WebChat. Fixes #51040. Thanks @carl-jeffrolc.
|
||||
- Providers/xAI: give Grok `web_search` a 60s default timeout, harden malformed xAI Responses parsing, and return structured timeout errors instead of aborting the tool call. Fixes #58063 and #58733. Thanks @dnishimura, @marvcasasola-svg, and @Nanako0129.
|
||||
|
||||
@@ -10,7 +10,9 @@ title: "Configure"
|
||||
Interactive prompt to set up credentials, devices, and agent defaults.
|
||||
|
||||
<Note>
|
||||
The **Model** section includes a multi-select for the `agents.defaults.models` allowlist (what shows up in `/model` and the model picker). Provider-scoped setup choices merge their selected models into the existing allowlist instead of replacing unrelated providers already in the config. Re-running provider auth from configure preserves an existing `agents.defaults.model.primary`. Use `openclaw models auth login --provider <id> --set-default` or `openclaw models set <model>` when you intentionally want to change the default model.
|
||||
The **Model** section includes a multi-select for the `agents.defaults.models` allowlist (what shows up in `/model` and the model picker). Provider-scoped setup choices merge their selected models into the existing allowlist instead of replacing unrelated providers already in the config.
|
||||
|
||||
Re-running provider auth from configure preserves an existing `agents.defaults.model.primary`, even when the provider's auth step returns a config patch with its own recommended default model. That means adding or reauthing xAI, OpenRouter, or another provider should make the new model available without taking over from your current primary model. Use `openclaw models auth login --provider <id> --set-default` or `openclaw models set <model>` when you intentionally want to change the default model.
|
||||
</Note>
|
||||
|
||||
When configure starts from a provider auth choice, the default-model and allowlist pickers prefer that provider automatically. For paired providers such as Volcengine and BytePlus, the same preference also matches their coding-plan variants (`volcengine-plan/*`, `byteplus-plan/*`). If the preferred-provider filter would produce an empty list, configure falls back to the unfiltered catalog instead of showing a blank picker.
|
||||
|
||||
@@ -19,6 +19,12 @@ Reference for **LLM/model providers** (not chat channels like WhatsApp/Telegram)
|
||||
- `models.providers.*.contextWindow` / `contextTokens` / `maxTokens` set provider-level defaults; `models.providers.*.models[].contextWindow` / `contextTokens` / `maxTokens` override them per model.
|
||||
- Fallback rules, cooldown probes, and session-override persistence: [Model failover](/concepts/model-failover).
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Adding provider auth does not change your primary model">
|
||||
`openclaw configure` preserves an existing `agents.defaults.model.primary` when you add or reauth a provider. Provider plugins may still return a recommended default model in their auth config patch, but configure treats that as "make this model available" when a primary model already exists, not "replace the current primary model."
|
||||
|
||||
To intentionally switch the default model, use `openclaw models set <provider/model>` or `openclaw models auth login --provider <id> --set-default`.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="OpenAI provider/runtime split">
|
||||
OpenAI-family routes are prefix-specific:
|
||||
|
||||
@@ -105,6 +105,7 @@ const LOCAL_AUTH_METHOD_ID = "local";
|
||||
const LOCAL_PROFILE_ID = `${LOCAL_PROVIDER_ID}:default`;
|
||||
const LOCAL_API_KEY = "local-provider-key";
|
||||
const LOCAL_DEFAULT_MODEL = `${LOCAL_PROVIDER_ID}/demo-model`;
|
||||
const EXISTING_DEFAULT_MODEL = "amazon-bedrock/anthropic.claude-3-5-sonnet-20241022-v2:0";
|
||||
|
||||
function buildProvider(): ProviderPlugin {
|
||||
return {
|
||||
@@ -133,6 +134,43 @@ function buildProvider(): ProviderPlugin {
|
||||
};
|
||||
}
|
||||
|
||||
function buildProviderWithDefaultModelPatch(): ProviderPlugin {
|
||||
return {
|
||||
id: LOCAL_PROVIDER_ID,
|
||||
label: LOCAL_PROVIDER_LABEL,
|
||||
auth: [
|
||||
{
|
||||
id: LOCAL_AUTH_METHOD_ID,
|
||||
label: LOCAL_PROVIDER_LABEL,
|
||||
kind: "custom",
|
||||
run: async () => ({
|
||||
profiles: [
|
||||
{
|
||||
profileId: LOCAL_PROFILE_ID,
|
||||
credential: {
|
||||
type: "api_key",
|
||||
provider: LOCAL_PROVIDER_ID,
|
||||
key: LOCAL_API_KEY,
|
||||
},
|
||||
},
|
||||
],
|
||||
configPatch: {
|
||||
agents: {
|
||||
defaults: {
|
||||
model: { primary: LOCAL_DEFAULT_MODEL },
|
||||
models: {
|
||||
[LOCAL_DEFAULT_MODEL]: { alias: "Local default" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
defaultModel: LOCAL_DEFAULT_MODEL,
|
||||
}),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
function buildParams(overrides: Partial<ApplyAuthChoiceParams> = {}): ApplyAuthChoiceParams {
|
||||
return {
|
||||
authChoice: LOCAL_PROVIDER_ID,
|
||||
@@ -332,6 +370,48 @@ describe("applyAuthChoiceLoadedPluginProvider", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps an existing default when provider auth patches its own primary model", async () => {
|
||||
const provider = buildProviderWithDefaultModelPatch();
|
||||
resolvePluginProviders.mockReturnValue([provider]);
|
||||
resolveProviderPluginChoice.mockReturnValue({
|
||||
provider,
|
||||
method: provider.auth[0],
|
||||
});
|
||||
const note = vi.fn(async () => {});
|
||||
|
||||
const result = await applyAuthChoiceLoadedPluginProvider(
|
||||
buildParams({
|
||||
config: {
|
||||
agents: {
|
||||
defaults: {
|
||||
model: { primary: EXISTING_DEFAULT_MODEL },
|
||||
models: {
|
||||
[EXISTING_DEFAULT_MODEL]: { alias: "Bedrock" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
prompter: {
|
||||
note,
|
||||
} as unknown as ApplyAuthChoiceParams["prompter"],
|
||||
preserveExistingDefaultModel: true,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(result?.config.agents?.defaults?.model).toEqual({
|
||||
primary: EXISTING_DEFAULT_MODEL,
|
||||
});
|
||||
expect(result?.config.agents?.defaults?.models).toEqual({
|
||||
[EXISTING_DEFAULT_MODEL]: { alias: "Bedrock" },
|
||||
[LOCAL_DEFAULT_MODEL]: { alias: "Local default" },
|
||||
});
|
||||
expect(runProviderModelSelectedHook).not.toHaveBeenCalled();
|
||||
expect(note).toHaveBeenCalledWith(
|
||||
`Kept existing default model ${EXISTING_DEFAULT_MODEL}; ${LOCAL_DEFAULT_MODEL} is available.`,
|
||||
"Model configured",
|
||||
);
|
||||
});
|
||||
|
||||
it("uses manifest-owned setup providers without loading the broad provider runtime", async () => {
|
||||
const provider = buildProvider();
|
||||
resolveManifestProviderAuthChoice.mockReturnValue({
|
||||
@@ -611,6 +691,59 @@ describe("applyAuthChoiceLoadedPluginProvider", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("preserves the existing primary model for plugin auth choices that patch defaults", async () => {
|
||||
const provider = buildProviderWithDefaultModelPatch();
|
||||
resolvePluginProviders.mockReturnValue([provider]);
|
||||
const note = vi.fn(async () => {});
|
||||
|
||||
const result = await applyAuthChoicePluginProvider(
|
||||
buildParams({
|
||||
authChoice: `provider-plugin:${LOCAL_PROVIDER_ID}:${LOCAL_AUTH_METHOD_ID}`,
|
||||
config: {
|
||||
agents: {
|
||||
defaults: {
|
||||
model: { primary: EXISTING_DEFAULT_MODEL },
|
||||
models: {
|
||||
[EXISTING_DEFAULT_MODEL]: { alias: "Bedrock" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
prompter: {
|
||||
note,
|
||||
} as unknown as ApplyAuthChoiceParams["prompter"],
|
||||
preserveExistingDefaultModel: true,
|
||||
}),
|
||||
{
|
||||
authChoice: `provider-plugin:${LOCAL_PROVIDER_ID}:${LOCAL_AUTH_METHOD_ID}`,
|
||||
pluginId: LOCAL_PROVIDER_ID,
|
||||
providerId: LOCAL_PROVIDER_ID,
|
||||
methodId: LOCAL_AUTH_METHOD_ID,
|
||||
label: LOCAL_PROVIDER_LABEL,
|
||||
},
|
||||
);
|
||||
|
||||
expect(result?.config.agents?.defaults?.model).toEqual({
|
||||
primary: EXISTING_DEFAULT_MODEL,
|
||||
});
|
||||
expect(result?.config.agents?.defaults?.models).toEqual({
|
||||
[EXISTING_DEFAULT_MODEL]: { alias: "Bedrock" },
|
||||
[LOCAL_DEFAULT_MODEL]: { alias: "Local default" },
|
||||
});
|
||||
expect(result?.config.plugins).toEqual({
|
||||
entries: {
|
||||
[LOCAL_PROVIDER_ID]: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(runProviderModelSelectedHook).not.toHaveBeenCalled();
|
||||
expect(note).toHaveBeenCalledWith(
|
||||
`Kept existing default model ${EXISTING_DEFAULT_MODEL}; ${LOCAL_DEFAULT_MODEL} is available.`,
|
||||
"Model configured",
|
||||
);
|
||||
});
|
||||
|
||||
it("stops early when the plugin is disabled in config", async () => {
|
||||
const note = vi.fn(async () => {});
|
||||
|
||||
|
||||
@@ -130,18 +130,24 @@ async function noteDefaultModelResult(params: {
|
||||
|
||||
async function applyDefaultModelFromAuthChoice(params: {
|
||||
config: OpenClawConfig;
|
||||
configBeforeProviderAuth?: OpenClawConfig;
|
||||
selectedModel: string;
|
||||
selectedModelDisplay?: string;
|
||||
preserveExistingDefaultModel: boolean | undefined;
|
||||
prompter: WizardPrompter;
|
||||
runSelectedModelHook: (config: OpenClawConfig) => Promise<void>;
|
||||
}): Promise<OpenClawConfig> {
|
||||
const previousPrimary = resolveConfiguredDefaultModelPrimary(params.config);
|
||||
const defaultModelBaseConfig = params.configBeforeProviderAuth ?? params.config;
|
||||
const previousPrimary = resolveConfiguredDefaultModelPrimary(defaultModelBaseConfig);
|
||||
const preservesDifferentPrimary =
|
||||
params.preserveExistingDefaultModel === true &&
|
||||
previousPrimary !== undefined &&
|
||||
previousPrimary !== params.selectedModel;
|
||||
const nextConfig = applyDefaultModel(params.config, params.selectedModel, {
|
||||
const defaultModelConfig =
|
||||
params.preserveExistingDefaultModel === true
|
||||
? restoreConfiguredPrimaryModel(params.config, defaultModelBaseConfig)
|
||||
: params.config;
|
||||
const nextConfig = applyDefaultModel(defaultModelConfig, params.selectedModel, {
|
||||
preserveExistingPrimary: params.preserveExistingDefaultModel === true,
|
||||
});
|
||||
if (!preservesDifferentPrimary) {
|
||||
@@ -394,6 +400,7 @@ export async function applyAuthChoiceLoadedPluginProvider(
|
||||
nextConfig = enabledConfig;
|
||||
}
|
||||
|
||||
const configBeforeProviderAuth = nextConfig;
|
||||
const applied = await runProviderPluginAuthMethod({
|
||||
config: nextConfig,
|
||||
env: params.env,
|
||||
@@ -416,6 +423,7 @@ export async function applyAuthChoiceLoadedPluginProvider(
|
||||
if (params.setDefaultModel) {
|
||||
nextConfig = await applyDefaultModelFromAuthChoice({
|
||||
config: nextConfig,
|
||||
configBeforeProviderAuth,
|
||||
selectedModel,
|
||||
selectedModelDisplay,
|
||||
preserveExistingDefaultModel: params.preserveExistingDefaultModel,
|
||||
@@ -488,6 +496,7 @@ export async function applyAuthChoicePluginProvider(
|
||||
return { config: nextConfig };
|
||||
}
|
||||
|
||||
const configBeforeProviderAuth = nextConfig;
|
||||
const applied = await runProviderPluginAuthMethod({
|
||||
config: nextConfig,
|
||||
env: params.env,
|
||||
@@ -509,6 +518,7 @@ export async function applyAuthChoicePluginProvider(
|
||||
if (params.setDefaultModel) {
|
||||
nextConfig = await applyDefaultModelFromAuthChoice({
|
||||
config: nextConfig,
|
||||
configBeforeProviderAuth,
|
||||
selectedModel,
|
||||
selectedModelDisplay,
|
||||
preserveExistingDefaultModel: params.preserveExistingDefaultModel,
|
||||
|
||||
Reference in New Issue
Block a user