Skip to content
This repository was archived by the owner on Mar 18, 2025. It is now read-only.

Commit 95b88d8

Browse files
authored
add support for structured outputs (#31)
* add support for structured outputs * fix structured outputs * of course null is an object
1 parent 3a459c0 commit 95b88d8

File tree

12 files changed

+2674
-699
lines changed

12 files changed

+2674
-699
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"name": "@cloudflare/example-structured-outputs",
3+
"private": true,
4+
"scripts": {
5+
"start": "wrangler dev",
6+
"deploy": "wrangler deploy"
7+
},
8+
"dependencies": {
9+
"@ai-sdk/openai": "^1.1.9",
10+
"ai": "^4.0.14",
11+
"react": "^19.0.0",
12+
"react-dom": "^19.0.0",
13+
"zod": "^3.24.2"
14+
},
15+
"devDependencies": {
16+
"@cloudflare/workers-types": "^4.20250214.0",
17+
"@types/react": "^19.0.1",
18+
"@types/react-dom": "^19.0.2",
19+
"wrangler": "3.109.3"
20+
},
21+
"overrides": {
22+
"react": "^19.0.0",
23+
"react-dom": "^19.0.0"
24+
}
25+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<meta name="description" content="Vercel AI ⤫ Workers AI" />
7+
<title>Workers AI Tool Calling</title>
8+
9+
<link
10+
rel="icon"
11+
href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🙂‍↔️</text></svg>"
12+
/>
13+
14+
<!-- Link to Google Fonts -->
15+
<link
16+
rel="stylesheet"
17+
href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap"
18+
/>
19+
20+
<!-- Favicon -->
21+
<link rel="icon" href="favicon.ico" type="image/x-icon" />
22+
23+
<!-- Social Media Metadata -->
24+
<meta property="og:title" content="YoHello Chat" />
25+
<meta
26+
property="og:description"
27+
content="Your website description goes here"
28+
/>
29+
<meta property="og:image" content="/og-image.jpg" />
30+
<meta name="twitter:card" content="/og-image.jpg" />
31+
32+
<!-- Add any other external libraries if needed -->
33+
</head>
34+
35+
<body>
36+
<div id="root"></div>
37+
<script src="/dist/index.js"></script>
38+
</body>
39+
</html>
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { useChat } from "ai/react";
2+
import { useState } from "react";
3+
4+
type ChatProps = {
5+
id: string;
6+
};
7+
8+
export default function Chat(props: ChatProps) {
9+
const { input, handleInputChange, setInput } = useChat();
10+
const [messages, setMessages] = useState<{ role: string; content: string }[]>(
11+
[]
12+
);
13+
14+
const handleSubmit = async (e: React.FormEvent) => {
15+
e.preventDefault();
16+
17+
setMessages((prevMessages) => [
18+
...prevMessages,
19+
{ role: "user", content: input },
20+
]);
21+
const currentInput = input;
22+
setInput("");
23+
24+
const response = await fetch(`/api`, {
25+
method: "POST",
26+
headers: {
27+
"Content-Type": "application/json",
28+
},
29+
body: JSON.stringify({
30+
messages: [{ role: "user", content: currentInput }],
31+
}),
32+
});
33+
34+
if (!response.ok) {
35+
console.error("Failed to send message");
36+
return;
37+
}
38+
39+
const data = await response.text();
40+
41+
if (data) {
42+
setMessages((prevMessages) => [
43+
...prevMessages,
44+
{ role: "assistant", content: data },
45+
]);
46+
}
47+
};
48+
49+
return (
50+
<>
51+
{messages.map((message) => (
52+
<div key={message.id}>
53+
{message.role === "user" ? "User: " : "AI: "}
54+
{message.content}
55+
</div>
56+
))}
57+
58+
<form onSubmit={handleSubmit}>
59+
<input name="prompt" value={input} onChange={handleInputChange} />
60+
<button type="submit">Submit</button>
61+
</form>
62+
</>
63+
);
64+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { createRoot } from "react-dom/client";
2+
import Chat from "./chat";
3+
4+
function App() {
5+
return <Chat id="example-room" />;
6+
}
7+
8+
const root = createRoot(document.getElementById("root")!);
9+
10+
root.render(<App />);
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"extends": "../../../../tsconfig.base.json",
3+
"compilerOptions": {
4+
"lib": ["DOM", "ESNext"]
5+
}
6+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { generateObject, type Message } from "ai";
2+
import { createWorkersAI } from "workers-ai-provider/src";
3+
import z from "zod";
4+
5+
export interface Env {
6+
AI: Ai;
7+
}
8+
9+
export default {
10+
async fetch(request, env) {
11+
const url = new URL(request.url);
12+
13+
if (request.method === "POST") {
14+
const { messages } = await request.json<{
15+
messages: Message[];
16+
}>();
17+
const workersai = createWorkersAI({ binding: env.AI });
18+
19+
// @ts-expect-error type instantiation too deep
20+
const { object } = await generateObject({
21+
model: workersai("@cf/meta/llama-3.1-8b-instruct"),
22+
// @ts-expect-error type instantiation too deep
23+
schema: z.object({
24+
recipe: z.object({
25+
name: z.string(),
26+
ingredients: z.array(
27+
z.object({ name: z.string(), amount: z.string() })
28+
),
29+
steps: z.array(z.string()),
30+
}),
31+
}),
32+
messages,
33+
});
34+
35+
return new Response(JSON.stringify(object));
36+
}
37+
38+
return new Response("Not Found!!", { status: 404 });
39+
},
40+
} satisfies ExportedHandler<Env>;
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"extends": "../../../../tsconfig.base.json",
3+
"compilerOptions": {
4+
"types": ["@cloudflare/workers-types"]
5+
}
6+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"name": "workers-ai-tool-calling",
3+
"main": "src/server/index.ts",
4+
"compatibility_date": "2025-02-24",
5+
"assets": {
6+
"directory": "public"
7+
},
8+
"build": {
9+
"command": "esbuild src/client/index.tsx --outdir=public/dist --bundle --sourcemap --format=esm --splitting"
10+
},
11+
"ai": {
12+
"binding": "AI"
13+
}
14+
}

examples/tool-calling/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"@cloudflare/workers-types": "^4.20250214.0",
1717
"@types/react": "^19.0.1",
1818
"@types/react-dom": "^19.0.2",
19-
"wrangler": "3.95.0"
19+
"wrangler": "3.109.3"
2020
},
2121
"overrides": {
2222
"react": "^19.0.0",

examples/tool-calling/wrangler.jsonc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "workers-ai-tool-calling",
33
"main": "src/server/index.ts",
4-
"compatibility_date": "2024-07-31",
4+
"compatibility_date": "2025-02-24",
55
"assets": {
66
"directory": "public"
77
},

0 commit comments

Comments
 (0)