fix: limit /model list output
This commit is contained in:
@@ -98,6 +98,7 @@ Docs: https://docs.clawd.bot
|
|||||||
- **BREAKING:** Reject invalid/unknown config entries and refuse to start the gateway for safety. Run `clawdbot doctor --fix` to repair, then update plugins (`clawdbot plugins update`) if you use any.
|
- **BREAKING:** Reject invalid/unknown config entries and refuse to start the gateway for safety. Run `clawdbot doctor --fix` to repair, then update plugins (`clawdbot plugins update`) if you use any.
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
- Models: limit `/model list` chat output to configured models when no allowlist is set.
|
||||||
- Discovery: shorten Bonjour DNS-SD service type to `_clawdbot-gw._tcp` and update discovery clients/docs.
|
- Discovery: shorten Bonjour DNS-SD service type to `_clawdbot-gw._tcp` and update discovery clients/docs.
|
||||||
- Diagnostics: export OTLP logs, correct queue depth tracking, and document message-flow telemetry.
|
- Diagnostics: export OTLP logs, correct queue depth tracking, and document message-flow telemetry.
|
||||||
- Diagnostics: emit message-flow diagnostics across channels via shared dispatch. (#1244)
|
- Diagnostics: emit message-flow diagnostics across channels via shared dispatch. (#1244)
|
||||||
|
|||||||
@@ -121,6 +121,42 @@ describe("directive behavior", () => {
|
|||||||
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
|
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
it("uses configured models when no allowlist is set", async () => {
|
||||||
|
await withTempHome(async (home) => {
|
||||||
|
vi.mocked(runEmbeddedPiAgent).mockReset();
|
||||||
|
vi.mocked(loadModelCatalog).mockResolvedValueOnce([
|
||||||
|
{ id: "claude-opus-4-5", name: "Opus 4.5", provider: "anthropic" },
|
||||||
|
{ id: "gpt-4.1-mini", name: "GPT-4.1 Mini", provider: "openai" },
|
||||||
|
{ id: "grok-4", name: "Grok 4", provider: "xai" },
|
||||||
|
]);
|
||||||
|
const storePath = path.join(home, "sessions.json");
|
||||||
|
|
||||||
|
const res = await getReplyFromConfig(
|
||||||
|
{ Body: "/model list", From: "+1222", To: "+1222", CommandAuthorized: true },
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
agents: {
|
||||||
|
defaults: {
|
||||||
|
model: {
|
||||||
|
primary: "anthropic/claude-opus-4-5",
|
||||||
|
fallbacks: ["openai/gpt-4.1-mini"],
|
||||||
|
},
|
||||||
|
imageModel: { primary: "minimax/MiniMax-M2.1" },
|
||||||
|
workspace: path.join(home, "clawd"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
session: { store: storePath },
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const text = Array.isArray(res) ? res[0]?.text : res?.text;
|
||||||
|
expect(text).toContain("anthropic/claude-opus-4-5");
|
||||||
|
expect(text).toContain("openai/gpt-4.1-mini");
|
||||||
|
expect(text).toContain("minimax/MiniMax-M2.1");
|
||||||
|
expect(text).not.toContain("xai/grok-4");
|
||||||
|
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
it("merges config allowlist models even when catalog is present", async () => {
|
it("merges config allowlist models even when catalog is present", async () => {
|
||||||
await withTempHome(async (home) => {
|
await withTempHome(async (home) => {
|
||||||
vi.mocked(runEmbeddedPiAgent).mockReset();
|
vi.mocked(runEmbeddedPiAgent).mockReset();
|
||||||
|
|||||||
@@ -36,6 +36,56 @@ function buildModelPickerCatalog(params: {
|
|||||||
defaultModel: params.defaultModel,
|
defaultModel: params.defaultModel,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const buildConfiguredCatalog = (): ModelPickerCatalogEntry[] => {
|
||||||
|
const out: ModelPickerCatalogEntry[] = [];
|
||||||
|
const keys = new Set<string>();
|
||||||
|
|
||||||
|
const pushRef = (ref: { provider: string; model: string }, name?: string) => {
|
||||||
|
const provider = normalizeProviderId(ref.provider);
|
||||||
|
const id = String(ref.model ?? "").trim();
|
||||||
|
if (!provider || !id) return;
|
||||||
|
const key = modelKey(provider, id);
|
||||||
|
if (keys.has(key)) return;
|
||||||
|
keys.add(key);
|
||||||
|
out.push({ provider, id, name: name ?? id });
|
||||||
|
};
|
||||||
|
|
||||||
|
const pushRaw = (raw?: string) => {
|
||||||
|
const value = String(raw ?? "").trim();
|
||||||
|
if (!value) return;
|
||||||
|
const resolved = resolveModelRefFromString({
|
||||||
|
raw: value,
|
||||||
|
defaultProvider: params.defaultProvider,
|
||||||
|
aliasIndex: params.aliasIndex,
|
||||||
|
});
|
||||||
|
if (!resolved) return;
|
||||||
|
pushRef(resolved.ref);
|
||||||
|
};
|
||||||
|
|
||||||
|
pushRef(resolvedDefault);
|
||||||
|
|
||||||
|
const modelConfig = params.cfg.agents?.defaults?.model;
|
||||||
|
const modelFallbacks =
|
||||||
|
modelConfig && typeof modelConfig === "object" ? (modelConfig.fallbacks ?? []) : [];
|
||||||
|
for (const fallback of modelFallbacks) {
|
||||||
|
pushRaw(String(fallback ?? ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
const imageConfig = params.cfg.agents?.defaults?.imageModel;
|
||||||
|
if (imageConfig && typeof imageConfig === "object") {
|
||||||
|
pushRaw(imageConfig.primary);
|
||||||
|
for (const fallback of imageConfig.fallbacks ?? []) {
|
||||||
|
pushRaw(String(fallback ?? ""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const raw of Object.keys(params.cfg.agents?.defaults?.models ?? {})) {
|
||||||
|
pushRaw(raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
};
|
||||||
|
|
||||||
const keys = new Set<string>();
|
const keys = new Set<string>();
|
||||||
const out: ModelPickerCatalogEntry[] = [];
|
const out: ModelPickerCatalogEntry[] = [];
|
||||||
|
|
||||||
@@ -49,6 +99,14 @@ function buildModelPickerCatalog(params: {
|
|||||||
out.push({ provider, id, name: entry.name });
|
out.push({ provider, id, name: entry.name });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const hasAllowlist = Object.keys(params.cfg.agents?.defaults?.models ?? {}).length > 0;
|
||||||
|
if (!hasAllowlist) {
|
||||||
|
for (const entry of buildConfiguredCatalog()) {
|
||||||
|
push(entry);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
// Prefer catalog entries (when available), but always merge in config-only
|
// Prefer catalog entries (when available), but always merge in config-only
|
||||||
// allowlist entries. This keeps custom providers/models visible in /model.
|
// allowlist entries. This keeps custom providers/models visible in /model.
|
||||||
for (const entry of params.allowedModelCatalog) {
|
for (const entry of params.allowedModelCatalog) {
|
||||||
|
|||||||
Reference in New Issue
Block a user