Telegram: resolve configured model buttons

This commit is contained in:
Alexander Davydov 2026-03-19 18:50:07 +03:00
parent 5df5f68241
commit 0a84b3de0c
2 changed files with 89 additions and 48 deletions

View File

@ -28,6 +28,7 @@ import {
} from "openclaw/plugin-sdk/reply-runtime";
import { buildCommandsPaginationKeyboard } from "openclaw/plugin-sdk/reply-runtime";
import {
buildConfiguredModelsProviderData,
buildModelsProviderData,
formatModelsAvailableHeader,
} from "openclaw/plugin-sdk/reply-runtime";
@ -1339,8 +1340,9 @@ export const registerTelegramHandlers = ({
resolvedThreadId,
senderId,
});
const modelData = await buildModelsProviderData(cfg, sessionState.agentId);
const { byProvider, providers } = modelData;
const configuredModelData = buildConfiguredModelsProviderData(cfg, sessionState.agentId);
let modelData = configuredModelData;
let { byProvider, providers } = modelData;
const editMessageWithButtons = async (
text: string,
@ -1428,11 +1430,28 @@ export const registerTelegramHandlers = ({
}
if (modelCallback.type === "select") {
const selection = resolveModelSelection({
let selection = resolveModelSelection({
callback: modelCallback,
providers,
byProvider,
});
let selectionAllowed =
selection.kind === "resolved" &&
Boolean(byProvider.get(selection.provider)?.has(selection.model));
if (!selectionAllowed || selection.kind !== "resolved") {
modelData = await buildModelsProviderData(cfg, sessionState.agentId);
({ byProvider, providers } = modelData);
selection = resolveModelSelection({
callback: modelCallback,
providers,
byProvider,
});
selectionAllowed =
selection.kind === "resolved" &&
Boolean(byProvider.get(selection.provider)?.has(selection.model));
}
if (selection.kind !== "resolved") {
const providerInfos: ProviderInfo[] = providers.map((p) => ({
id: p,
@ -1446,8 +1465,7 @@ export const registerTelegramHandlers = ({
return;
}
const modelSet = byProvider.get(selection.provider);
if (!modelSet?.has(selection.model)) {
if (!selectionAllowed) {
await editMessageWithButtons(
`❌ Model "${selection.provider}/${selection.model}" is not allowed.`,
[],

View File

@ -30,40 +30,22 @@ export type ModelsProviderData = {
resolvedDefault: { provider: string; model: string };
};
/**
* Build provider/model data from config and catalog.
* Exported for reuse by callback handlers.
*/
export async function buildModelsProviderData(
cfg: OpenClawConfig,
agentId?: string,
): Promise<ModelsProviderData> {
const resolvedDefault = resolveDefaultModelForAgent({
cfg,
agentId,
});
const catalog = await loadModelCatalog({ config: cfg });
const allowed = buildAllowedModelSet({
cfg,
catalog,
defaultProvider: resolvedDefault.provider,
defaultModel: resolvedDefault.model,
agentId,
});
function addProviderModel(byProvider: Map<string, Set<string>>, provider: string, model: string) {
const key = normalizeProviderId(provider);
const set = byProvider.get(key) ?? new Set<string>();
set.add(model);
byProvider.set(key, set);
}
function buildConfiguredProviderModelMap(params: {
cfg: OpenClawConfig;
resolvedDefault: { provider: string; model: string };
}): Map<string, Set<string>> {
const aliasIndex = buildModelAliasIndex({
cfg,
defaultProvider: resolvedDefault.provider,
cfg: params.cfg,
defaultProvider: params.resolvedDefault.provider,
});
const byProvider = new Map<string, Set<string>>();
const add = (p: string, m: string) => {
const key = normalizeProviderId(p);
const set = byProvider.get(key) ?? new Set<string>();
set.add(m);
byProvider.set(key, set);
};
const addRawModelRef = (raw?: string) => {
const trimmed = raw?.trim();
@ -72,17 +54,17 @@ export async function buildModelsProviderData(
}
const resolved = resolveModelRefFromString({
raw: trimmed,
defaultProvider: resolvedDefault.provider,
defaultProvider: params.resolvedDefault.provider,
aliasIndex,
});
if (!resolved) {
return;
}
add(resolved.ref.provider, resolved.ref.model);
addProviderModel(byProvider, resolved.ref.provider, resolved.ref.model);
};
const addModelConfigEntries = () => {
const modelConfig = cfg.agents?.defaults?.model;
const modelConfig = params.cfg.agents?.defaults?.model;
if (typeof modelConfig === "string") {
addRawModelRef(modelConfig);
} else if (modelConfig && typeof modelConfig === "object") {
@ -92,7 +74,7 @@ export async function buildModelsProviderData(
}
}
const imageConfig = cfg.agents?.defaults?.imageModel;
const imageConfig = params.cfg.agents?.defaults?.imageModel;
if (typeof imageConfig === "string") {
addRawModelRef(imageConfig);
} else if (imageConfig && typeof imageConfig === "object") {
@ -103,20 +85,61 @@ export async function buildModelsProviderData(
}
};
for (const entry of allowed.allowedCatalog) {
add(entry.provider, entry.id);
}
// Include config-only allowlist keys that aren't in the curated catalog.
for (const raw of Object.keys(cfg.agents?.defaults?.models ?? {})) {
for (const raw of Object.keys(params.cfg.agents?.defaults?.models ?? {})) {
addRawModelRef(raw);
}
// Ensure configured defaults/fallbacks/image models show up even when the
// curated catalog doesn't know about them (custom providers, dev builds, etc.).
add(resolvedDefault.provider, resolvedDefault.model);
// Always include the resolved default model even when it is not in the
// curated catalog yet (custom providers, discovery-backed providers, etc.).
addProviderModel(byProvider, params.resolvedDefault.provider, params.resolvedDefault.model);
addModelConfigEntries();
return byProvider;
}
export function buildConfiguredModelsProviderData(
cfg: OpenClawConfig,
agentId?: string,
): ModelsProviderData {
const resolvedDefault = resolveDefaultModelForAgent({
cfg,
agentId,
});
const byProvider = buildConfiguredProviderModelMap({
cfg,
resolvedDefault,
});
const providers = [...byProvider.keys()].toSorted();
return { byProvider, providers, resolvedDefault };
}
/**
* Build provider/model data from config and catalog.
* Exported for reuse by callback handlers.
*/
export async function buildModelsProviderData(
cfg: OpenClawConfig,
agentId?: string,
): Promise<ModelsProviderData> {
const configured = buildConfiguredModelsProviderData(cfg, agentId);
const resolvedDefault = configured.resolvedDefault;
const byProvider = new Map(
[...configured.byProvider.entries()].map(([provider, models]) => [provider, new Set(models)]),
);
const catalog = await loadModelCatalog({ config: cfg });
const allowed = buildAllowedModelSet({
cfg,
catalog,
defaultProvider: resolvedDefault.provider,
defaultModel: resolvedDefault.model,
agentId,
});
for (const entry of allowed.allowedCatalog) {
addProviderModel(byProvider, entry.provider, entry.id);
}
const providers = [...byProvider.keys()].toSorted();
return { byProvider, providers, resolvedDefault };