wuyiqunLu commited on
Commit
46f65e5
·
unverified ·
1 Parent(s): 0fd8446

fix: online stream message issue (#71)

Browse files
.gitignore CHANGED
@@ -38,4 +38,5 @@ yarn-error.log*
38
  .env*.local
39
 
40
  # ts
41
- tsconfig.tsbuildinfo
 
 
38
  .env*.local
39
 
40
  # ts
41
+ tsconfig.tsbuildinfo
42
+ certificates
app/api/vision-agent/route.ts CHANGED
@@ -1,7 +1,8 @@
1
- import { StreamingTextResponse } from 'ai';
2
 
3
  // import { auth } from '@/auth';
4
  import { MessageUI, SignedPayload } from '@/lib/types';
 
5
  import { logger, withLogging } from '@/lib/logger';
6
  import { CLEANED_SEPARATOR } from '@/lib/constants';
7
  import { cleanAnswerMessage, cleanInputMessage } from '@/lib/utils/content';
@@ -145,29 +146,21 @@ export const POST = withLogging(
145
  );
146
  }
147
  }
 
148
 
149
  if (fetchResponse.body) {
150
  const encoder = new TextEncoder();
151
  const decoder = new TextDecoder('utf-8');
152
  let maxChunkSize = 0;
153
- const stream = fetchResponse.body.pipeThrough(
154
- new TransformStream({
155
- flush: () => {
156
- logger.info(
157
- session,
158
- {
159
- message: 'Streaming finished',
160
- maxChunkSize,
161
- },
162
- request,
163
- '__AGENT_DONE',
164
- );
165
- },
166
- transform: async (chunk, controller) => {
167
- const data = decoder.decode(chunk, { stream: true });
168
  maxChunkSize = Math.max(data.length, maxChunkSize);
169
  const lines = data.split('\n');
170
  const results = [];
 
171
  for (let line of lines) {
172
  if (!line.trim()) {
173
  continue;
@@ -194,6 +187,7 @@ export const POST = withLogging(
194
  }
195
  msg.payload.result = JSON.stringify(result);
196
  results.push(JSON.stringify(msg));
 
197
  } catch (e) {
198
  console.error(e);
199
  logger.error(
@@ -203,6 +197,7 @@ export const POST = withLogging(
203
  },
204
  request,
205
  );
 
206
  }
207
  }
208
  controller.enqueue(
@@ -210,10 +205,26 @@ export const POST = withLogging(
210
  results.length === 0 ? '' : results.join('\n') + '\n',
211
  ),
212
  );
213
- },
214
- }),
215
- );
216
- return new StreamingTextResponse(stream);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
217
  } else {
218
  return fetchResponse;
219
  }
 
1
+ import { StreamingTextResponse, experimental_StreamData } from 'ai';
2
 
3
  // import { auth } from '@/auth';
4
  import { MessageUI, SignedPayload } from '@/lib/types';
5
+
6
  import { logger, withLogging } from '@/lib/logger';
7
  import { CLEANED_SEPARATOR } from '@/lib/constants';
8
  import { cleanAnswerMessage, cleanInputMessage } from '@/lib/utils/content';
 
146
  );
147
  }
148
  }
149
+ // const streamData = new experimental_StreamData();
150
 
151
  if (fetchResponse.body) {
152
  const encoder = new TextEncoder();
153
  const decoder = new TextDecoder('utf-8');
154
  let maxChunkSize = 0;
155
+ const stream = new ReadableStream({
156
+ async start(controller) {
157
+ // const parser = createParser(streamParser);
158
+ for await (const chunk of fetchResponse.body as any) {
159
+ const data = decoder.decode(chunk);
 
 
 
 
 
 
 
 
 
 
160
  maxChunkSize = Math.max(data.length, maxChunkSize);
161
  const lines = data.split('\n');
162
  const results = [];
163
+ let done = false;
164
  for (let line of lines) {
165
  if (!line.trim()) {
166
  continue;
 
187
  }
188
  msg.payload.result = JSON.stringify(result);
189
  results.push(JSON.stringify(msg));
190
+ done = true;
191
  } catch (e) {
192
  console.error(e);
193
  logger.error(
 
197
  },
198
  request,
199
  );
200
+ controller.error(e);
201
  }
202
  }
203
  controller.enqueue(
 
205
  results.length === 0 ? '' : results.join('\n') + '\n',
206
  ),
207
  );
208
+ if (done) {
209
+ logger.info(
210
+ session,
211
+ {
212
+ message: 'Streaming finished',
213
+ maxChunkSize,
214
+ },
215
+ request,
216
+ '__AGENT_DONE',
217
+ );
218
+ controller.close();
219
+ }
220
+ }
221
+ },
222
+ });
223
+ return new Response(stream, {
224
+ headers: {
225
+ 'Content-Type': 'application/x-ndjson',
226
+ },
227
+ });
228
  } else {
229
  return fetchResponse;
230
  }
components/chat/ChatMessage.tsx CHANGED
@@ -1,5 +1,4 @@
1
  import { useMemo, useState } from 'react';
2
- import { cn } from '@/lib/utils';
3
  import { CodeBlock } from '@/components/ui/CodeBlock';
4
  import {
5
  IconCheckCircle,
@@ -114,7 +113,7 @@ const ChunkPayloadAction: React.FC<{
114
  <IconListUnordered />
115
  </Button>
116
  </DialogTrigger>
117
- <DialogContent className="max-w-5xl">
118
  <Table>
119
  <TableHeader>
120
  <TableRow className="border-primary/50">
 
1
  import { useMemo, useState } from 'react';
 
2
  import { CodeBlock } from '@/components/ui/CodeBlock';
3
  import {
4
  IconCheckCircle,
 
113
  <IconListUnordered />
114
  </Button>
115
  </DialogTrigger>
116
+ <DialogContent className="max-w-5xl" onOpenAutoFocus={e => e.preventDefault()}>
117
  <Table>
118
  <TableHeader>
119
  <TableRow className="border-primary/50">