Skip to content

Commit 799d69f

Browse files
committed
feat: gemini client
1 parent 089e805 commit 799d69f

File tree

4 files changed

+31
-4
lines changed

4 files changed

+31
-4
lines changed

apps/web/src/games/demo-with-backend/components/AiChatBackendDemo.tsx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export const AiChatBackendDemo: React.FC = () => {
77
const [input, setInput] = React.useState('');
88
const [loading, setLoading] = React.useState(false);
99
const [error, setError] = React.useState('');
10+
const [stream, setStream] = React.useState(true);
1011
const abortRef = React.useRef<AbortController | null>(null);
1112

1213
const onSend = async () => {
@@ -21,11 +22,12 @@ export const AiChatBackendDemo: React.FC = () => {
2122
const assistantDraft: ChatMessage = { role: 'assistant', content: '' };
2223
setMessages(prev => [...prev, assistantDraft]);
2324
try {
24-
await callBackendAi({
25+
const result = await callBackendAi({
2526
model,
2627
messages: nextMessages,
2728
signal: controller.signal,
28-
onChunk: (m: { role: 'assistant'; content: string; reasoning_content: string; timestamp: string }) => {
29+
stream,
30+
onChunk: (m: { role: 'assistant'; content: string; reasoning_content: string; timestamp: string }) => {
2931
assistantDraft.content = m.content;
3032
setMessages(prev => {
3133
const copy = [...prev];
@@ -34,6 +36,13 @@ export const AiChatBackendDemo: React.FC = () => {
3436
});
3537
}
3638
});
39+
if (!stream) {
40+
setMessages(prev => {
41+
const copy = [...prev];
42+
copy[copy.length - 1] = { role: 'assistant', content: result.content };
43+
return copy;
44+
});
45+
}
3746
} catch (e) {
3847
setError(e instanceof Error ? e.message : '调用失败');
3948
} finally {
@@ -51,6 +60,10 @@ export const AiChatBackendDemo: React.FC = () => {
5160
<h2 className="text-base sm:text-lg font-semibold text-gray-900">AI 后端代理演示</h2>
5261
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3">
5362
<input className="px-3 py-2 border rounded col-span-1 sm:col-span-1" placeholder="Model" value={model} onChange={(e) => setModel(e.target.value)} />
63+
<label className="flex items-center gap-2 text-sm col-span-1">
64+
<input type="checkbox" checked={stream} onChange={(e) => setStream(e.target.checked)} />
65+
<span>流式</span>
66+
</label>
5467
</div>
5568
<div className="h-64 border rounded p-3 overflow-auto bg-gray-50">
5669
{messages.map((m, i) => (

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"dependencies": {
2020
"@tobenot/basic-web-game-backend-contract": "^1.1.4",
2121
"@trpc/client": "^11.4.3",
22+
"@trpc/server": "^11.4.3",
2223
"@types/react-router-dom": "^5.3.3",
2324
"react": "^18.2.0",
2425
"react-dom": "^18.2.0",

packages/services/src/AiService.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,10 @@ type BackendCallParams = {
6161
messages: ChatMessage[];
6262
signal?: AbortSignal;
6363
onChunk?: (m: { role: 'assistant'; content: string; reasoning_content: string; timestamp: string }) => void;
64+
stream?: boolean;
6465
};
6566

66-
export async function callBackendAi({ model, messages, signal, onChunk }: BackendCallParams) {
67+
export async function callBackendAi({ model, messages, signal, onChunk, stream = true }: BackendCallParams) {
6768
const baseUrl = getBackendBaseUrl();
6869
const token = typeof window !== 'undefined' ? localStorage.getItem('sessionToken') : null;
6970
const response = await fetch(`${baseUrl}/api/v1/chat/completions`, {
@@ -73,12 +74,19 @@ export async function callBackendAi({ model, messages, signal, onChunk }: Backen
7374
...(token ? { Authorization: `Bearer ${token}` } : {})
7475
},
7576
signal,
76-
body: JSON.stringify({ model, messages, stream: true })
77+
body: JSON.stringify({ model, messages, stream })
7778
});
7879
if (!response.ok) {
7980
const errorText = await response.text();
8081
throw new Error(`AI后端请求失败: ${response.status} - ${errorText}`);
8182
}
83+
if (!stream) {
84+
const data = await response.json();
85+
const content = data?.choices?.[0]?.message?.content ?? '';
86+
const newMessage = { role: 'assistant' as const, content, reasoning_content: '', timestamp: new Date().toISOString() };
87+
if (onChunk) onChunk(newMessage);
88+
return newMessage;
89+
}
8290
const newMessage = { role: 'assistant' as const, content: '', reasoning_content: '', timestamp: new Date().toISOString() };
8391
const reader = response.body?.getReader();
8492
if (!reader) return newMessage;

yarn.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -945,6 +945,11 @@
945945
resolved "https://registry.yarnpkg.com/@trpc/client/-/client-11.4.3.tgz#0768180ede079f679408f316bbfbcb2bb88f8ba0"
946946
integrity sha512-i2suttUCfColktXT8bqex5kHW5jpT15nwUh0hGSDiW1keN621kSUQKcLJ095blqQAUgB+lsmgSqSMmB4L9shQQ==
947947

948+
"@trpc/server@^11.4.3":
949+
version "11.4.4"
950+
resolved "https://registry.npmmirror.com/@trpc/server/-/server-11.4.4.tgz#847455e43ec236e163f0a34d95237d140618dfb7"
951+
integrity sha512-VkJb2xnb4rCynuwlCvgPBh5aM+Dco6fBBIo6lWAdJJRYVwtyE5bxNZBgUvRRz/cFSEAy0vmzLxF7aABDJfK5Rg==
952+
948953
"@types/archiver@^6.0.3":
949954
version "6.0.3"
950955
resolved "https://registry.yarnpkg.com/@types/archiver/-/archiver-6.0.3.tgz#074eb6f4febc0128c25a205a8263da3d4688df53"

0 commit comments

Comments
 (0)