diff --git a/packages/ai/src/protocols/bedrock-converse.ts b/packages/ai/src/protocols/bedrock-converse.ts index 3740f10f89d..6807bd9387e 100644 --- a/packages/ai/src/protocols/bedrock-converse.ts +++ b/packages/ai/src/protocols/bedrock-converse.ts @@ -212,11 +212,7 @@ const BedrockEvent = Schema.Struct({ metrics: Schema.optional(Schema.Unknown), }), ), - internalServerException: Schema.optional(BedrockStreamException), - modelStreamErrorException: Schema.optional(BedrockStreamException), - validationException: Schema.optional(BedrockStreamException), - throttlingException: Schema.optional(BedrockStreamException), - serviceUnavailableException: Schema.optional(BedrockStreamException), + exception: Schema.optional(Schema.Struct({ type: Schema.String, details: BedrockStreamException })), }) type BedrockEvent = Schema.Schema.Type @@ -650,22 +646,14 @@ const step = (state: ParserState, event: BedrockEvent) => ] as const } - const exception = ( - [ - ["internalServerException", event.internalServerException], - ["modelStreamErrorException", event.modelStreamErrorException], - ["serviceUnavailableException", event.serviceUnavailableException], - ["throttlingException", event.throttlingException], - ["validationException", event.validationException], - ] as const - ).find((entry) => entry[1] !== undefined) - if (exception) { + if (event.exception) { return yield* new AIError({ module: ADAPTER, method: "stream", reason: classifyProviderFailure({ - message: exception[1]?.message ?? exception[1]?.originalMessage ?? "Bedrock Converse stream error", - code: exception[0], + message: + event.exception.details.message ?? event.exception.details.originalMessage ?? "Bedrock Converse stream error", + code: event.exception.type, }), }) } diff --git a/packages/ai/src/protocols/bedrock-event-stream.ts b/packages/ai/src/protocols/bedrock-event-stream.ts index 782a1b77ca1..32ef974e32e 100644 --- a/packages/ai/src/protocols/bedrock-event-stream.ts +++ b/packages/ai/src/protocols/bedrock-event-stream.ts @@ -82,7 +82,7 @@ const consumeFrames = (route: string) => (state: FrameBufferState, chunk: Uint8A "Failed to parse Bedrock Converse event-stream payload", )) as Record delete parsed.p - out.push({ [eventType]: parsed }) + out.push(messageType === "exception" ? { exception: { type: eventType, details: parsed } } : { [eventType]: parsed }) } return [cursor, out] as const }) diff --git a/packages/ai/test/provider/bedrock-converse.test.ts b/packages/ai/test/provider/bedrock-converse.test.ts index 58e5e2ee206..701e5507a33 100644 --- a/packages/ai/test/provider/bedrock-converse.test.ts +++ b/packages/ai/test/provider/bedrock-converse.test.ts @@ -716,6 +716,32 @@ describe("Bedrock Converse route", () => { }), ) + it.effect("ignores unknown normal stream events", () => + Effect.gen(function* () { + const body = concat([ + eventFrame("messageStart", { role: "assistant" }), + eventFrame("futureEvent", { message: "Ignore this" }), + eventFrame("messageStop", { stopReason: "end_turn" }), + ]) + const response = yield* LLMClient.generate(baseRequest).pipe(Effect.provide(fixedBytes(body))) + + expect(response.finishReason).toEqual({ normalized: "stop", raw: "end_turn" }) + }), + ) + + it.effect("fails unknown stream exceptions after message stop", () => + Effect.gen(function* () { + const body = concat([ + eventFrame("messageStart", { role: "assistant" }), + eventFrame("messageStop", { stopReason: "end_turn" }), + exceptionFrame("futureException", { message: "A future provider failure" }), + ]) + const error = yield* LLMClient.generate(baseRequest).pipe(Effect.provide(fixedBytes(body)), Effect.flip) + + expect(error.reason).toMatchObject({ _tag: "UnknownProvider", message: "A future provider failure" }) + }), + ) + it.effect("classifies throttlingException as a rate limit", () => Effect.gen(function* () { const body = concat([