@@ -498,114 +498,6 @@ export class ChatContext {
498498 async toProviderFormat ( format : ProviderFormat , injectDummyUserMessage : boolean = true ) {
499499 return await toChatCtx ( format , this , injectDummyUserMessage ) ;
500500 }
501-
502- /**
503- * Compare this ChatContext with another for logical equivalence.
504- * Unlike strict equality, this method:
505- * - Ignores timestamps (createdAt fields)
506- * - Ignores other volatile metadata
507- * - Focuses on content: compares IDs, types, and payload
508- *
509- * This is useful for detecting if the conversation content has changed,
510- * for example when validating preemptive generation results.
511- *
512- * @param other - The ChatContext to compare with
513- * @returns true if both contexts contain the same sequence of items with matching essential fields
514- */
515- isEquivalent ( other : ChatContext ) : boolean {
516- // Same object reference
517- if ( this === other ) {
518- return true ;
519- }
520-
521- // Different lengths
522- if ( this . _items . length !== other . _items . length ) {
523- return false ;
524- }
525-
526- // Compare each item pair
527- for ( let i = 0 ; i < this . _items . length ; i ++ ) {
528- const a = this . _items [ i ] ! ;
529- const b = other . _items [ i ] ! ;
530-
531- // IDs and types must match
532- if ( a . id !== b . id || a . type !== b . type ) {
533- return false ;
534- }
535-
536- // Type-specific field comparison
537- if ( a . type === 'message' && b . type === 'message' ) {
538- // Compare role, content, and interrupted status (not timestamp)
539- if ( a . role !== b . role || a . interrupted !== b . interrupted ) {
540- return false ;
541- }
542-
543- // Compare content arrays
544- if ( a . content . length !== b . content . length ) {
545- return false ;
546- }
547-
548- for ( let j = 0 ; j < a . content . length ; j ++ ) {
549- const ca = a . content [ j ] ! ;
550- const cb = b . content [ j ] ! ;
551-
552- // Both are strings
553- if ( typeof ca === 'string' && typeof cb === 'string' ) {
554- if ( ca !== cb ) {
555- return false ;
556- }
557- }
558- // Both are objects
559- else if ( typeof ca === 'object' && typeof cb === 'object' ) {
560- if ( ca . type !== cb . type ) {
561- return false ;
562- }
563-
564- if ( ca . type === 'image_content' && cb . type === 'image_content' ) {
565- // Compare essential image fields (not cache)
566- if (
567- ca . id !== cb . id ||
568- ca . image !== cb . image ||
569- ca . inferenceDetail !== cb . inferenceDetail ||
570- ca . inferenceWidth !== cb . inferenceWidth ||
571- ca . inferenceHeight !== cb . inferenceHeight ||
572- ca . mimeType !== cb . mimeType
573- ) {
574- return false ;
575- }
576- } else if ( ca . type === 'audio_content' && cb . type === 'audio_content' ) {
577- // Compare audio transcript (frames comparison would be too expensive)
578- if ( ca . transcript !== cb . transcript ) {
579- return false ;
580- }
581- }
582- }
583- // Mismatched types
584- else {
585- return false ;
586- }
587- }
588- } else if ( a . type === 'function_call' && b . type === 'function_call' ) {
589- // Compare name, callId, and args (not timestamp)
590- if ( a . name !== b . name || a . callId !== b . callId || a . args !== b . args ) {
591- return false ;
592- }
593- } else if ( a . type === 'function_call_output' && b . type === 'function_call_output' ) {
594- // Compare name, callId, output, and isError (not timestamp)
595- if (
596- a . name !== b . name ||
597- a . callId !== b . callId ||
598- a . output !== b . output ||
599- a . isError !== b . isError
600- ) {
601- return false ;
602- }
603- }
604- }
605-
606- return true ;
607- }
608-
609501 /**
610502 * Internal helper used by `truncate` & `addMessage` to find the correct
611503 * insertion index for a timestamp so the list remains sorted.
0 commit comments