Skip to content

Commit 7b4ae2d

Browse files
committed
fixes
1 parent fb2733f commit 7b4ae2d

7 files changed

Lines changed: 174 additions & 11 deletions

File tree

.github/scripts/provider-type-update-workflow.test.mjs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,13 @@ test("protects AGENTS.md in planning, implementation, and path policy", () => {
6060
assert.match(workflow, /Do not edit `plan\.md`, `AGENTS\.md`/);
6161
assert.match(workflow, /':\(glob\)\*\*\/AGENTS\.md'/);
6262
});
63+
64+
test("limits transform capture to providers with configured credentials", () => {
65+
assert.match(
66+
workflow,
67+
/capture_providers=\(\).*ANTHROPIC_API_KEY.*AWS_BEARER_TOKEN_BEDROCK.*GOOGLE_API_KEY.*OPENAI_API_KEY/s
68+
);
69+
assert.match(workflow, /CAPTURE_PROVIDERS="\$capture_provider_list"/);
70+
assert.doesNotMatch(workflow, /GOOGLE_APPLICATION_CREDENTIALS:/);
71+
assert.doesNotMatch(workflow, /VERTEX_PROJECT:/);
72+
});

.github/workflows/update-provider-types.yml

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -891,7 +891,33 @@ jobs:
891891
CAPTURE_CASES: ${{ steps.capture_cases.outputs.cases }}
892892
GOOGLE_API_KEY: ${{ secrets.LINGUA_CAPTURE_GOOGLE_API_KEY }}
893893
OPENAI_API_KEY: ${{ secrets.LINGUA_CAPTURE_OPENAI_API_KEY }}
894-
run: make capture-transforms CASES="$CAPTURE_CASES" FORCE=1
894+
run: |
895+
set -euo pipefail
896+
capture_providers=()
897+
if [ -n "${ANTHROPIC_API_KEY:-}" ]; then
898+
capture_providers+=(anthropic)
899+
fi
900+
if [ -n "${AWS_BEARER_TOKEN_BEDROCK:-}" ]; then
901+
capture_providers+=(bedrock)
902+
fi
903+
if [ -n "${GOOGLE_API_KEY:-}" ]; then
904+
capture_providers+=(google)
905+
fi
906+
if [ -n "${OPENAI_API_KEY:-}" ]; then
907+
capture_providers+=(chat-completions responses)
908+
fi
909+
910+
if [ "${#capture_providers[@]}" -eq 0 ]; then
911+
echo "No transform capture credentials are configured; skipping transform capture."
912+
exit 0
913+
fi
914+
915+
capture_provider_list=$(IFS=,; echo "${capture_providers[*]}")
916+
echo "Capturing transforms with credentialed providers: $capture_provider_list"
917+
make capture-transforms \
918+
CASES="$CAPTURE_CASES" \
919+
CAPTURE_PROVIDERS="$capture_provider_list" \
920+
FORCE=1
895921
896922
- name: Verify payload fixture sync
897923
id: payload_sync

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ capture: lingua-wasm ## Capture payloads (snapshots + transforms + vitest snapsh
102102
cd payloads && pnpm capture $(if $(FILTER),--filter $(FILTER)) $(if $(CASES),--cases $(CASES)) $(if $(FORCE),--force)
103103

104104
capture-transforms: lingua-wasm ## Re-capture only transforms (e.g. make capture-transforms CASES=streamParam,toolsParam FORCE=1)
105-
cd payloads && pnpm tsx scripts/transforms/capture-transforms.ts $(if $(FILTER),$(FILTER)) $(if $(CASES),--cases $(CASES)) $(if $(PAIR),--pair $(PAIR)) $(if $(FORCE),--force)
105+
cd payloads && pnpm tsx scripts/transforms/capture-transforms.ts $(if $(FILTER),$(FILTER)) $(if $(CASES),--cases $(CASES)) $(if $(PAIR),--pair $(PAIR)) $(if $(CAPTURE_PROVIDERS),--capture-providers $(CAPTURE_PROVIDERS)) $(if $(FORCE),--force)
106106

107107
update-snapshots: lingua-wasm ## Update vitest snapshots without recapturing
108108
cd payloads && pnpm vitest run scripts/transforms -u

payloads/scripts/capture.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { describe, expect, test } from "vitest";
2+
import { findUnmatchedRequestedCases } from "./capture";
3+
4+
describe("requested capture case validation", () => {
5+
test("rejects every requested case omitted by provider selection", () => {
6+
expect(
7+
findUnmatchedRequestedCases(
8+
["supportedCase", "incompatibleCase", "misspelledCase"],
9+
["supportedCase"]
10+
)
11+
).toEqual(["incompatibleCase", "misspelledCase"]);
12+
});
13+
14+
test("accepts requested cases matched by at least one selected provider", () => {
15+
expect(
16+
findUnmatchedRequestedCases(
17+
["sharedCase", "providerCase"],
18+
["sharedCase", "sharedCase", "providerCase"]
19+
)
20+
).toEqual([]);
21+
});
22+
23+
test("does not require explicit cases for unfiltered captures", () => {
24+
expect(findUnmatchedRequestedCases(undefined, [])).toEqual([]);
25+
});
26+
});

payloads/scripts/capture.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,20 @@ function getAllCases(options: CaptureOptions): CaseToRun[] {
149149
return cases;
150150
}
151151

152+
export function findUnmatchedRequestedCases(
153+
requestedCases: string[] | undefined,
154+
matchedCaseNames: Iterable<string>
155+
): string[] {
156+
if (!requestedCases) {
157+
return [];
158+
}
159+
160+
const matched = new Set(matchedCaseNames);
161+
return [...new Set(requestedCases)].filter(
162+
(caseName) => !matched.has(caseName)
163+
);
164+
}
165+
152166
async function captureProviderSnapshots(
153167
cases: CaseToRun[],
154168
options: CaptureOptions
@@ -269,9 +283,13 @@ async function main() {
269283
}
270284
return;
271285
}
272-
if (options.cases && allCases.length === 0) {
286+
const unmatchedRequestedCases = findUnmatchedRequestedCases(
287+
options.cases,
288+
allCases.map((case_) => case_.caseName)
289+
);
290+
if (unmatchedRequestedCases.length > 0) {
273291
throw new Error(
274-
`None of the requested cases matched the selected providers: ${options.cases.join(",")}`
292+
`Requested cases did not match the selected providers: ${unmatchedRequestedCases.join(",")}`
275293
);
276294
}
277295

payloads/scripts/transforms/capture-transforms.test.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { describe, expect, test } from "vitest";
22
import {
3+
isCaptureProviderSelected,
34
parseCaptureTransformArgs,
45
selectRequestedCases,
56
} from "./capture-transforms";
@@ -11,6 +12,7 @@ describe("capture transform arguments", () => {
1112
force: true,
1213
pair: undefined,
1314
cases: undefined,
15+
captureProviders: undefined,
1416
});
1517
});
1618

@@ -27,6 +29,7 @@ describe("capture transform arguments", () => {
2729
force: true,
2830
pair: { source: "anthropic", target: "responses" },
2931
cases: undefined,
32+
captureProviders: undefined,
3033
});
3134
});
3235

@@ -42,8 +45,55 @@ describe("capture transform arguments", () => {
4245
force: true,
4346
pair: undefined,
4447
cases: ["streamParam", "streamOptionsParam"],
48+
captureProviders: undefined,
4549
});
4650
});
51+
52+
test("parses credentialed capture providers without treating them as a filter", () => {
53+
expect(
54+
parseCaptureTransformArgs([
55+
"--capture-providers",
56+
"anthropic,chat-completions,responses,google",
57+
"--cases",
58+
"streamParam",
59+
"--force",
60+
])
61+
).toEqual({
62+
filter: undefined,
63+
force: true,
64+
pair: undefined,
65+
cases: ["streamParam"],
66+
captureProviders: [
67+
"anthropic",
68+
"chat-completions",
69+
"responses",
70+
"google",
71+
],
72+
});
73+
});
74+
});
75+
76+
describe("capture provider selection", () => {
77+
test("keeps pairs whose actual capture provider is credentialed", () => {
78+
expect(
79+
isCaptureProviderSelected({ target: "anthropic" }, [
80+
"anthropic",
81+
"google",
82+
])
83+
).toBe(true);
84+
expect(
85+
isCaptureProviderSelected(
86+
{ target: "anthropic", captureProvider: "baseten" },
87+
["anthropic", "google"]
88+
)
89+
).toBe(false);
90+
});
91+
92+
test("keeps every pair when no credential filter is provided", () => {
93+
expect(isCaptureProviderSelected({ target: "vertex-anthropic" })).toBe(
94+
true
95+
);
96+
});
4797
});
4898

4999
describe("capture transform case selection", () => {

payloads/scripts/transforms/capture-transforms.ts

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
getResponsePath,
2222
getStreamingResponsePath,
2323
type SourceFormat,
24+
type TransformPair,
2425
} from "./helpers";
2526

2627
const GOOGLE_API_BASE = "https://generativelanguage.googleapis.com/v1beta";
@@ -218,7 +219,8 @@ export async function captureTransforms(
218219
filter?: string,
219220
force?: boolean,
220221
requestedPair?: { source: string; target: string },
221-
requestedCases?: string[]
222+
requestedCases?: string[],
223+
requestedCaptureProviders?: string[]
222224
): Promise<{ captured: number; skipped: number; failed: number }> {
223225
mkdirSync(TRANSFORMS_DIR, { recursive: true });
224226

@@ -229,6 +231,9 @@ export async function captureTransforms(
229231
const nonStreamingTasks: (() => Promise<void>)[] = [];
230232

231233
for (const p of TRANSFORM_PAIRS) {
234+
if (!isCaptureProviderSelected(p, requestedCaptureProviders)) {
235+
continue;
236+
}
232237
if (
233238
requestedPair &&
234239
(p.source !== requestedPair.source || p.target !== requestedPair.target)
@@ -293,6 +298,9 @@ export async function captureTransforms(
293298
const streamingTasks: (() => Promise<void>)[] = [];
294299

295300
for (const streamingPair of STREAMING_PAIRS) {
301+
if (!isCaptureProviderSelected(streamingPair, requestedCaptureProviders)) {
302+
continue;
303+
}
296304
if (
297305
requestedPair &&
298306
(streamingPair.source !== requestedPair.source ||
@@ -391,6 +399,18 @@ export async function captureTransforms(
391399
return { captured, skipped, failed };
392400
}
393401

402+
export function isCaptureProviderSelected(
403+
pair: Pick<TransformPair, "target" | "captureProvider">,
404+
requestedCaptureProviders?: string[]
405+
): boolean {
406+
if (!requestedCaptureProviders) {
407+
return true;
408+
}
409+
410+
const captureProvider = pair.captureProvider ?? pair.target;
411+
return requestedCaptureProviders.includes(captureProvider);
412+
}
413+
394414
export function selectRequestedCases(
395415
caseNames: string[],
396416
requestedCases?: string[]
@@ -408,6 +428,7 @@ export function parseCaptureTransformArgs(args: string[]): {
408428
force: boolean;
409429
pair: { source: string; target: string } | undefined;
410430
cases: string[] | undefined;
431+
captureProviders: string[] | undefined;
411432
} {
412433
const force = args.includes("--force");
413434
const pairIdx = args.indexOf("--pair");
@@ -420,22 +441,34 @@ export function parseCaptureTransformArgs(args: string[]): {
420441
const cases = casesArg
421442
? casesArg.split(",").filter((caseName) => caseName.length > 0)
422443
: undefined;
444+
const captureProvidersIdx = args.indexOf("--capture-providers");
445+
const captureProvidersArg =
446+
captureProvidersIdx !== -1 ? args[captureProvidersIdx + 1] : undefined;
447+
const captureProviders = captureProvidersArg
448+
? captureProvidersArg.split(",").filter((provider) => provider.length > 0)
449+
: undefined;
423450
const filter = args.find(
424451
(arg, index) =>
425452
!arg.startsWith("--") &&
426453
(pairIdx === -1 || index !== pairIdx + 1) &&
427-
(casesIdx === -1 || index !== casesIdx + 1)
454+
(casesIdx === -1 || index !== casesIdx + 1) &&
455+
(captureProvidersIdx === -1 || index !== captureProvidersIdx + 1)
428456
);
429457

430-
return { filter, force, pair, cases };
458+
return { filter, force, pair, cases, captureProviders };
431459
}
432460

433461
async function main() {
434-
const { filter, force, pair, cases } = parseCaptureTransformArgs(
435-
process.argv.slice(2)
462+
const { filter, force, pair, cases, captureProviders } =
463+
parseCaptureTransformArgs(process.argv.slice(2));
464+
465+
const { failed } = await captureTransforms(
466+
filter,
467+
force,
468+
pair,
469+
cases,
470+
captureProviders
436471
);
437-
438-
const { failed } = await captureTransforms(filter, force, pair, cases);
439472
process.exit(failed > 0 ? 1 : 0);
440473
}
441474

0 commit comments

Comments
 (0)