Skip to content

Commit 4878d3e

Browse files
authored
fix: resolve tts secret refs for local infer (#72549)
1 parent 6a05b9e commit 4878d3e

5 files changed

Lines changed: 97 additions & 2 deletions

src/cli/capability-cli.test.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,13 @@ const mocks = vi.hoisted(() => ({
9797
: {}),
9898
}),
9999
),
100+
resolveCommandSecretRefsViaGateway: vi.fn(async ({ config }: { config: unknown }) => ({
101+
resolvedConfig: config,
102+
diagnostics: [],
103+
targetStatesByPath: {},
104+
hadUnresolvedTargets: false,
105+
})),
106+
getTtsCommandSecretTargetIds: vi.fn(() => new Set(["messages.tts.providers.*.apiKey"])),
100107
createEmbeddingProvider: vi.fn(async () => ({
101108
provider: {
102109
id: "openai",
@@ -188,6 +195,14 @@ vi.mock("../gateway/connection-details.js", () => ({
188195
})),
189196
}));
190197

198+
vi.mock("./command-secret-gateway.js", () => ({
199+
resolveCommandSecretRefsViaGateway: mocks.resolveCommandSecretRefsViaGateway,
200+
}));
201+
202+
vi.mock("./command-secret-targets.js", () => ({
203+
getTtsCommandSecretTargetIds: mocks.getTtsCommandSecretTargetIds,
204+
}));
205+
191206
vi.mock("../media-understanding/runtime.js", () => ({
192207
describeImageFile:
193208
mocks.describeImageFile as typeof import("../media-understanding/runtime.js").describeImageFile,
@@ -311,6 +326,15 @@ describe("capability cli", () => {
311326
mocks.generateVideo.mockReset();
312327
mocks.transcribeAudioFile.mockClear();
313328
mocks.textToSpeech.mockClear();
329+
mocks.resolveCommandSecretRefsViaGateway
330+
.mockReset()
331+
.mockImplementation(async ({ config }: { config: unknown }) => ({
332+
resolvedConfig: config,
333+
diagnostics: [],
334+
targetStatesByPath: {},
335+
hadUnresolvedTargets: false,
336+
}));
337+
mocks.getTtsCommandSecretTargetIds.mockClear();
314338
mocks.setTtsProvider.mockClear();
315339
mocks.resolveExplicitTtsOverrides.mockClear();
316340
mocks.buildMediaUnderstandingRegistry.mockReset().mockReturnValue(new Map());
@@ -1057,6 +1081,58 @@ describe("capability cli", () => {
10571081
expect(mocks.setTtsProvider).not.toHaveBeenCalled();
10581082
});
10591083

1084+
it("resolves static TTS SecretRefs before local conversion", async () => {
1085+
const sourceConfig = {
1086+
messages: {
1087+
tts: {
1088+
providers: {
1089+
minimax: {
1090+
apiKey: { source: "exec", provider: "mockexec", id: "minimax/tts/apiKey" },
1091+
},
1092+
},
1093+
},
1094+
},
1095+
};
1096+
const resolvedConfig = {
1097+
messages: {
1098+
tts: {
1099+
providers: {
1100+
minimax: {
1101+
apiKey: "resolved-minimax-key",
1102+
},
1103+
},
1104+
},
1105+
},
1106+
};
1107+
mocks.loadConfig.mockReturnValueOnce(sourceConfig);
1108+
mocks.resolveCommandSecretRefsViaGateway.mockResolvedValueOnce({
1109+
resolvedConfig,
1110+
diagnostics: [],
1111+
targetStatesByPath: {
1112+
"messages.tts.providers.minimax.apiKey": "resolved_local",
1113+
},
1114+
hadUnresolvedTargets: false,
1115+
});
1116+
1117+
await runRegisteredCli({
1118+
register: registerCapabilityCli as (program: Command) => void,
1119+
argv: ["capability", "tts", "convert", "--text", "hello", "--json"],
1120+
});
1121+
1122+
expect(mocks.resolveCommandSecretRefsViaGateway).toHaveBeenCalledWith({
1123+
config: sourceConfig,
1124+
commandName: "infer tts convert",
1125+
targetIds: new Set(["messages.tts.providers.*.apiKey"]),
1126+
mode: "enforce_resolved",
1127+
});
1128+
expect(mocks.resolveExplicitTtsOverrides).toHaveBeenCalledWith(
1129+
expect.objectContaining({ cfg: resolvedConfig }),
1130+
);
1131+
expect(mocks.textToSpeech).toHaveBeenCalledWith(
1132+
expect.objectContaining({ cfg: resolvedConfig }),
1133+
);
1134+
});
1135+
10601136
it("disables TTS fallback when explicit provider or voice/model selection is requested", async () => {
10611137
await runRegisteredCli({
10621138
register: registerCapabilityCli as (program: Command) => void,

src/cli/capability-cli.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ import {
7979
runWebSearch,
8080
} from "../web-search/runtime.js";
8181
import { runCommandWithRuntime } from "./cli-utils.js";
82+
import { resolveCommandSecretRefsViaGateway } from "./command-secret-gateway.js";
83+
import { getTtsCommandSecretTargetIds } from "./command-secret-targets.js";
8284
import { createDefaultDeps } from "./deps.js";
8385
import { removeCommandByName } from "./program/command-tree.js";
8486
import { collectOption } from "./program/helpers.js";
@@ -1111,7 +1113,12 @@ async function runTtsConvert(params: {
11111113
} satisfies CapabilityEnvelope;
11121114
}
11131115

1114-
const cfg = loadConfig();
1116+
const { resolvedConfig: cfg } = await resolveCommandSecretRefsViaGateway({
1117+
config: loadConfig(),
1118+
commandName: "infer tts convert",
1119+
targetIds: getTtsCommandSecretTargetIds(),
1120+
mode: "enforce_resolved",
1121+
});
11151122
const overrides = resolveExplicitTtsOverrides({
11161123
cfg,
11171124
provider: params.provider,

src/cli/command-secret-resolution.coverage.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { readCommandSource } from "./command-source.test-helpers.js";
44

55
const SECRET_TARGET_CALLSITES = [
66
bundledPluginFile("memory-core", "src/cli.runtime.ts"),
7+
"src/cli/capability-cli.ts",
78
"src/cli/qr-cli.ts",
89
"src/agents/agent-runtime-config.ts",
910
"src/commands/agent.ts",

src/cli/command-secret-targets.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ import {
5858
getQrRemoteCommandSecretTargetIds,
5959
getScopedChannelsCommandSecretTargets,
6060
getSecurityAuditCommandSecretTargetIds,
61+
getTtsCommandSecretTargetIds,
6162
} from "./command-secret-targets.js";
6263

6364
describe("command secret target ids", () => {
@@ -73,6 +74,11 @@ describe("command secret target ids", () => {
7374
expect(ids.has("channels.discord.token")).toBe(false);
7475
});
7576

77+
it("keeps static TTS targets out of the registry path", () => {
78+
const ids = getTtsCommandSecretTargetIds();
79+
expect(ids).toEqual(new Set(["messages.tts.providers.*.apiKey"]));
80+
});
81+
7682
it("includes memorySearch remote targets for agent runtime commands", () => {
7783
const ids = getAgentRuntimeCommandSecretTargetIds();
7884
expect(ids.has("agents.defaults.memorySearch.remote.apiKey")).toBe(true);

src/cli/command-secret-targets.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,13 @@ const STATIC_MODEL_TARGET_IDS = [
2323
"models.providers.*.request.tls.key",
2424
"models.providers.*.request.tls.passphrase",
2525
] as const;
26+
const STATIC_TTS_TARGET_IDS = ["messages.tts.providers.*.apiKey"] as const;
2627
const STATIC_AGENT_RUNTIME_BASE_TARGET_IDS = [
2728
...STATIC_MODEL_TARGET_IDS,
2829
"agents.defaults.memorySearch.remote.apiKey",
2930
"agents.list[].memorySearch.remote.apiKey",
3031
"agents.list[].tts.providers.*.apiKey",
31-
"messages.tts.providers.*.apiKey",
32+
...STATIC_TTS_TARGET_IDS,
3233
"skills.entries.*.apiKey",
3334
"tools.web.search.apiKey",
3435
] as const;
@@ -221,6 +222,10 @@ export function getModelsCommandSecretTargetIds(): Set<string> {
221222
return toTargetIdSet(STATIC_MODEL_TARGET_IDS);
222223
}
223224

225+
export function getTtsCommandSecretTargetIds(): Set<string> {
226+
return toTargetIdSet(STATIC_TTS_TARGET_IDS);
227+
}
228+
224229
export function getAgentRuntimeCommandSecretTargetIds(params?: {
225230
includeChannelTargets?: boolean;
226231
}): Set<string> {

0 commit comments

Comments
 (0)