@@ -46,12 +46,8 @@ type OpenAIResponsesInputContent = Schema.Schema.Type<typeof OpenAIResponsesInpu
4646const OpenAIResponsesOutputText = Schema . Struct ( {
4747 type : Schema . tag ( "output_text" ) ,
4848 text : Schema . String ,
49- annotations : Schema . Array ( Schema . Unknown ) ,
5049} )
5150
52- const OpenAIResponsesMessagePhase = Schema . Literals ( [ "commentary" , "final_answer" ] )
53- type OpenAIResponsesMessagePhase = Schema . Schema . Type < typeof OpenAIResponsesMessagePhase >
54-
5551const OpenAIResponsesReasoningSummaryText = Schema . Struct ( {
5652 type : Schema . tag ( "summary_text" ) ,
5753 text : Schema . String ,
@@ -82,19 +78,7 @@ const OpenAIResponsesFunctionCallOutput = Schema.Union([
8278const OpenAIResponsesInputItem = Schema . Union ( [
8379 Schema . Struct ( { role : Schema . tag ( "system" ) , content : Schema . String } ) ,
8480 Schema . Struct ( { role : Schema . tag ( "user" ) , content : Schema . Array ( OpenAIResponsesInputContent ) } ) ,
85- Schema . Struct ( {
86- role : Schema . tag ( "assistant" ) ,
87- content : Schema . String ,
88- phase : optionalNull ( OpenAIResponsesMessagePhase ) ,
89- } ) ,
90- Schema . Struct ( {
91- type : Schema . tag ( "message" ) ,
92- id : Schema . String ,
93- status : Schema . Literals ( [ "in_progress" , "completed" , "incomplete" ] ) ,
94- role : Schema . tag ( "assistant" ) ,
95- content : Schema . Array ( OpenAIResponsesOutputText ) ,
96- phase : optionalNull ( OpenAIResponsesMessagePhase ) ,
97- } ) ,
81+ Schema . Struct ( { role : Schema . tag ( "assistant" ) , content : Schema . Array ( OpenAIResponsesOutputText ) } ) ,
9882 OpenAIResponsesReasoningItem ,
9983 OpenAIResponsesItemReference ,
10084 Schema . Struct ( {
@@ -210,9 +194,7 @@ const OpenAIResponsesStreamItem = Schema.Struct({
210194 server_label : Schema . optional ( Schema . String ) ,
211195 output : Schema . optional ( Schema . Unknown ) ,
212196 error : Schema . optional ( Schema . Unknown ) ,
213- content : Schema . optional ( Schema . Array ( Schema . Unknown ) ) ,
214197 encrypted_content : optionalNull ( Schema . String ) ,
215- phase : optionalNull ( OpenAIResponsesMessagePhase ) ,
216198} )
217199type OpenAIResponsesStreamItem = Schema . Schema . Type < typeof OpenAIResponsesStreamItem >
218200
@@ -230,9 +212,7 @@ const OpenAIResponsesErrorPayload = Schema.Struct({
230212const OpenAIResponsesEvent = Schema . Struct ( {
231213 type : Schema . String ,
232214 delta : Schema . optional ( Schema . String ) ,
233- text : Schema . optional ( Schema . String ) ,
234215 item_id : Schema . optional ( Schema . String ) ,
235- content_index : Schema . optional ( Schema . Number ) ,
236216 summary_index : Schema . optional ( Schema . Number ) ,
237217 item : Schema . optional ( OpenAIResponsesStreamItem ) ,
238218 response : Schema . optional (
@@ -257,18 +237,10 @@ interface ParserState {
257237 readonly tools : ToolStream . State < string >
258238 readonly hasFunctionCall : boolean
259239 readonly lifecycle : Lifecycle . State
260- readonly messageItems : Readonly < Record < string , MessageStreamItem > >
261- readonly messageContentIDs : ReadonlySet < string >
262- readonly nextMessageContentID : number
263240 readonly reasoningItems : Readonly < Record < string , ReasoningStreamItem > >
264241 readonly store : boolean | undefined
265242}
266243
267- interface MessageStreamItem {
268- readonly providerMetadata ?: ProviderMetadata
269- readonly content : Readonly < Record < number , { readonly id : string ; readonly text : string } > >
270- }
271-
272244type ReasoningSummaryStatus = "active" | "can-conclude" | "concluded"
273245
274246interface ReasoningStreamItem {
@@ -326,26 +298,6 @@ const lowerReasoning = (part: ReasoningPart): OpenAIResponsesReasoningInput | un
326298 }
327299}
328300
329- const messagePhase = ( part : TextPart ) : OpenAIResponsesMessagePhase | null | undefined => {
330- const phase = part . providerMetadata ?. openai ?. phase
331- return phase === "commentary" || phase === "final_answer" || phase === null ? phase : undefined
332- }
333-
334- const messageItemID = ( part : TextPart ) => {
335- const itemID = part . providerMetadata ?. openai ?. itemId
336- return typeof itemID === "string" && itemID . length > 0 ? itemID : undefined
337- }
338-
339- const messageStatus = ( part : TextPart ) => {
340- const status = part . providerMetadata ?. openai ?. status
341- return status === "in_progress" || status === "completed" || status === "incomplete" ? status : undefined
342- }
343-
344- const messageAnnotations = ( part : TextPart ) => {
345- const annotations = part . providerMetadata ?. openai ?. annotations
346- return Array . isArray ( annotations ) ? annotations : [ ]
347- }
348-
349301const hostedToolItemID = ( part : ToolResultPart ) => {
350302 const openai = part . providerMetadata ?. openai
351303 return ProviderShared . isRecord ( openai ) && typeof openai . itemId === "string" && openai . itemId . length > 0
@@ -416,49 +368,17 @@ const lowerMessages = Effect.fn("OpenAIResponses.lowerMessages")(function* (requ
416368 }
417369
418370 if ( message . role === "assistant" ) {
419- const inputStart = input . length
420371 const content : TextPart [ ] = [ ]
421- let phase : OpenAIResponsesMessagePhase | null | undefined
422- let itemID : string | undefined
423- let status : "in_progress" | "completed" | "incomplete" | undefined
424372 const reasoningItems : Record < string , OpenAIResponsesReasoningReplay > = { }
425373 const reasoningReferences = new Set < string > ( )
426374 const hostedToolReferences = new Set < string > ( )
427375 const flushText = ( ) => {
428376 if ( content . length === 0 ) return
429- input . push (
430- itemID
431- ? {
432- type : "message" ,
433- id : itemID ,
434- status : status ?? "completed" ,
435- role : "assistant" ,
436- content : content . map ( ( part ) => ( {
437- type : "output_text" ,
438- text : part . text ,
439- annotations : messageAnnotations ( part ) ,
440- } ) ) ,
441- ...( phase !== undefined ? { phase } : { } ) ,
442- }
443- : {
444- role : "assistant" ,
445- content : ProviderShared . joinText ( content ) ,
446- ...( phase !== undefined ? { phase } : { } ) ,
447- } ,
448- )
377+ input . push ( { role : "assistant" , content : content . map ( ( part ) => ( { type : "output_text" , text : part . text } ) ) } )
449378 content . splice ( 0 , content . length )
450- phase = undefined
451- itemID = undefined
452- status = undefined
453379 }
454380 for ( const part of message . content ) {
455381 if ( part . type === "text" ) {
456- const nextPhase = messagePhase ( part )
457- const nextItemID = messageItemID ( part )
458- if ( content . length > 0 && ( phase !== nextPhase || itemID !== nextItemID ) ) flushText ( )
459- phase = nextPhase
460- itemID = nextItemID
461- status = messageStatus ( part ) ?? status
462382 content . push ( part )
463383 continue
464384 }
@@ -509,20 +429,6 @@ const lowerMessages = Effect.fn("OpenAIResponses.lowerMessages")(function* (requ
509429 ] )
510430 }
511431 flushText ( )
512- if ( store === false && Object . values ( reasoningItems ) . some ( ( item ) => typeof item . encrypted_content !== "string" ) )
513- input . splice (
514- inputStart ,
515- input . length - inputStart ,
516- ...input . slice ( inputStart ) . map ( ( item ) =>
517- "type" in item && item . type === "message"
518- ? {
519- role : "assistant" as const ,
520- content : ProviderShared . joinText ( item . content ) ,
521- ...( item . phase !== undefined ? { phase : item . phase } : { } ) ,
522- }
523- : item ,
524- ) ,
525- )
526432 continue
527433 }
528434
@@ -706,134 +612,15 @@ const NO_EVENTS: StepResult["1"] = []
706612// the protocol's `terminal` predicate stay in sync.
707613const TERMINAL_TYPES = new Set ( [ "response.completed" , "response.incomplete" , "response.failed" ] )
708614
709- const messageMetadata = ( item : OpenAIResponsesStreamItem , id : string , previous ?: ProviderMetadata ) => {
710- const openai = previous ?. openai
711- const phase = item . phase !== undefined ? item . phase : openai ?. phase
712- const status =
713- item . status === "in_progress" || item . status === "completed" || item . status === "incomplete"
714- ? item . status
715- : openai ?. status
716- return openaiMetadata ( {
717- itemId : id ,
718- ...( phase === "commentary" || phase === "final_answer" || phase === null ? { phase } : { } ) ,
719- ...( status === "in_progress" || status === "completed" || status === "incomplete" ? { status } : { } ) ,
720- } )
721- }
722-
723- const messageContentMetadata = (
724- providerMetadata : ProviderMetadata ,
725- item : OpenAIResponsesStreamItem ,
726- index : number ,
727- ) : ProviderMetadata => {
728- const content = item . content ?. [ index ]
729- if ( ! ProviderShared . isRecord ( content ) || content . type !== "output_text" || ! Array . isArray ( content . annotations ) )
730- return providerMetadata
731- return openaiMetadata ( { ...providerMetadata . openai , annotations : content . annotations } )
732- }
733-
734- const ensureMessageContent = ( state : ParserState , event : OpenAIResponsesEvent ) => {
735- const itemID = event . item_id ?? "text-0"
736- const index = event . content_index ?? 0
737- const item = state . messageItems [ itemID ] ?? { content : { } }
738- const existing = item . content [ index ]
739- if ( existing ) return { state, itemID, index, item, content : existing }
740- const findID = ( next : number ) : readonly [ string , number ] => {
741- const id = `openai-text-${ next } `
742- return state . messageContentIDs . has ( id ) ? findID ( next + 1 ) : [ id , next + 1 ]
743- }
744- const [ id , nextMessageContentID ] =
745- index === 0 && ! state . messageContentIDs . has ( itemID )
746- ? ( [ itemID , state . nextMessageContentID ] as const )
747- : findID ( state . nextMessageContentID )
748- const content = { id, text : "" }
749- const nextItem = { ...item , content : { ...item . content , [ index ] : content } }
750- return {
751- state : {
752- ...state ,
753- messageItems : { ...state . messageItems , [ itemID ] : nextItem } ,
754- messageContentIDs : new Set ( [ ...state . messageContentIDs , id ] ) ,
755- nextMessageContentID,
756- } ,
757- itemID,
758- index,
759- item : nextItem ,
760- content,
761- }
762- }
763-
764- const updateMessageContent = (
765- state : ParserState ,
766- itemID : string ,
767- index : number ,
768- content : { readonly id : string ; readonly text : string } ,
769- ) : ParserState => ( {
770- ...state ,
771- messageItems : {
772- ...state . messageItems ,
773- [ itemID ] : {
774- ...state . messageItems [ itemID ] ,
775- content : { ...state . messageItems [ itemID ] ?. content , [ index ] : content } ,
776- } ,
777- } ,
778- } )
779-
780- const closeOtherMessageContent = ( state : ParserState , events : LLMEvent [ ] , item : MessageStreamItem , index : number ) =>
781- Object . entries ( item . content ) . reduce (
782- ( lifecycle , entry ) =>
783- Number ( entry [ 0 ] ) === index ? lifecycle : Lifecycle . textEnd ( lifecycle , events , entry [ 1 ] . id , item . providerMetadata ) ,
784- state . lifecycle ,
785- )
786-
787- const appendOutputText = ( state : ParserState , event : OpenAIResponsesEvent , text : string ) : StepResult => {
788- const ensured = ensureMessageContent ( state , event )
615+ const onOutputTextDelta = ( state : ParserState , event : OpenAIResponsesEvent ) : StepResult => {
616+ if ( ! event . delta ) return [ state , NO_EVENTS ]
789617 const events : LLMEvent [ ] = [ ]
790- const lifecycle = Lifecycle . textStart (
791- closeOtherMessageContent ( ensured . state , events , ensured . item , ensured . index ) ,
792- events ,
793- ensured . content . id ,
794- ensured . item . providerMetadata ,
795- )
796618 return [
797- {
798- ...updateMessageContent ( ensured . state , ensured . itemID , ensured . index , {
799- ...ensured . content ,
800- text : ensured . content . text + text ,
801- } ) ,
802- lifecycle : Lifecycle . textDelta ( lifecycle , events , ensured . content . id , text ) ,
803- } ,
619+ { ...state , lifecycle : Lifecycle . textDelta ( state . lifecycle , events , event . item_id ?? "text-0" , event . delta ) } ,
804620 events ,
805621 ]
806622}
807623
808- const onOutputTextDelta = ( state : ParserState , event : OpenAIResponsesEvent ) : StepResult => {
809- if ( ! event . delta ) return [ state , NO_EVENTS ]
810- return appendOutputText ( state , event , event . delta )
811- }
812-
813- const onOutputTextDone = ( state : ParserState , event : OpenAIResponsesEvent ) : StepResult => {
814- if ( event . text === undefined ) return [ state , NO_EVENTS ]
815- const ensured = ensureMessageContent ( state , event )
816- if ( event . text === ensured . content . text ) {
817- if ( ensured . state . lifecycle . text . has ( ensured . content . id ) ) return [ ensured . state , NO_EVENTS ]
818- const events : LLMEvent [ ] = [ ]
819- return [
820- {
821- ...ensured . state ,
822- lifecycle : Lifecycle . textStart (
823- closeOtherMessageContent ( ensured . state , events , ensured . item , ensured . index ) ,
824- events ,
825- ensured . content . id ,
826- ensured . item . providerMetadata ,
827- ) ,
828- } ,
829- events ,
830- ]
831- }
832- if ( event . text . startsWith ( ensured . content . text ) )
833- return appendOutputText ( ensured . state , event , event . text . slice ( ensured . content . text . length ) )
834- return [ ensured . state , NO_EVENTS ]
835- }
836-
837624const onReasoningDelta = ( state : ParserState , event : OpenAIResponsesEvent ) : StepResult => {
838625 if ( ! event . delta ) return [ state , NO_EVENTS ]
839626 const events : LLMEvent [ ] = [ ]
@@ -868,23 +655,6 @@ const reasoningMetadata = (item: OpenAIResponsesStreamItem & { id: string }) =>
868655// best-effort, not guaranteed.
869656const onOutputItemAdded = ( state : ParserState , event : OpenAIResponsesEvent ) : StepResult => {
870657 const item = event . item
871- if ( item ?. type === "message" && item . id ) {
872- const existing = state . messageItems [ item . id ]
873- return [
874- {
875- ...state ,
876- messageItems : {
877- ...state . messageItems ,
878- [ item . id ] : {
879- ...existing ,
880- providerMetadata : messageMetadata ( item , item . id , existing ?. providerMetadata ) ,
881- content : existing ?. content ?? { } ,
882- } ,
883- } ,
884- } ,
885- NO_EVENTS ,
886- ]
887- }
888658 if ( item && isReasoningItem ( item ) ) {
889659 const events : LLMEvent [ ] = [ ]
890660 return [
@@ -1042,32 +812,6 @@ const onOutputItemDone = Effect.fn("OpenAIResponses.onOutputItemDone")(function*
1042812 const item = event . item
1043813 if ( ! item ) return [ state , NO_EVENTS ] satisfies StepResult
1044814
1045- if ( item . type === "message" && item . id ) {
1046- const events : LLMEvent [ ] = [ ]
1047- const itemID = item . id
1048- const messageItem = state . messageItems [ itemID ]
1049- const { [ itemID ] : _finished , ...messageItems } = state . messageItems
1050- const providerMetadata = messageMetadata ( item , itemID , messageItem ?. providerMetadata )
1051- const lifecycle = Object . entries ( messageItem ?. content ?? { } ) . reduce (
1052- ( lifecycle , entry ) =>
1053- Lifecycle . textEnd (
1054- lifecycle ,
1055- events ,
1056- entry [ 1 ] . id ,
1057- messageContentMetadata ( providerMetadata , item , Number ( entry [ 0 ] ) ) ,
1058- ) ,
1059- state . lifecycle ,
1060- )
1061- return [
1062- {
1063- ...state ,
1064- lifecycle,
1065- messageItems,
1066- } ,
1067- events ,
1068- ] satisfies StepResult
1069- }
1070-
1071815 if ( item . type === "function_call" ) {
1072816 if ( ! item . id || ! item . call_id || ! item . name ) return [ state , NO_EVENTS ] satisfies StepResult
1073817 const tools = state . tools [ item . id ]
@@ -1195,7 +939,6 @@ const step = (state: ParserState, event: OpenAIResponsesEvent) => {
1195939 if ( event . type === "response.reasoning_summary_part.done" )
1196940 return Effect . succeed ( onReasoningSummaryPartDone ( state , event ) )
1197941 if ( event . type === "response.output_item.added" ) return Effect . succeed ( onOutputItemAdded ( state , event ) )
1198- if ( event . type === "response.output_text.done" ) return Effect . succeed ( onOutputTextDone ( state , event ) )
1199942 if ( event . type === "response.function_call_arguments.delta" ) return onFunctionCallArgumentsDelta ( state , event )
1200943 if ( event . type === "response.output_item.done" ) return onOutputItemDone ( state , event )
1201944 if ( event . type === "response.completed" || event . type === "response.incomplete" )
@@ -1225,9 +968,6 @@ export const protocol = Protocol.make({
1225968 hasFunctionCall : false ,
1226969 tools : ToolStream . empty < string > ( ) ,
1227970 lifecycle : Lifecycle . initial ( ) ,
1228- messageItems : { } ,
1229- messageContentIDs : new Set < string > ( ) ,
1230- nextMessageContentID : 0 ,
1231971 reasoningItems : { } ,
1232972 store : OpenAIOptions . store ( request ) ,
1233973 } ) ,
0 commit comments