Skip to content

Commit c3b922b

Browse files
committed
[Docs Site] Add Support AI page
1 parent fd304f4 commit c3b922b

File tree

4 files changed

+117
-0
lines changed

4 files changed

+117
-0
lines changed

package-lock.json

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
"@iarna/toml": "2.2.5",
4444
"@lottiefiles/dotlottie-react": "0.13.5",
4545
"@marsidev/react-turnstile": "1.1.0",
46+
"@microsoft/fetch-event-source": "2.0.1",
4647
"@nanostores/react": "1.0.0",
4748
"@octokit/webhooks-types": "7.6.1",
4849
"@stoplight/json-schema-tree": "4.0.0",

src/components/SupportAI.tsx

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { useState } from "react";
2+
import Markdown from "react-markdown";
3+
import { fetchEventSource } from "@microsoft/fetch-event-source";
4+
5+
type Messages = { role: "user" | "assistant"; content: string }[];
6+
7+
function Messages({ messages }: { messages: Messages }) {
8+
return (
9+
<div className="flex flex-col justify-center gap-4">
10+
{messages
11+
.filter((message) => Boolean(message.content))
12+
.map((message, index) => (
13+
<div
14+
key={index}
15+
className={`w-fit max-w-3/4 rounded p-2 ${message.role === "user" ? "self-end bg-gray-700" : "self-start bg-orange-900"}`}
16+
>
17+
<Markdown>{message.content}</Markdown>
18+
</div>
19+
))}
20+
</div>
21+
);
22+
}
23+
24+
export default function SupportAI() {
25+
const [threadId, setThreadId] = useState<string | undefined>();
26+
const [messages, setMessages] = useState<Messages>([]);
27+
28+
const [question, setQuestion] = useState<string>("");
29+
const [answer, setAnswer] = useState<string>("");
30+
31+
async function handleSubmit() {
32+
setMessages((messages) => [
33+
...messages,
34+
{ role: "user", content: question },
35+
{ role: "assistant", content: "" },
36+
]);
37+
setQuestion("");
38+
39+
const controller = new AbortController();
40+
const { signal } = controller;
41+
42+
let chunkedAnswer = "";
43+
44+
await fetchEventSource("http://localhost:8010/proxy/devdocs/ask", {
45+
method: "POST",
46+
body: JSON.stringify({
47+
question,
48+
threadId,
49+
}),
50+
signal,
51+
openWhenHidden: true,
52+
onmessage(ev) {
53+
if (ev.data === "[DONE]") {
54+
controller.abort();
55+
}
56+
57+
const { threadId, response } = JSON.parse(ev.data);
58+
59+
if (threadId) {
60+
setThreadId(threadId);
61+
}
62+
63+
if (!response) return;
64+
65+
chunkedAnswer += response;
66+
setMessages((messages) => {
67+
const newMessages = [...messages];
68+
newMessages[newMessages.length - 1].content = chunkedAnswer;
69+
return newMessages;
70+
});
71+
},
72+
});
73+
}
74+
75+
return (
76+
<div>
77+
<Messages messages={messages} />
78+
<div className="flex items-center justify-center gap-4">
79+
<input
80+
type="text"
81+
placeholder="Ask a question..."
82+
value={question}
83+
onChange={(e) => setQuestion(e.target.value)}
84+
onKeyDown={async (e) => {
85+
if (e.key === "Enter") {
86+
e.preventDefault();
87+
await handleSubmit();
88+
}
89+
}}
90+
/>
91+
</div>
92+
<div>
93+
<strong>Debug:</strong>
94+
<pre className="whitespace-pre-wrap">
95+
{JSON.stringify({ threadId, messages, question, answer }, null, 2)}
96+
</pre>
97+
</div>
98+
</div>
99+
);
100+
}

src/pages/support/ai.astro

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
import StarlightPage from "@astrojs/starlight/components/StarlightPage.astro";
3+
import SupportAI from "~/components/SupportAI.tsx";
4+
---
5+
6+
<StarlightPage frontmatter={{ title: "Support AI", tableOfContents: false }}>
7+
<SupportAI client:load />
8+
</StarlightPage>

0 commit comments

Comments
 (0)