fix(cli): reject malformed infer model refs (#115225)

This commit is contained in:
Peter Steinberger
2026-07-28 12:03:58 -04:00
committed by GitHub
parent b2859cec4b
commit 964c5abbde
4 changed files with 35 additions and 4 deletions

View File

@@ -2979,6 +2979,34 @@ describe("capability cli", () => {
expect(firstEmbeddingProviderCall()?.model).toBe("text-embedding-3-large");
});
it.each([
{
name: "embedding create",
argv: ["capability", "embedding", "create", "--text", "hello"],
},
{
name: "image generate",
argv: ["capability", "image", "generate", "--prompt", "portrait"],
},
{
name: "image edit",
argv: ["capability", "image", "edit", "--file", "photo.png", "--prompt", "crop it"],
},
{
name: "video generate",
argv: ["capability", "video", "generate", "--prompt", "clip"],
},
])("rejects malformed model refs before $name provider dispatch", async ({ argv }) => {
for (const model of ["openai/", "/gpt-4.1-mini"]) {
await expect(runCap(...argv, "--model", model, "--json")).rejects.toThrow("exit 1");
expectRuntimeErrorContains("Model overrides must use the form <provider/model>.");
expect(mocks.resolveCommandConfigWithSecrets).not.toHaveBeenCalled();
expect(mocks.createEmbeddingProvider).not.toHaveBeenCalled();
expect(mocks.generateImage).not.toHaveBeenCalled();
expect(mocks.generateVideo).not.toHaveBeenCalled();
}
});
it("cleans provider auth profiles and usage stats on logout", async () => {
mocks.loadAuthProfileStoreForRuntime.mockReturnValue({
profiles: {

View File

@@ -16,8 +16,8 @@ import {
formatEnvelopeForText,
providerHasGenericConfig,
providerSummaryText,
requireProviderModelOverride,
resolveLocalCapabilityRuntimeConfig,
resolveModelRefOverride,
} from "./shared.js";
async function closeEmbeddingProviderWithRetry(provider: {
@@ -40,18 +40,19 @@ async function runMemoryEmbeddingCreate(params: {
provider?: string;
model?: string;
}) {
const modelRef = requireProviderModelOverride(params.model);
const cfg = await resolveLocalCapabilityRuntimeConfig({
commandName: "infer embedding create",
targetIds: getMemoryEmbeddingCommandSecretTargetIds(),
});
const modelRef = resolveModelRefOverride(params.model);
const requestedProvider = normalizeOptionalString(params.provider) || modelRef.provider || "auto";
const requestedProvider =
normalizeOptionalString(params.provider) || modelRef?.provider || "auto";
const result = await createEmbeddingProvider({
config: cfg,
agentDir: resolveAgentDir(cfg, resolveDefaultAgentId(cfg)),
provider: requestedProvider,
fallback: "none",
model: modelRef.model ?? "",
model: modelRef?.model ?? "",
});
if (!result.provider) {
throw new Error(result.providerUnavailableReason ?? "No embedding provider available.");

View File

@@ -64,6 +64,7 @@ async function runImageGenerate(params: {
output?: string;
timeoutMs?: number;
}) {
requireProviderModelOverride(params.model);
const cfg = await resolveLocalCapabilityRuntimeConfig({
commandName: `infer ${params.capability}`,
targetIds: getModelsCommandSecretTargetIds(),

View File

@@ -109,6 +109,7 @@ async function runVideoGenerate(params: {
watermark?: boolean;
timeoutMs?: number;
}) {
requireProviderModelOverride(params.model);
const cfg = await resolveLocalCapabilityRuntimeConfig({
commandName: "infer video.generate",
targetIds: getModelsCommandSecretTargetIds(),