@link-assistant/agent 0.16.10 → 0.16.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/provider/provider.ts +79 -0
package/package.json
CHANGED
package/src/provider/provider.ts
CHANGED
|
@@ -1289,6 +1289,85 @@ export namespace Provider {
|
|
|
1289
1289
|
responseHeaders: Object.fromEntries(response.headers.entries()),
|
|
1290
1290
|
}));
|
|
1291
1291
|
|
|
1292
|
+
// Log response body for debugging provider failures
|
|
1293
|
+
// For streaming responses (SSE/event-stream), tee() the stream so the AI SDK
|
|
1294
|
+
// still receives the full stream while we asynchronously log a preview.
|
|
1295
|
+
// For non-streaming responses, buffer the body and reconstruct the Response.
|
|
1296
|
+
// See: https://github.com/link-assistant/agent/issues/204
|
|
1297
|
+
const responseBodyMaxChars = 4000;
|
|
1298
|
+
const contentType = response.headers.get('content-type') ?? '';
|
|
1299
|
+
const isStreaming =
|
|
1300
|
+
contentType.includes('event-stream') ||
|
|
1301
|
+
contentType.includes('octet-stream');
|
|
1302
|
+
|
|
1303
|
+
if (response.body) {
|
|
1304
|
+
if (isStreaming) {
|
|
1305
|
+
// Tee the stream: one copy for AI SDK, one for logging
|
|
1306
|
+
const [sdkStream, logStream] = response.body.tee();
|
|
1307
|
+
|
|
1308
|
+
// Consume log stream asynchronously (does not block SDK)
|
|
1309
|
+
(async () => {
|
|
1310
|
+
try {
|
|
1311
|
+
const reader = logStream.getReader();
|
|
1312
|
+
const decoder = new TextDecoder();
|
|
1313
|
+
let bodyPreview = '';
|
|
1314
|
+
let truncated = false;
|
|
1315
|
+
while (true) {
|
|
1316
|
+
const { done, value } = await reader.read();
|
|
1317
|
+
if (done) break;
|
|
1318
|
+
if (!truncated) {
|
|
1319
|
+
const chunk = decoder.decode(value, { stream: true });
|
|
1320
|
+
bodyPreview += chunk;
|
|
1321
|
+
if (bodyPreview.length > responseBodyMaxChars) {
|
|
1322
|
+
bodyPreview = bodyPreview.slice(
|
|
1323
|
+
0,
|
|
1324
|
+
responseBodyMaxChars
|
|
1325
|
+
);
|
|
1326
|
+
truncated = true;
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
log.info(() => ({
|
|
1331
|
+
message: 'HTTP response body (stream)',
|
|
1332
|
+
providerID: provider.id,
|
|
1333
|
+
url,
|
|
1334
|
+
bodyPreview: truncated
|
|
1335
|
+
? bodyPreview + `... [truncated]`
|
|
1336
|
+
: bodyPreview,
|
|
1337
|
+
}));
|
|
1338
|
+
} catch {
|
|
1339
|
+
// Ignore logging errors — do not affect the SDK stream
|
|
1340
|
+
}
|
|
1341
|
+
})();
|
|
1342
|
+
|
|
1343
|
+
// Return response with the SDK's copy of the stream
|
|
1344
|
+
return new Response(sdkStream, {
|
|
1345
|
+
status: response.status,
|
|
1346
|
+
statusText: response.statusText,
|
|
1347
|
+
headers: response.headers,
|
|
1348
|
+
});
|
|
1349
|
+
} else {
|
|
1350
|
+
// Non-streaming: buffer body, log it, reconstruct Response
|
|
1351
|
+
const bodyText = await response.text();
|
|
1352
|
+
const bodyPreview =
|
|
1353
|
+
bodyText.length > responseBodyMaxChars
|
|
1354
|
+
? bodyText.slice(0, responseBodyMaxChars) +
|
|
1355
|
+
`... [truncated, total ${bodyText.length} chars]`
|
|
1356
|
+
: bodyText;
|
|
1357
|
+
log.info(() => ({
|
|
1358
|
+
message: 'HTTP response body',
|
|
1359
|
+
providerID: provider.id,
|
|
1360
|
+
url,
|
|
1361
|
+
bodyPreview,
|
|
1362
|
+
}));
|
|
1363
|
+
return new Response(bodyText, {
|
|
1364
|
+
status: response.status,
|
|
1365
|
+
statusText: response.statusText,
|
|
1366
|
+
headers: response.headers,
|
|
1367
|
+
});
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1292
1371
|
return response;
|
|
1293
1372
|
} catch (error) {
|
|
1294
1373
|
const durationMs = Date.now() - startMs;
|