Spaces:
Sleeping
Sleeping
wuyiqunLu
commited on
fix: online stream message issue (#71)
Browse files- .gitignore +2 -1
- app/api/vision-agent/route.ts +31 -20
- components/chat/ChatMessage.tsx +1 -2
.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 =
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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">
|