Skip to content

Commit 289952c

Browse files
Merge pull request #51 from netlify/add-contextual-chat-bot-example
Add new example: Contextual Chatbot using OpenAI, Astro, and Netlify + Blob storage
2 parents ce36763 + 8f292fa commit 289952c

File tree

17 files changed

+8621
-0
lines changed

17 files changed

+8621
-0
lines changed

examples/ai-chat-blob-context/.cursor/rules/netlify-development.mdc

Lines changed: 840 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# build output
2+
dist/
3+
4+
# generated types
5+
.astro/
6+
7+
# dependencies
8+
node_modules/
9+
10+
# logs
11+
npm-debug.log*
12+
yarn-debug.log*
13+
yarn-error.log*
14+
pnpm-debug.log*
15+
16+
# environment variables
17+
.env
18+
.env.production
19+
20+
# macOS-specific files
21+
.DS_Store
22+
23+
# jetbrains setting folder
24+
.idea/
25+
26+
# Local Netlify folder
27+
.netlify
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"recommendations": ["astro-build.astro-vscode"],
3+
"unwantedRecommendations": []
4+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"version": "0.2.0",
3+
"configurations": [
4+
{
5+
"command": "./node_modules/.bin/astro dev",
6+
"name": "Development server",
7+
"request": "launch",
8+
"type": "node-terminal"
9+
}
10+
]
11+
}

examples/ai-chat-blob-context/README.md

Whitespace-only changes.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// @ts-check
2+
import { defineConfig } from 'astro/config';
3+
4+
import tailwindcss from '@tailwindcss/vite';
5+
import netlify from '@astrojs/netlify';
6+
7+
import react from '@astrojs/react';
8+
9+
// https://astro.build/config
10+
export default defineConfig({
11+
vite: {
12+
plugins: [tailwindcss()]
13+
},
14+
15+
adapter: netlify(),
16+
integrations: [react()]
17+
});
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import type { Context } from "@netlify/functions";
2+
import { getDeployStore } from "@netlify/blobs";
3+
import OpenAI from "openai";
4+
5+
const CHAT_KEY = "current-chat";
6+
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
7+
8+
interface ChatMessage {
9+
role: "user" | "assistant";
10+
content: string;
11+
}
12+
13+
export default async function(req: Request, context: Context) {
14+
if (req.method !== "POST") {
15+
return new Response("Method Not Allowed", { status: 405 });
16+
}
17+
18+
try {
19+
const { message, newConversation } = await req.json();
20+
const store = getDeployStore("chat-history");
21+
22+
23+
if (newConversation) {
24+
await store.setJSON(CHAT_KEY, []);
25+
return new Response(JSON.stringify({ success: true }));
26+
}
27+
28+
if (!message) {
29+
return new Response("Message is required", { status: 400 });
30+
}
31+
32+
// Get history and update with user message
33+
const history = (await store.get(CHAT_KEY, { type: "json" })) as ChatMessage[] || [];
34+
const updatedHistory = [...history, { role: "user", content: message }];
35+
36+
// Stream the AI response
37+
const stream = await openai.chat.completions.create({
38+
model: "gpt-3.5-turbo",
39+
messages: updatedHistory,
40+
stream: true,
41+
});
42+
43+
return new Response(
44+
new ReadableStream({
45+
async start(controller) {
46+
let assistantMessage = '';
47+
for await (const chunk of stream) {
48+
const text = chunk.choices[0]?.delta?.content || "";
49+
assistantMessage += text;
50+
controller.enqueue(new TextEncoder().encode(text));
51+
}
52+
53+
await store.setJSON(CHAT_KEY, [
54+
...updatedHistory,
55+
{ role: "assistant", content: assistantMessage }
56+
]);
57+
controller.close();
58+
},
59+
}),
60+
{
61+
headers: {
62+
"Content-Type": "text/event-stream",
63+
"Cache-Control": "no-cache",
64+
"Connection": "keep-alive",
65+
},
66+
}
67+
);
68+
69+
} catch (error) {
70+
console.error("Error:", error);
71+
return new Response(JSON.stringify({ error: "Internal Server Error" }), {
72+
status: 500,
73+
});
74+
}
75+
}

0 commit comments

Comments
 (0)