Skip to content

Commit 76a6701

Browse files
authored
[Vectorize] feat: Add Wrangler command for Vectorize list-vectors operation (#10341)
1 parent eb32a3a commit 76a6701

File tree

6 files changed

+368
-0
lines changed

6 files changed

+368
-0
lines changed

.changeset/bumpy-wolves-tell.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"wrangler": minor
3+
---
4+
5+
feat: Add Wrangler command for Vectorize list-vectors operation

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

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ describe("vectorize help", () => {
2828
wrangler vectorize delete <name> Delete a Vectorize index
2929
wrangler vectorize get <name> Get a Vectorize index by name
3030
wrangler vectorize list List your Vectorize indexes
31+
wrangler vectorize list-vectors <name> List vector identifiers in a Vectorize index
3132
wrangler vectorize query <name> Query a Vectorize index
3233
wrangler vectorize insert <name> Insert vectors into a Vectorize index
3334
wrangler vectorize upsert <name> Upsert vectors into a Vectorize index
@@ -69,6 +70,7 @@ describe("vectorize help", () => {
6970
wrangler vectorize delete <name> Delete a Vectorize index
7071
wrangler vectorize get <name> Get a Vectorize index by name
7172
wrangler vectorize list List your Vectorize indexes
73+
wrangler vectorize list-vectors <name> List vector identifiers in a Vectorize index
7274
wrangler vectorize query <name> Query a Vectorize index
7375
wrangler vectorize insert <name> Insert vectors into a Vectorize index
7476
wrangler vectorize upsert <name> Upsert vectors into a Vectorize index
@@ -677,6 +679,146 @@ describe("vectorize commands", () => {
677679
✅ Successfully enqueued metadata index deletion request. Mutation changeset identifier: xxxxxx-xxxx-xxxx-xxxx-xxxxxx."
678680
`);
679681
});
682+
683+
it("should show help when the list-vectors command is passed without an index", async () => {
684+
await expect(() => runWrangler("vectorize list-vectors")).rejects.toThrow(
685+
"Not enough non-option arguments: got 0, need at least 1"
686+
);
687+
688+
expect(std.err).toMatchInlineSnapshot(`
689+
"X [ERROR] Not enough non-option arguments: got 0, need at least 1
690+
691+
"
692+
`);
693+
expect(std.out).toMatchInlineSnapshot(`
694+
"
695+
wrangler vectorize list-vectors <name>
696+
697+
List vector identifiers in a Vectorize index
698+
699+
POSITIONALS
700+
name The name of the Vectorize index [string] [required]
701+
702+
GLOBAL FLAGS
703+
-c, --config Path to Wrangler configuration file [string]
704+
--cwd Run as if Wrangler was started in the specified directory instead of the current working directory [string]
705+
-e, --env Environment to use for operations, and for selecting .env and .dev.vars files [string]
706+
--env-file Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files [array]
707+
-h, --help Show help [boolean]
708+
-v, --version Show version number [boolean]
709+
710+
OPTIONS
711+
--count Maximum number of vectors to return (1-1000) [number]
712+
--cursor Cursor for pagination to get the next page of results [string]
713+
--json Return output as clean JSON [boolean] [default: false]
714+
715+
EXAMPLES
716+
wrangler vectorize list-vectors my-index List vector identifiers in the index 'my-index'
717+
wrangler vectorize list-vectors my-index --count 50 List up to 50 vector identifiers
718+
wrangler vectorize list-vectors my-index --cursor abc123 Continue listing from a specific cursor position"
719+
`);
720+
});
721+
722+
it("should handle list-vectors on a vectorize index", async () => {
723+
mockVectorizeV2Request();
724+
await runWrangler("vectorize list-vectors test-index");
725+
expect(std.out).toMatchInlineSnapshot(`
726+
"📋 Listing vectors in index 'test-index'...
727+
┌─┬─┐
728+
│ # │ Vector ID │
729+
├─┼─┤
730+
│ 1 │ vector-1 │
731+
├─┼─┤
732+
│ 2 │ vector-2 │
733+
├─┼─┤
734+
│ 3 │ vector-3 │
735+
└─┴─┘
736+
737+
Showing 3 of 5 total vectors
738+
739+
💡 To get the next page, run:
740+
wrangler vectorize list-vectors test-index --cursor next-page-cursor"
741+
`);
742+
});
743+
744+
it("should handle list-vectors with custom count parameter", async () => {
745+
mockVectorizeV2Request();
746+
await runWrangler("vectorize list-vectors test-index --count 2");
747+
expect(std.out).toMatchInlineSnapshot(`
748+
"📋 Listing vectors in index 'test-index'...
749+
┌─┬─┐
750+
│ # │ Vector ID │
751+
├─┼─┤
752+
│ 1 │ vector-1 │
753+
├─┼─┤
754+
│ 2 │ vector-2 │
755+
└─┴─┘
756+
757+
Showing 2 of 5 total vectors
758+
759+
💡 To get the next page, run:
760+
wrangler vectorize list-vectors test-index --cursor next-page-cursor"
761+
`);
762+
});
763+
764+
it("should handle list-vectors with cursor pagination", async () => {
765+
mockVectorizeV2Request();
766+
await runWrangler(
767+
"vectorize list-vectors test-index --cursor next-page-cursor"
768+
);
769+
expect(std.out).toMatchInlineSnapshot(`
770+
"📋 Listing vectors in index 'test-index'...
771+
┌─┬─┐
772+
│ # │ Vector ID │
773+
├─┼─┤
774+
│ 1 │ vector-4 │
775+
├─┼─┤
776+
│ 2 │ vector-5 │
777+
└─┴─┘
778+
779+
Showing 2 of 5 total vectors"
780+
`);
781+
});
782+
783+
it("should handle list-vectors with JSON output", async () => {
784+
mockVectorizeV2Request();
785+
await runWrangler("vectorize list-vectors test-index --json");
786+
expect(std.out).toMatchInlineSnapshot(`
787+
"📋 Listing vectors in index 'test-index'...
788+
{
789+
\\"count\\": 3,
790+
\\"totalCount\\": 5,
791+
\\"isTruncated\\": true,
792+
\\"nextCursor\\": \\"next-page-cursor\\",
793+
\\"cursorExpirationTimestamp\\": \\"2025-08-13T20:32:52.469144957+00:00\\",
794+
\\"vectors\\": [
795+
{
796+
\\"id\\": \\"vector-1\\"
797+
},
798+
{
799+
\\"id\\": \\"vector-2\\"
800+
},
801+
{
802+
\\"id\\": \\"vector-3\\"
803+
}
804+
]
805+
}"
806+
`);
807+
});
808+
809+
it("should warn when list-vectors returns no vectors", async () => {
810+
mockVectorizeV2RequestError();
811+
await runWrangler("vectorize list-vectors test-index");
812+
expect(std.out).toMatchInlineSnapshot(`
813+
"📋 Listing vectors in index 'test-index'..."
814+
`);
815+
816+
expect(std.warn).toMatchInlineSnapshot(`
817+
"▲ [WARNING] No vectors found in this index.
818+
819+
"
820+
`);
821+
});
680822
});
681823

682824
describe("vectorize query filter", () => {
@@ -1120,6 +1262,58 @@ function mockVectorizeV2Request() {
11201262
);
11211263
},
11221264
{ once: true }
1265+
),
1266+
http.get(
1267+
"*/accounts/:accountId/vectorize/v2/indexes/test-index/list",
1268+
({ request }) => {
1269+
const url = new URL(request.url);
1270+
const count = url.searchParams.get("count");
1271+
const cursor = url.searchParams.get("cursor");
1272+
1273+
// Mock pagination logic
1274+
if (cursor === "next-page-cursor") {
1275+
const vectors = [{ id: "vector-4" }, { id: "vector-5" }];
1276+
return HttpResponse.json(
1277+
createFetchResult(
1278+
{
1279+
count: vectors.length,
1280+
totalCount: 5,
1281+
isTruncated: false,
1282+
nextCursor: null,
1283+
cursorExpirationTimestamp: null,
1284+
vectors,
1285+
},
1286+
true
1287+
)
1288+
);
1289+
}
1290+
1291+
// Default first page response
1292+
const pageSize = count ? parseInt(count) : 3;
1293+
const mockVectors = [
1294+
{ id: "vector-1" },
1295+
{ id: "vector-2" },
1296+
{ id: "vector-3" },
1297+
];
1298+
1299+
const returnedVectors = mockVectors.slice(0, pageSize);
1300+
1301+
return HttpResponse.json(
1302+
createFetchResult(
1303+
{
1304+
count: returnedVectors.length,
1305+
totalCount: 5,
1306+
isTruncated: returnedVectors.length < 5,
1307+
nextCursor:
1308+
returnedVectors.length < 5 ? "next-page-cursor" : null,
1309+
cursorExpirationTimestamp: "2025-08-13T20:32:52.469144957+00:00",
1310+
vectors: returnedVectors,
1311+
},
1312+
true
1313+
)
1314+
);
1315+
},
1316+
{ once: true }
11231317
)
11241318
);
11251319
}
@@ -1168,6 +1362,25 @@ function mockVectorizeV2RequestError() {
11681362
);
11691363
},
11701364
{ once: true }
1365+
),
1366+
http.get(
1367+
"*/accounts/:accountId/vectorize/v2/indexes/test-index/list",
1368+
() => {
1369+
return HttpResponse.json(
1370+
createFetchResult(
1371+
{
1372+
count: 0,
1373+
totalCount: 0,
1374+
isTruncated: false,
1375+
nextCursor: null,
1376+
cursorExpirationTimestamp: null,
1377+
vectors: [],
1378+
},
1379+
true
1380+
)
1381+
);
1382+
},
1383+
{ once: true }
11711384
)
11721385
);
11731386
}

packages/wrangler/src/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,7 @@ import { vectorizeInfoCommand } from "./vectorize/info";
299299
import { vectorizeInsertCommand } from "./vectorize/insert";
300300
import { vectorizeListCommand } from "./vectorize/list";
301301
import { vectorizeListMetadataIndexCommand } from "./vectorize/listMetadataIndex";
302+
import { vectorizeListVectorsCommand } from "./vectorize/listVectors";
302303
import { vectorizeQueryCommand } from "./vectorize/query";
303304
import { vectorizeUpsertCommand } from "./vectorize/upsert";
304305
import { versionsNamespace } from "./versions";
@@ -1030,6 +1031,10 @@ export function createCLIParser(argv: string[]) {
10301031
},
10311032
{ command: "wrangler vectorize get", definition: vectorizeGetCommand },
10321033
{ command: "wrangler vectorize list", definition: vectorizeListCommand },
1034+
{
1035+
command: "wrangler vectorize list-vectors",
1036+
definition: vectorizeListVectorsCommand,
1037+
},
10331038
{ command: "wrangler vectorize query", definition: vectorizeQueryCommand },
10341039
{
10351040
command: "wrangler vectorize insert",

packages/wrangler/src/vectorize/client.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type {
66
VectorizeAsyncMutation,
77
VectorizeIndex,
88
VectorizeIndexDetails,
9+
VectorizeListVectorsResponse,
910
VectorizeMatches,
1011
VectorizeMetadataIndexList,
1112
VectorizeMetadataIndexProperty,
@@ -293,3 +294,31 @@ export async function deleteMetadataIndex(
293294
}
294295
);
295296
}
297+
298+
export async function listVectors(
299+
config: Config,
300+
indexName: string,
301+
options?: {
302+
count?: number;
303+
cursor?: string;
304+
}
305+
): Promise<VectorizeListVectorsResponse> {
306+
const accountId = await requireAuth(config);
307+
308+
const searchParams = new URLSearchParams();
309+
if (options?.count !== undefined) {
310+
searchParams.set("count", options.count.toString());
311+
}
312+
if (options?.cursor !== undefined) {
313+
searchParams.set("cursor", options.cursor);
314+
}
315+
316+
const queryString = searchParams.toString();
317+
const url = `/accounts/${accountId}/vectorize/v2/indexes/${indexName}/list${
318+
queryString ? `?${queryString}` : ""
319+
}`;
320+
321+
return await fetchResult(config, url, {
322+
method: "GET",
323+
});
324+
}

0 commit comments

Comments
 (0)