Skip to content

Commit 8d1e130

Browse files
fix(wrangler): vectorize list and list-metadata-index should output valid json (#12807)
Co-authored-by: emily-shen <[email protected]> Co-authored-by: emily-shen <[email protected]>
1 parent 01f252d commit 8d1e130

10 files changed

Lines changed: 298 additions & 45 deletions

File tree

.changeset/quiet-foxes-grow.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
"wrangler": patch
3+
---
4+
5+
fix: `vectorize` commands now output valid json
6+
7+
This fixes:
8+
9+
- `wrangler vectorize create`
10+
- `wrangler vectorize info`
11+
- `wrangler vectorize insert`
12+
- `wrangler vectorize upsert`
13+
- `wrangler vectorize list`
14+
- `wrangler vectorize list-vectors`
15+
- `wrangler vectorize list-metadata-index`
16+
17+
Also, `wrangler vectorize create --json` now also includes the `created_at`, `modified_on` and `description` fields.

packages/wrangler/src/__tests__/vectorize/vectorize.test.ts

Lines changed: 153 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -369,14 +369,108 @@ describe("vectorize commands", () => {
369369
expect(std.warn).toMatchInlineSnapshot(`
370370
"▲ [WARNING] 
371371
372-
You haven't created any indexes on this account.
372+
You haven't created any indexes on this account.
373373
374-
Use 'wrangler vectorize create <name>' to create one, or visit
375-
[4mhttps://developers.cloudflare.com/vectorize/[0m to get started.
374+
Use 'wrangler vectorize create <name>' to create one, or visit
375+
[4mhttps://developers.cloudflare.com/vectorize/[0m to get started.
376376
377377
378-
"
378+
"
379+
`);
380+
});
381+
382+
it("should return empty array JSON when there are no vectorize indexes with --json flag", async () => {
383+
mockVectorizeV2RequestError();
384+
await runWrangler("vectorize list --json");
385+
expect(JSON.parse(std.out)).toMatchInlineSnapshot(`[]`);
386+
expect(std.warn).toBe("");
387+
expect(std.err).toBe("");
388+
});
389+
390+
it("should handle listing vectorize indexes with valid JSON output", async () => {
391+
mockVectorizeV2Request();
392+
await runWrangler("vectorize list --json");
393+
expect(JSON.parse(std.out)).toMatchInlineSnapshot(`
394+
[
395+
{
396+
"config": {
397+
"dimensions": 1536,
398+
"metric": "euclidean",
399+
},
400+
"created_on": "2024-07-11T13:02:18.00268Z",
401+
"description": "test-desc",
402+
"modified_on": "2024-07-11T13:02:18.00268Z",
403+
"name": "test-index",
404+
},
405+
{
406+
"config": {
407+
"dimensions": 32,
408+
"metric": "dot-product",
409+
},
410+
"created_on": "2024-07-11T13:02:18.00268Z",
411+
"description": "another-desc",
412+
"modified_on": "2024-07-11T13:02:18.00268Z",
413+
"name": "another-index",
414+
},
415+
]
416+
`);
417+
expect(std.warn).toBe("");
418+
expect(std.err).toBe("");
419+
});
420+
421+
it("should handle creating a vectorize index with valid JSON output", async () => {
422+
mockVectorizeV2Request();
423+
await runWrangler(
424+
"vectorize create test-index --dimensions=1536 --metric=euclidean --json"
425+
);
426+
expect(JSON.parse(std.out)).toMatchInlineSnapshot(`
427+
{
428+
"config": {
429+
"dimensions": 1536,
430+
"metric": "euclidean",
431+
},
432+
"created_on": "2024-07-11T13:02:18.00268Z",
433+
"description": "test-desc",
434+
"modified_on": "2024-07-11T13:02:18.00268Z",
435+
"name": "test-index",
436+
}
437+
`);
438+
expect(std.warn).toBe("");
439+
expect(std.err).toBe("");
440+
});
441+
442+
it("should handle get on a vectorize index with valid JSON output", async () => {
443+
mockVectorizeV2Request();
444+
await runWrangler("vectorize get test-index --json");
445+
expect(JSON.parse(std.out)).toMatchInlineSnapshot(`
446+
{
447+
"config": {
448+
"dimensions": 1536,
449+
"metric": "euclidean",
450+
},
451+
"created_on": "2024-07-11T13:02:18.00268Z",
452+
"description": "test-desc",
453+
"modified_on": "2024-07-11T13:02:18.00268Z",
454+
"name": "test-index",
455+
}
379456
`);
457+
expect(std.warn).toBe("");
458+
expect(std.err).toBe("");
459+
});
460+
461+
it("should handle info on a vectorize index with valid JSON output", async () => {
462+
mockVectorizeV2Request();
463+
await runWrangler("vectorize info test-index --json");
464+
expect(JSON.parse(std.out)).toMatchInlineSnapshot(`
465+
{
466+
"dimensions": 1024,
467+
"processedUpToDatetime": "2024-07-19T13:11:44.064Z",
468+
"processedUpToMutation": "7f11d6e5-d126-4f76-936e-fbfec079e0be",
469+
"vectorCount": 1000,
470+
}
471+
`);
472+
expect(std.warn).toBe("");
473+
expect(std.err).toBe("");
380474
});
381475

382476
it("should handle a get on a vectorize V1 index", async () => {
@@ -869,14 +963,45 @@ describe("vectorize commands", () => {
869963
expect(std.warn).toMatchInlineSnapshot(`
870964
"▲ [WARNING] 
871965
872-
You haven't created any metadata indexes on this account.
966+
You haven't created any metadata indexes on this account.
873967
874-
Use 'wrangler vectorize create-metadata-index <name>' to create one, or visit
875-
[4mhttps://developers.cloudflare.com/vectorize/[0m to get started.
968+
Use 'wrangler vectorize create-metadata-index <name>' to create one, or visit
969+
[4mhttps://developers.cloudflare.com/vectorize/[0m to get started.
876970
877971
878-
"
972+
"
973+
`);
974+
});
975+
976+
it("should return empty array JSON when list metadata indexes returns empty with --json flag", async () => {
977+
mockVectorizeV2RequestError();
978+
await runWrangler("vectorize list-metadata-index test-index --json");
979+
expect(JSON.parse(std.out)).toMatchInlineSnapshot(`[]`);
980+
expect(std.warn).toBe("");
981+
expect(std.err).toBe("");
982+
});
983+
984+
it("should handle list-metadata-index with valid JSON output", async () => {
985+
mockVectorizeV2Request();
986+
await runWrangler("vectorize list-metadata-index test-index --json");
987+
expect(JSON.parse(std.out)).toMatchInlineSnapshot(`
988+
[
989+
{
990+
"indexType": "string",
991+
"propertyName": "string-prop",
992+
},
993+
{
994+
"indexType": "number",
995+
"propertyName": "num-prop",
996+
},
997+
{
998+
"indexType": "boolean",
999+
"propertyName": "bool-prop",
1000+
},
1001+
]
8791002
`);
1003+
expect(std.warn).toBe("");
1004+
expect(std.err).toBe("");
8801005
});
8811006

8821007
it("should handle delete metadata index", async () => {
@@ -1025,6 +1150,8 @@ describe("vectorize commands", () => {
10251150
],
10261151
}
10271152
`);
1153+
expect(std.warn).toBe("");
1154+
expect(std.err).toBe("");
10281155
});
10291156

10301157
it("should warn when list-vectors returns no vectors", async () => {
@@ -1040,8 +1167,25 @@ describe("vectorize commands", () => {
10401167
expect(std.warn).toMatchInlineSnapshot(`
10411168
"▲ [WARNING] No vectors found in this index.
10421169
1043-
"
1170+
"
1171+
`);
1172+
});
1173+
1174+
it("should return valid JSON when list-vectors returns no vectors with --json flag", async () => {
1175+
mockVectorizeV2RequestError();
1176+
await runWrangler("vectorize list-vectors test-index --json");
1177+
expect(JSON.parse(std.out)).toMatchInlineSnapshot(`
1178+
{
1179+
"count": 0,
1180+
"cursorExpirationTimestamp": null,
1181+
"isTruncated": false,
1182+
"nextCursor": null,
1183+
"totalCount": 0,
1184+
"vectors": [],
1185+
}
10441186
`);
1187+
expect(std.warn).toBe("");
1188+
expect(std.err).toBe("");
10451189
});
10461190
});
10471191

packages/wrangler/src/__tests__/vectorize/vectorize.upsert.test.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,78 @@ describe("dataset upsert", () => {
239239
`);
240240
});
241241

242+
it("should output valid JSON for insert with --json flag", async () => {
243+
writeFileSync(
244+
"vectors.ndjson",
245+
testVectors.map((v) => JSON.stringify(v)).join(`\n`)
246+
);
247+
248+
const mutationId = crypto.randomUUID();
249+
250+
msw.use(
251+
http.post("*/vectorize/v2/indexes/:indexName/insert", async () => {
252+
return HttpResponse.json(
253+
{
254+
success: true,
255+
errors: [],
256+
messages: [],
257+
result: { mutationId: mutationId },
258+
},
259+
{ status: 200 }
260+
);
261+
})
262+
);
263+
264+
await runWrangler(
265+
`vectorize insert my-index --file vectors.ndjson --batch-size 10 --json`
266+
);
267+
268+
expect(JSON.parse(std.out)).toMatchInlineSnapshot(`
269+
{
270+
"count": 5,
271+
"index": "my-index",
272+
}
273+
`);
274+
expect(std.warn).toBe("");
275+
expect(std.err).toBe("");
276+
});
277+
278+
it("should output valid JSON for upsert with --json flag", async () => {
279+
writeFileSync(
280+
"vectors.ndjson",
281+
testVectors.map((v) => JSON.stringify(v)).join(`\n`)
282+
);
283+
284+
const mutationId = crypto.randomUUID();
285+
286+
msw.use(
287+
http.post("*/vectorize/v2/indexes/:indexName/upsert", async () => {
288+
return HttpResponse.json(
289+
{
290+
success: true,
291+
errors: [],
292+
messages: [],
293+
result: { mutationId: mutationId },
294+
},
295+
{ status: 200 }
296+
);
297+
})
298+
);
299+
300+
await runWrangler(
301+
`vectorize upsert my-index --file vectors.ndjson --batch-size 10 --json`
302+
);
303+
304+
expect(JSON.parse(std.out)).toMatchInlineSnapshot(`
305+
{
306+
"count": 5,
307+
"index": "my-index",
308+
}
309+
`);
310+
expect(std.warn).toBe("");
311+
expect(std.err).toBe("");
312+
});
313+
242314
it("should reject an invalid file param", async () => {
243315
await expect(
244316
runWrangler("vectorize upsert my-index --file invalid_vectors.ndjson")

packages/wrangler/src/vectorize/create.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,11 @@ export const vectorizeCreateCommand = createCommand({
7171
let indexConfig;
7272
if (args.preset) {
7373
indexConfig = { preset: args.preset };
74-
logger.log(
75-
`Configuring index based for the embedding model ${args.preset}.`
76-
);
74+
if (!args.json) {
75+
logger.log(
76+
`Configuring index based for the embedding model ${args.preset}.`
77+
);
78+
}
7779
} else if (args.dimensions && args.metric) {
7880
// We let the server validate the supported (maximum) dimensions so that we
7981
// don't have to keep wrangler in sync with server-side changes
@@ -87,7 +89,7 @@ export const vectorizeCreateCommand = createCommand({
8789
);
8890
}
8991

90-
if (args.deprecatedV1) {
92+
if (args.deprecatedV1 && !args.json) {
9193
logger.warn(
9294
"Creation of legacy Vectorize indexes will be blocked by December 2024"
9395
);
@@ -99,7 +101,9 @@ export const vectorizeCreateCommand = createCommand({
99101
config: indexConfig,
100102
};
101103

102-
logger.log(`🚧 Creating index: '${args.name}'`);
104+
if (!args.json) {
105+
logger.log(`🚧 Creating index: '${args.name}'`);
106+
}
103107
const indexResult = await createIndex(config, index, args.deprecatedV1);
104108

105109
let bindingName: string;
@@ -110,7 +114,7 @@ export const vectorizeCreateCommand = createCommand({
110114
}
111115

112116
if (args.json) {
113-
logger.log(JSON.stringify(index, null, 2));
117+
logger.log(JSON.stringify(indexResult, null, 2));
114118
return;
115119
}
116120

packages/wrangler/src/vectorize/info.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ export const vectorizeInfoCommand = createCommand({
2525
},
2626
positionalArgs: ["name"],
2727
async handler(args, { config }) {
28-
logger.log(`📋 Fetching index info...`);
28+
if (!args.json) {
29+
logger.log(`📋 Fetching index info...`);
30+
}
2931
const info = await indexInfo(config, args.name);
3032

3133
if (args.json) {

packages/wrangler/src/vectorize/insert.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -90,21 +90,27 @@ export const vectorizeInsertCommand = createCommand({
9090
})
9191
);
9292
if (args.deprecatedV1) {
93-
logger.log(`✨ Uploading vector batch (${batch.length} vectors)`);
93+
if (!args.json) {
94+
logger.log(`✨ Uploading vector batch (${batch.length} vectors)`);
95+
}
9496
const idxPart = await insertIntoIndexV1(config, args.name, formData);
9597
vectorInsertCount += idxPart.count;
9698
} else {
9799
const mutation = await insertIntoIndex(config, args.name, formData);
98100
vectorInsertCount += batch.length;
99-
logger.log(
100-
`✨ Enqueued ${batch.length} vectors into index '${args.name}' for insertion. Mutation changeset identifier: ${mutation.mutationId}`
101-
);
101+
if (!args.json) {
102+
logger.log(
103+
`✨ Enqueued ${batch.length} vectors into index '${args.name}' for insertion. Mutation changeset identifier: ${mutation.mutationId}`
104+
);
105+
}
102106
}
103107

104108
if (vectorInsertCount > VECTORIZE_MAX_UPSERT_VECTOR_RECORDS) {
105-
logger.warn(
106-
`🚧 While Vectorize is in beta, we've limited uploads to 100k vectors per run. You may run this again with another batch to upload further`
107-
);
109+
if (!args.json) {
110+
logger.warn(
111+
`🚧 While Vectorize is in beta, we've limited uploads to 100k vectors per run. You may run this again with another batch to upload further`
112+
);
113+
}
108114
break;
109115
}
110116
}

0 commit comments

Comments
 (0)