@@ -9,6 +9,10 @@ import { ResponseInputItem, ResponseOutputText, ResponseStreamEvent, Tool } from
99import { Stream } from 'openai/core/streaming' ;
1010import { AzureOpenAI } from 'openai' ;
1111
12+ type Citation = ResponseOutputText . ContainerFileCitation | ResponseOutputText . URLCitation | ResponseOutputText . FileCitation | ResponseOutputText . FilePath ;
13+ type CitationWithBounds = ResponseOutputText . ContainerFileCitation | ResponseOutputText . URLCitation ;
14+ type CitationWithFileId = ResponseOutputText . FileCitation | ResponseOutputText . FilePath ;
15+
1216export class OpenAIError extends Error {
1317 type : string ;
1418 param : string ;
@@ -23,12 +27,12 @@ export class OpenAIError extends Error {
2327 }
2428}
2529
26- const replaceUrlCitations = ( text : string , annotations : any [ ] ) => {
27- if ( annotations ) {
28- const urlCitations = annotations . filter ( ann => ann . type === 'url_citation' ) as ResponseOutputText . URLCitation [ ] ;
29- if ( urlCitations . length > 0 ) {
30- let result = text . substring ( 0 , urlCitations [ 0 ] . start_index - 1 ) ;
31- urlCitations . forEach ( ( ann ) => {
30+ const replaceCitations = ( text : string , annotations : Citation [ ] ) => {
31+ const citations = annotations . filter ( ann => ann . type === 'container_file_citation' || ann . type === 'url_citation' ) as CitationWithBounds [ ] ;
32+ if ( citations ) {
33+ if ( citations . length > 0 ) {
34+ let result = text . substring ( 0 , citations [ 0 ] . start_index - 1 ) ;
35+ citations . forEach ( ( ann ) => {
3236 result += `\n\n* ${ text . substring ( ann . start_index , ann . end_index ) } ` ;
3337 } ) ;
3438
@@ -39,10 +43,12 @@ const replaceUrlCitations = (text: string, annotations: any[]) => {
3943 return text ;
4044}
4145
42- function replaceFileCitations ( text : string , annotations ?: any [ ] , fileIdNameMap ?: Record < string , string > ) : string {
46+ function replaceFileCitations ( text : string , annotations : Citation [ ] , fileIdNameMap ?: Record < string , string > ) : string {
47+ const citations = annotations . filter ( ann => ann . type === 'file_citation' || ann . type === 'file_path' ) as CitationWithFileId [ ] ;
48+
4349 //Support "fileciteturn0file12" style citations in GPT-5
4450 if ( ! fileIdNameMap ) return text ;
45- if ( ( ! annotations || annotations . length === 0 ) && text . indexOf ( "" ) == - 1 ) return text ;
51+ if ( ( ! citations || citations . length === 0 ) && text . indexOf ( "" ) == - 1 ) return text ;
4652 let result = text ;
4753 let loopBreaker = 10 ; // Prevent infinite loops
4854 while ( / [ \uE200 - \uE210 ] / u. test ( result ) && -- loopBreaker > 0 ) {
@@ -53,16 +59,14 @@ function replaceFileCitations(text: string, annotations?: any[], fileIdNameMap?:
5359 return "【5:" + p2 + "†" + realFilename + "】" ;
5460 } ) ;
5561 }
56- if ( annotations ) {
57- for ( const ann of annotations ) {
58- if ( ann . type === 'file_citation' && ann . file_id ) {
59- const citationRegex = / 【 * † ( .+ ?) 】 / g;
62+ if ( citations ) {
63+ for ( const ann of citations ) {
64+ const citationRegex = / 【 * † ( .+ ?) 】 / g;
6065 result = result . replace ( citationRegex , ( match , filename ) => {
6166 const fileId = ann . file_id ;
6267 const realFilename = fileIdNameMap [ fileId ] || filename ;
6368 return match . replace ( filename , realFilename ) ;
6469 } ) ;
65- }
6670 }
6771 }
6872 return result ;
@@ -175,7 +179,7 @@ export const OpenAIStream = async (conversation: Conversation, userName: string,
175179
176180 case 'response.content_part.done' :
177181 if ( chunk . part . type === 'output_text' ) {
178- text = replaceUrlCitations ( chunk . part . text , chunk . part . annotations ) ;
182+ text = replaceCitations ( chunk . part . text , chunk . part . annotations ) ;
179183 text = replaceFileCitations ( text || '' , chunk . part . annotations , fileIdNameMap ) ;
180184 }
181185
0 commit comments