1- import { LLMClient } from "../llm/client.js" ;
1+ import { LLMClient , LLMProgressEvent } from "../llm/client.js" ;
22import { AnalysisCorpusRow , ResolvedPaperSource , buildAbstractFallbackText } from "./paperText.js" ;
33import { ResponsesPdfAnalysisClient } from "../../integrations/openai/responsesPdfAnalysisClient.js" ;
44
@@ -71,23 +71,31 @@ export async function analyzePaperWithLlm(args: {
7171 paper : AnalysisCorpusRow ;
7272 source : ResolvedPaperSource ;
7373 maxAttempts ?: number ;
74+ onProgress ?: ( message : string ) => void ;
7475} ) : Promise < PaperAnalysisResult > {
7576 const maxAttempts = Math . max ( 1 , args . maxAttempts ?? 2 ) ;
7677 let lastError : Error | undefined ;
7778
7879 for ( let attempt = 1 ; attempt <= maxAttempts ; attempt += 1 ) {
7980 try {
81+ args . onProgress ?.( `Starting LLM analysis attempt ${ attempt } /${ maxAttempts } .` ) ;
8082 const completion = await args . llm . complete ( buildPaperAnalysisPrompt ( args . paper , args . source ) , {
81- systemPrompt : ANALYSIS_SYSTEM_PROMPT
83+ systemPrompt : ANALYSIS_SYSTEM_PROMPT ,
84+ onProgress : ( event ) => {
85+ emitLlmProgress ( args . onProgress , event ) ;
86+ }
8287 } ) ;
88+ args . onProgress ?.( "Received LLM output. Parsing structured JSON." ) ;
8389 const parsed = parsePaperAnalysisJson ( completion . text ) ;
90+ args . onProgress ?.( "Structured JSON parsed successfully." ) ;
8491 return {
8592 ...normalizePaperAnalysis ( args . paper , args . source , parsed ) ,
8693 attempts : attempt ,
8794 rawJson : parsed
8895 } ;
8996 } catch ( error ) {
9097 lastError = error instanceof Error ? error : new Error ( String ( error ) ) ;
98+ args . onProgress ?.( `Analysis attempt ${ attempt } /${ maxAttempts } failed: ${ lastError . message } ` ) ;
9199 }
92100 }
93101
@@ -101,6 +109,7 @@ export async function analyzePaperWithResponsesPdf(args: {
101109 model : string ;
102110 maxAttempts ?: number ;
103111 abortSignal ?: AbortSignal ;
112+ onProgress ?: ( message : string ) => void ;
104113} ) : Promise < PaperAnalysisResult > {
105114 const maxAttempts = Math . max ( 1 , args . maxAttempts ?? 2 ) ;
106115 let lastError : Error | undefined ;
@@ -113,27 +122,65 @@ export async function analyzePaperWithResponsesPdf(args: {
113122
114123 for ( let attempt = 1 ; attempt <= maxAttempts ; attempt += 1 ) {
115124 try {
125+ args . onProgress ?.( `Starting Responses API PDF analysis attempt ${ attempt } /${ maxAttempts } with model ${ args . model } .` ) ;
116126 const completion = await args . client . analyzePdf ( {
117127 model : args . model ,
118128 pdfUrl : args . pdfUrl ,
119129 prompt : buildPaperAnalysisFilePrompt ( args . paper ) ,
120130 systemPrompt : ANALYSIS_SYSTEM_PROMPT ,
121- abortSignal : args . abortSignal
131+ abortSignal : args . abortSignal ,
132+ onProgress : ( message ) => args . onProgress ?.( message )
122133 } ) ;
134+ args . onProgress ?.( "Received Responses API output. Parsing structured JSON." ) ;
123135 const parsed = parsePaperAnalysisJson ( completion . text ) ;
136+ args . onProgress ?.( "Structured JSON parsed successfully." ) ;
124137 return {
125138 ...normalizePaperAnalysis ( args . paper , sourceHint , parsed ) ,
126139 attempts : attempt ,
127140 rawJson : parsed
128141 } ;
129142 } catch ( error ) {
130143 lastError = error instanceof Error ? error : new Error ( String ( error ) ) ;
144+ args . onProgress ?.( `PDF analysis attempt ${ attempt } /${ maxAttempts } failed: ${ lastError . message } ` ) ;
131145 }
132146 }
133147
134148 throw lastError ?? new Error ( "paper_analysis_failed" ) ;
135149}
136150
151+ export function shouldFallbackResponsesPdfToLocalText ( error : unknown ) : boolean {
152+ const message = error instanceof Error ? error . message : String ( error ) ;
153+ return [
154+ / t i m e o u t w h i l e d o w n l o a d i n g / i,
155+ / f a i l e d t o d o w n l o a d / i,
156+ / u n a b l e t o d o w n l o a d / i,
157+ / u n a b l e t o f e t c h / i,
158+ / c o u l d n o t f e t c h / i,
159+ / f i l e _ u r l / i,
160+ / r e m o t e f i l e / i
161+ ] . some ( ( pattern ) => pattern . test ( message ) ) ;
162+ }
163+
164+ function emitLlmProgress (
165+ onProgress : ( ( message : string ) => void ) | undefined ,
166+ event : LLMProgressEvent
167+ ) : void {
168+ if ( ! onProgress ) {
169+ return ;
170+ }
171+ if ( event . type === "delta" ) {
172+ const text = event . text . trim ( ) ;
173+ if ( text ) {
174+ onProgress ( `LLM> ${ text } ` ) ;
175+ }
176+ return ;
177+ }
178+ const text = event . text . trim ( ) ;
179+ if ( text ) {
180+ onProgress ( text ) ;
181+ }
182+ }
183+
137184export function buildPaperAnalysisPrompt ( paper : AnalysisCorpusRow , source : ResolvedPaperSource ) : string {
138185 return [
139186 "Analyze the following paper and extract structured evidence." ,
0 commit comments