11/** biome-ignore-all lint/style/noNonNullAssertion: fine for tests */
22/** biome-ignore-all lint/suspicious/noExplicitAny: fine for tests */
33import { describe , expect , test } from "bun:test" ;
4+ import { APICallError } from "ai" ;
45import {
56 applyCompactionToMessages ,
67 buildCompactionRequestMessage ,
@@ -9,6 +10,7 @@ import {
910 countCompactionMarkers ,
1011 createCompactionMarkerPart ,
1112 createCompactionTool ,
13+ findAPICallError ,
1214 findCompactionSummary ,
1315 isOutOfContextError ,
1416} from "./compaction" ;
@@ -55,62 +57,77 @@ function summaryMsg(
5557}
5658
5759describe ( "isOutOfContextError" , ( ) => {
58- test ( "returns true for Anthropic context limit errors" , ( ) => {
59- expect ( isOutOfContextError ( new Error ( "max_tokens_exceeded" ) ) ) . toBe ( true ) ;
60+ const createApiError = ( message : string ) =>
61+ new APICallError ( {
62+ message,
63+ url : "https://api.example.com" ,
64+ requestBodyValues : { } ,
65+ statusCode : 400 ,
66+ } ) ;
67+
68+ test ( "returns true for APICallError with context limit message" , ( ) => {
6069 expect (
61- isOutOfContextError ( new Error ( "The context window has been exceeded" ) )
70+ isOutOfContextError (
71+ createApiError ( "Input is too long for requested model" )
72+ )
6273 ) . toBe ( true ) ;
63- } ) ;
64-
65- test ( "returns true for OpenAI context_length_exceeded errors" , ( ) => {
66- expect ( isOutOfContextError ( new Error ( "context_length_exceeded" ) ) ) . toBe (
74+ expect ( isOutOfContextError ( createApiError ( "context_length_exceeded" ) ) ) . toBe (
6775 true
6876 ) ;
6977 } ) ;
7078
71- test ( "returns true for generic token limit exceeded messages" , ( ) => {
72- expect ( isOutOfContextError ( new Error ( "token limit exceeded" ) ) ) . toBe ( true ) ;
73- expect (
74- isOutOfContextError ( new Error ( "Token limit has been exceeded" ) )
75- ) . toBe ( true ) ;
76- expect ( isOutOfContextError ( new Error ( "maximum tokens reached" ) ) ) . toBe ( true ) ;
79+ test ( "returns true for APICallError in cause chain" , ( ) => {
80+ const apiError = createApiError ( "max_tokens_exceeded" ) ;
81+ const wrapper = new Error ( "Gateway error" ) ;
82+ ( wrapper as { cause ?: unknown } ) . cause = apiError ;
83+ expect ( isOutOfContextError ( wrapper ) ) . toBe ( true ) ;
7784 } ) ;
7885
79- test ( "returns true for context window errors" , ( ) => {
80- expect ( isOutOfContextError ( new Error ( "context window exceeded" ) ) ) . toBe (
81- true
82- ) ;
83- expect ( isOutOfContextError ( new Error ( "context length exceeded" ) ) ) . toBe (
84- true
86+ test ( "returns false for APICallError with unrelated message" , ( ) => {
87+ expect ( isOutOfContextError ( createApiError ( "authentication failed" ) ) ) . toBe (
88+ false
8589 ) ;
8690 } ) ;
8791
88- test ( "returns true for input too long errors" , ( ) => {
89- expect ( isOutOfContextError ( new Error ( "input is too long" ) ) ) . toBe ( true ) ;
90- expect ( isOutOfContextError ( new Error ( "prompt is too long" ) ) ) . toBe ( true ) ;
92+ test ( "returns false for non-APICallError even if message matches pattern" , ( ) => {
93+ expect ( isOutOfContextError ( new Error ( "context_length_exceeded" ) ) ) . toBe (
94+ false
95+ ) ;
96+ expect ( isOutOfContextError ( "input too long" ) ) . toBe ( false ) ;
9197 } ) ;
98+ } ) ;
99+
100+ describe ( "findAPICallError" , ( ) => {
101+ const createApiError = ( message : string ) =>
102+ new APICallError ( {
103+ message,
104+ url : "https://api.example.com" ,
105+ requestBodyValues : { } ,
106+ statusCode : 400 ,
107+ } ) ;
92108
93- test ( "returns false for unrelated errors" , ( ) => {
94- expect ( isOutOfContextError ( new Error ( "network error" ) ) ) . toBe ( false ) ;
95- expect ( isOutOfContextError ( new Error ( "authentication failed" ) ) ) . toBe ( false ) ;
96- expect ( isOutOfContextError ( new Error ( "rate limit exceeded" ) ) ) . toBe ( false ) ;
109+ test ( "returns the APICallError when provided directly" , ( ) => {
110+ const error = createApiError ( "test" ) ;
111+ expect ( findAPICallError ( error ) ) . toBe ( error ) ;
97112 } ) ;
98113
99- test ( "handles string messages" , ( ) => {
100- expect ( isOutOfContextError ( "token limit exceeded" ) ) . toBe ( true ) ;
101- expect ( isOutOfContextError ( "some other error" ) ) . toBe ( false ) ;
114+ test ( "returns APICallError from single-level cause" , ( ) => {
115+ const apiError = createApiError ( "test" ) ;
116+ const wrapper = new Error ( "wrapper" ) ;
117+ ( wrapper as { cause ?: unknown } ) . cause = apiError ;
118+ expect ( findAPICallError ( wrapper ) ) . toBe ( apiError ) ;
102119 } ) ;
103120
104- test ( "handles objects with message property" , ( ) => {
105- expect ( isOutOfContextError ( { message : "token limit exceeded" } ) ) . toBe ( true ) ;
106- expect ( isOutOfContextError ( { message : "some other error" } ) ) . toBe ( false ) ;
121+ test ( "returns APICallError from deep cause chain" , ( ) => {
122+ const apiError = createApiError ( "test" ) ;
123+ const wrapper = { cause : { cause : apiError } } ;
124+ expect ( findAPICallError ( wrapper ) ) . toBe ( apiError ) ;
107125 } ) ;
108126
109- test ( "returns false for non-error values" , ( ) => {
110- expect ( isOutOfContextError ( null ) ) . toBe ( false ) ;
111- expect ( isOutOfContextError ( undefined ) ) . toBe ( false ) ;
112- expect ( isOutOfContextError ( 123 ) ) . toBe ( false ) ;
113- expect ( isOutOfContextError ( { } ) ) . toBe ( false ) ;
127+ test ( "returns null when no APICallError present" , ( ) => {
128+ expect ( findAPICallError ( new Error ( "other" ) ) ) . toBeNull ( ) ;
129+ expect ( findAPICallError ( "string" ) ) . toBeNull ( ) ;
130+ expect ( findAPICallError ( null ) ) . toBeNull ( ) ;
114131 } ) ;
115132} ) ;
116133
0 commit comments