Skip to content

Commit c32f514

Browse files
[wrangler] feat: Support for Constellation AI (#3091)
* [wrangler] feat: Support for Constellation AI Added initial commands to manage Constallation AI projects/models from Wrangler. * test: disable colors on tables when testing --------- Co-authored-by: Pete Bacon Darwin <[email protected]>
1 parent 53a61f3 commit c32f514

22 files changed

+965
-5
lines changed

.changeset/tall-geckos-try.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+
Added initial commands for integrating with Constellation AI.
Lines changed: 371 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,371 @@
1+
import * as fs from "node:fs";
2+
import { rest } from "msw";
3+
import { endEventLoop } from "./helpers/end-event-loop";
4+
import { mockAccountId, mockApiToken } from "./helpers/mock-account-id";
5+
import { mockConsoleMethods } from "./helpers/mock-console";
6+
import { clearDialogs, mockConfirm } from "./helpers/mock-dialogs";
7+
import { useMockIsTTY } from "./helpers/mock-istty";
8+
import { createFetchResult, msw } from "./helpers/msw";
9+
import { runInTempDir } from "./helpers/run-in-tmp";
10+
import { runWrangler } from "./helpers/run-wrangler";
11+
12+
describe("constellation help", () => {
13+
const std = mockConsoleMethods();
14+
runInTempDir();
15+
16+
it("should show help when no argument is passed", async () => {
17+
await runWrangler("constellation");
18+
await endEventLoop();
19+
20+
expect(std.out).toMatchInlineSnapshot(`
21+
"wrangler constellation
22+
23+
🤖 Interact with Constellation AI models
24+
25+
Commands:
26+
wrangler constellation project Manage your projects
27+
wrangler constellation model Manage your models
28+
wrangler constellation catalog Check the curated model catalog
29+
wrangler constellation runtime Check the suported runtimes
30+
31+
Flags:
32+
-j, --experimental-json-config Experimental: Support wrangler.json [boolean]
33+
-c, --config Path to .toml configuration file [string]
34+
-e, --env Environment to use for operations and .env files [string]
35+
-h, --help Show help [boolean]
36+
-v, --version Show version number [boolean]"
37+
`);
38+
});
39+
40+
it("should show help when an invalid argument is passed", async () => {
41+
await expect(() => runWrangler("constellation asdf")).rejects.toThrow(
42+
"Unknown argument: asdf"
43+
);
44+
45+
expect(std.err).toMatchInlineSnapshot(`
46+
"X [ERROR] Unknown argument: asdf
47+
48+
"
49+
`);
50+
expect(std.out).toMatchInlineSnapshot(`
51+
"
52+
wrangler constellation
53+
54+
🤖 Interact with Constellation AI models
55+
56+
Commands:
57+
wrangler constellation project Manage your projects
58+
wrangler constellation model Manage your models
59+
wrangler constellation catalog Check the curated model catalog
60+
wrangler constellation runtime Check the suported runtimes
61+
62+
Flags:
63+
-j, --experimental-json-config Experimental: Support wrangler.json [boolean]
64+
-c, --config Path to .toml configuration file [string]
65+
-e, --env Environment to use for operations and .env files [string]
66+
-h, --help Show help [boolean]
67+
-v, --version Show version number [boolean]"
68+
`);
69+
});
70+
});
71+
72+
describe("constellation commands", () => {
73+
mockAccountId();
74+
mockApiToken();
75+
runInTempDir();
76+
const { setIsTTY } = useMockIsTTY();
77+
78+
const std = mockConsoleMethods();
79+
80+
beforeEach(() => {
81+
// @ts-expect-error we're using a very simple setTimeout mock here
82+
jest.spyOn(global, "setTimeout").mockImplementation((fn, _period) => {
83+
setImmediate(fn);
84+
});
85+
setIsTTY(true);
86+
});
87+
88+
afterEach(() => {
89+
clearDialogs();
90+
});
91+
92+
it("should handle project creation", async () => {
93+
mockConstellationRequest();
94+
await runWrangler("constellation project create new_project ONNX");
95+
expect(std.out).toMatchInlineSnapshot(`
96+
"--------------------
97+
🚧 Constellation AI is currently in open alpha and is not recommended for production data and traffic
98+
🚧 Please report any bugs to https://github.com/cloudflare/workers-sdk/issues/new/choose
99+
🚧 To give feedback, visit https://discord.gg/cloudflaredev
100+
--------------------
101+
102+
✅ Successfully created Project \\"new_project3\\"!"
103+
`);
104+
});
105+
106+
it("should handle project listing", async () => {
107+
mockConstellationRequest();
108+
await runWrangler("constellation project list");
109+
expect(std.out).toMatchInlineSnapshot(`
110+
"--------------------
111+
🚧 Constellation AI is currently in open alpha and is not recommended for production data and traffic
112+
🚧 Please report any bugs to https://github.com/cloudflare/workers-sdk/issues/new/choose
113+
🚧 To give feedback, visit https://discord.gg/cloudflaredev
114+
--------------------
115+
116+
┌──────────────────────────────────────┬──────────────┬─────────┬─────────────────────────────┐
117+
│ id │ name │ runtime │ created_at │
118+
├──────────────────────────────────────┼──────────────┼─────────┼─────────────────────────────┤
119+
│ 4806cdcf-9aa7-4fa2-b6a1-77fe9e196680 │ new_project3 │ ONNX │ 2023-04-28T13:25:58.513105Z │
120+
└──────────────────────────────────────┴──────────────┴─────────┴─────────────────────────────┘"
121+
`);
122+
});
123+
124+
it("should handle project deletion", async () => {
125+
mockConstellationRequest();
126+
mockConfirm({
127+
text: "Ok to proceed?",
128+
result: true,
129+
});
130+
131+
await runWrangler("constellation project delete new_project3");
132+
expect(std.out).toMatchInlineSnapshot(`
133+
"--------------------
134+
🚧 Constellation AI is currently in open alpha and is not recommended for production data and traffic
135+
🚧 Please report any bugs to https://github.com/cloudflare/workers-sdk/issues/new/choose
136+
🚧 To give feedback, visit https://discord.gg/cloudflaredev
137+
--------------------
138+
139+
About to delete Project 'new_project3' (4806cdcf-9aa7-4fa2-b6a1-77fe9e196680).
140+
Deleting...
141+
Deleted 'new_project3' successfully."
142+
`);
143+
});
144+
145+
it("should handle catalog list", async () => {
146+
mockConstellationRequest();
147+
await runWrangler("constellation catalog list");
148+
expect(std.out).toMatchInlineSnapshot(`
149+
"--------------------
150+
🚧 Constellation AI is currently in open alpha and is not recommended for production data and traffic
151+
🚧 Please report any bugs to https://github.com/cloudflare/workers-sdk/issues/new/choose
152+
🚧 To give feedback, visit https://discord.gg/cloudflaredev
153+
--------------------
154+
155+
┌──────────────────────────────────────┬──────────────────────┬─────────────────┬───────────────┐
156+
│ project_id │ project_name │ project_runtime │ models │
157+
├──────────────────────────────────────┼──────────────────────┼─────────────────┼───────────────┤
158+
│ b162a29d-0a6d-4155-bedf-54a01fc8d0ef │ image-classification │ ONNX │ squeezenet1_1 │
159+
└──────────────────────────────────────┴──────────────────────┴─────────────────┴───────────────┘"
160+
`);
161+
});
162+
163+
it("should handle runtime list", async () => {
164+
mockConstellationRequest();
165+
await runWrangler("constellation runtime list");
166+
expect(std.out).toMatchInlineSnapshot(`
167+
"--------------------
168+
🚧 Constellation AI is currently in open alpha and is not recommended for production data and traffic
169+
🚧 Please report any bugs to https://github.com/cloudflare/workers-sdk/issues/new/choose
170+
🚧 To give feedback, visit https://discord.gg/cloudflaredev
171+
--------------------
172+
173+
┌─────────┐
174+
│ name │
175+
├─────────┤
176+
│ ONNX │
177+
├─────────┤
178+
│ XGBoost │
179+
└─────────┘"
180+
`);
181+
});
182+
183+
it("should handle model upload", async () => {
184+
mockConstellationRequest();
185+
await fs.promises.writeFile("model.onnx", `model`);
186+
await runWrangler(
187+
"constellation model upload new_project3 model2 model.onnx"
188+
);
189+
expect(std.out).toMatchInlineSnapshot(`
190+
"--------------------
191+
🚧 Constellation AI is currently in open alpha and is not recommended for production data and traffic
192+
🚧 Please report any bugs to https://github.com/cloudflare/workers-sdk/issues/new/choose
193+
🚧 To give feedback, visit https://discord.gg/cloudflaredev
194+
--------------------
195+
196+
✅ Successfully uploaded Model \\"model2\\"!"
197+
`);
198+
});
199+
200+
it("should handle model list", async () => {
201+
mockConstellationRequest();
202+
await runWrangler("constellation model list new_project3");
203+
expect(std.out).toMatchInlineSnapshot(`
204+
"--------------------
205+
🚧 Constellation AI is currently in open alpha and is not recommended for production data and traffic
206+
🚧 Please report any bugs to https://github.com/cloudflare/workers-sdk/issues/new/choose
207+
🚧 To give feedback, visit https://discord.gg/cloudflaredev
208+
--------------------
209+
210+
┌──────────────────────────────────────┬──────────────────────────────────────┬────────┬─────────────┬─────────────────────────────┐
211+
│ id │ project_id │ name │ description │ created_at │
212+
├──────────────────────────────────────┼──────────────────────────────────────┼────────┼─────────────┼─────────────────────────────┤
213+
│ 450bb086-3c09-4991-a0cc-eed48c504ae0 │ 9d478427-dea6-4988-9b16-f6f8888d974c │ model1 │ │ 2023-04-28T11:15:14.806217Z │
214+
├──────────────────────────────────────┼──────────────────────────────────────┼────────┼─────────────┼─────────────────────────────┤
215+
│ 2dd35b4e-0c7a-4c7a-a9e2-e33c0e17bc02 │ 9d478427-dea6-4988-9b16-f6f8888d974c │ model2 │ │ 2023-04-28T13:50:37.494090Z │
216+
└──────────────────────────────────────┴──────────────────────────────────────┴────────┴─────────────┴─────────────────────────────┘"
217+
`);
218+
});
219+
220+
it("should handle model deletion", async () => {
221+
mockConstellationRequest();
222+
mockConfirm({
223+
text: "Ok to proceed?",
224+
result: true,
225+
});
226+
227+
await runWrangler("constellation model delete new_project3 model2");
228+
expect(std.out).toMatchInlineSnapshot(`
229+
"--------------------
230+
🚧 Constellation AI is currently in open alpha and is not recommended for production data and traffic
231+
🚧 Please report any bugs to https://github.com/cloudflare/workers-sdk/issues/new/choose
232+
🚧 To give feedback, visit https://discord.gg/cloudflaredev
233+
--------------------
234+
235+
About to delete Model 'model2' (2dd35b4e-0c7a-4c7a-a9e2-e33c0e17bc02).
236+
Deleting...
237+
Deleted 'model2' successfully."
238+
`);
239+
});
240+
});
241+
242+
/** Create a mock handler for Constellation API */
243+
function mockConstellationRequest() {
244+
msw.use(
245+
rest.get("*/accounts/:accountId/constellation/project", (req, res, ctx) => {
246+
return res.once(
247+
ctx.json(
248+
createFetchResult(
249+
[
250+
{
251+
id: "4806cdcf-9aa7-4fa2-b6a1-77fe9e196680",
252+
name: "new_project3",
253+
runtime: "ONNX",
254+
created_at: "2023-04-28T13:25:58.513105Z",
255+
},
256+
],
257+
true
258+
)
259+
)
260+
);
261+
}),
262+
rest.post(
263+
"*/accounts/:accountId/constellation/project",
264+
(req, res, ctx) => {
265+
return res.once(
266+
ctx.json(
267+
createFetchResult(
268+
{
269+
id: "4806cdcf-9aa7-4fa2-b6a1-77fe9e196680",
270+
name: "new_project3",
271+
runtime: "ONNX",
272+
created_at: "2023-04-28T13:25:58.513105Z",
273+
},
274+
true
275+
)
276+
)
277+
);
278+
}
279+
),
280+
rest.delete(
281+
"*/accounts/:accountId/constellation/project/4806cdcf-9aa7-4fa2-b6a1-77fe9e196680",
282+
(req, res, ctx) => {
283+
return res.once(ctx.json(createFetchResult(null, true)));
284+
}
285+
),
286+
rest.get("*/accounts/:accountId/constellation/catalog", (req, res, ctx) => {
287+
return res.once(
288+
ctx.json(
289+
createFetchResult(
290+
[
291+
{
292+
project: {
293+
id: "b162a29d-0a6d-4155-bedf-54a01fc8d0ef",
294+
name: "image-classification",
295+
runtime: "ONNX",
296+
created_at: "2023-04-27T18:55:38.417187Z",
297+
},
298+
models: [
299+
{
300+
id: "edb202d3-f4ac-43ab-8762-3ae6b43c4c57",
301+
project_id: "b162a29d-0a6d-4155-bedf-54a01fc8d0ef",
302+
name: "squeezenet1_1",
303+
description: null,
304+
created_at: "2023-04-27T18:56:15.305087Z",
305+
},
306+
],
307+
},
308+
],
309+
true
310+
)
311+
)
312+
);
313+
}),
314+
rest.get("*/accounts/:accountId/constellation/runtime", (req, res, ctx) => {
315+
return res.once(ctx.json(createFetchResult(["ONNX", "XGBoost"], true)));
316+
}),
317+
rest.post(
318+
"*/accounts/:accountId/constellation/project/4806cdcf-9aa7-4fa2-b6a1-77fe9e196680/model",
319+
(req, res, ctx) => {
320+
return res.once(
321+
ctx.json(
322+
createFetchResult(
323+
{
324+
id: "2dd35b4e-0c7a-4c7a-a9e2-e33c0e17bc02",
325+
project_id: "4806cdcf-9aa7-4fa2-b6a1-77fe9e196680",
326+
name: "model2",
327+
description: null,
328+
created_at: "2023-04-28T13:50:37.494090Z",
329+
},
330+
true
331+
)
332+
)
333+
);
334+
}
335+
),
336+
rest.get(
337+
"*/accounts/:accountId/constellation/project/4806cdcf-9aa7-4fa2-b6a1-77fe9e196680/model",
338+
(req, res, ctx) => {
339+
return res.once(
340+
ctx.json(
341+
createFetchResult(
342+
[
343+
{
344+
id: "450bb086-3c09-4991-a0cc-eed48c504ae0",
345+
project_id: "9d478427-dea6-4988-9b16-f6f8888d974c",
346+
name: "model1",
347+
description: null,
348+
created_at: "2023-04-28T11:15:14.806217Z",
349+
},
350+
{
351+
id: "2dd35b4e-0c7a-4c7a-a9e2-e33c0e17bc02",
352+
project_id: "9d478427-dea6-4988-9b16-f6f8888d974c",
353+
name: "model2",
354+
description: null,
355+
created_at: "2023-04-28T13:50:37.494090Z",
356+
},
357+
],
358+
true
359+
)
360+
)
361+
);
362+
}
363+
),
364+
rest.delete(
365+
"*/accounts/:accountId/constellation/project/4806cdcf-9aa7-4fa2-b6a1-77fe9e196680/model/2dd35b4e-0c7a-4c7a-a9e2-e33c0e17bc02",
366+
(req, res, ctx) => {
367+
return res.once(ctx.json(createFetchResult(null, true)));
368+
}
369+
)
370+
);
371+
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ describe("wrangler", () => {
4949
wrangler r2 📦 Interact with an R2 store
5050
wrangler dispatch-namespace 📦 Interact with a dispatch namespace
5151
wrangler d1 🗄 Interact with a D1 database
52+
wrangler constellation 🤖 Interact with Constellation AI models
5253
wrangler pubsub 📮 Interact and manage Pub/Sub Brokers
5354
wrangler mtls-certificate 🪪 Manage certificates used for mTLS connections
5455
wrangler login 🔓 Login to Cloudflare
@@ -102,6 +103,7 @@ describe("wrangler", () => {
102103
wrangler r2 📦 Interact with an R2 store
103104
wrangler dispatch-namespace 📦 Interact with a dispatch namespace
104105
wrangler d1 🗄 Interact with a D1 database
106+
wrangler constellation 🤖 Interact with Constellation AI models
105107
wrangler pubsub 📮 Interact and manage Pub/Sub Brokers
106108
wrangler mtls-certificate 🪪 Manage certificates used for mTLS connections
107109
wrangler login 🔓 Login to Cloudflare

0 commit comments

Comments
 (0)