shows more helpful errors when users' prefills are invalid during AWS streaming

This commit is contained in:
nai-degen 2024-03-07 13:28:23 -06:00
parent 96756d32f3
commit 9ce10b4f6a
2 changed files with 36 additions and 8 deletions

View File

@ -6,7 +6,6 @@ import { APIFormat, keyPool } from "../../../shared/key-management";
import {
copySseResponseHeaders,
initializeSseStream,
} from "../../../shared/streaming";
import type { logger } from "../../../logger";
import { enqueue } from "../../queue";
@ -15,7 +14,8 @@ import { getAwsEventStreamDecoder } from "./streaming/aws-event-stream-decoder";
import { EventAggregator } from "./streaming/event-aggregator";
import { SSEMessageTransformer } from "./streaming/sse-message-transformer";
import { SSEStreamAdapter } from "./streaming/sse-stream-adapter";
import { buildSpoofedSSE } from "./error-generator";
import { buildSpoofedSSE, sendErrorToClient } from "./error-generator";
import { BadRequestError } from "../../../shared/errors";
const pipelineAsync = promisify(pipeline);
@ -109,6 +109,18 @@ export const handleStreamedResponse: RawResponseBodyHandler = async (
);
req.retryCount++;
await enqueue(req);
} else if (err instanceof BadRequestError) {
sendErrorToClient({
req,
res,
options: {
format: req.inboundApi,
title: "Proxy streaming error (Bad Request)",
message: `The API returned an error while streaming your request. Your prompt might not be formatted correctly.\n\n*${err.message}*`,
reqId: req.id,
model: req.body?.model,
},
});
} else {
const { message, stack, lastEvent } = err;
const eventText = JSON.stringify(lastEvent, null, 2) ?? "undefined";

View File

@ -4,6 +4,7 @@ import { Message } from "@smithy/eventstream-codec";
import { APIFormat } from "../../../../shared/key-management";
import { RetryableError } from "../index";
import { buildSpoofedSSE } from "../error-generator";
import { BadRequestError } from "../../../../shared/errors";
type SSEStreamAdapterOptions = TransformOptions & {
contentType?: string;
@ -59,8 +60,7 @@ export class SSEStreamAdapter extends Transform {
return [`event: ${eventObj.type}`, `data: ${event}`].join(`\n`);
}
}
// Intentional fallthrough, as non-JSON events may as well be errors
// noinspection FallThroughInSwitchStatementJS
// noinspection FallThroughInSwitchStatementJS -- non-JSON data is unexpected
case "exception":
case "error":
const type = String(
@ -72,16 +72,32 @@ export class SSEStreamAdapter extends Transform {
"AWS request throttled after streaming has already started; retrying"
);
throw new RetryableError("AWS request throttled mid-stream");
case "validationexception":
try {
const { message } = JSON.parse(bodyStr);
this.log.error({ message }, "Received AWS validation error");
this.emit(
"error",
new BadRequestError(`AWS validation error: ${message}`)
);
return null;
} catch (error) {
this.log.error(
{ body: bodyStr, error },
"Could not parse AWS validation error"
);
}
// noinspection FallThroughInSwitchStatementJS -- who knows what this is
default:
this.log.error({ message, type }, "Received bad AWS stream event");
let text;
try {
const { bytes } = JSON.parse(bodyStr);
text = Buffer.from(bytes, "base64").toString("utf8");
text = JSON.parse(bodyStr).message;
} catch (error) {
text = bodyStr;
}
const error: any = new Error(`Got mysterious error chunk: ${type}`);
const error: any = new Error(
`Got mysterious error chunk: [${type}] ${text}`
);
error.lastEvent = text;
this.emit("error", error);
return null;