@@ -74,17 +74,42 @@ export function failedImportDocs(response: RagIndexJobStatusResponse): RagIndexJ
7474 ) ;
7575}
7676
77- /** Per-document failure summary: server message passed through verbatim + per-document detail */
77+ /**
78+ * Whether every document has reached a terminal state (FINISH or *FAILED).
79+ * Used to stop polling before ingestion_status becomes COMPLETED — the server
80+ * may leave the job on RUNNING indefinitely even after all documents finish.
81+ */
82+ function allDocsTerminal ( response : RagIndexJobStatusResponse ) : boolean {
83+ const rows = response . data ?. rows ?? [ ] ;
84+ if ( rows . length === 0 ) return false ;
85+ const totalCount = response . data ?. total_count ;
86+ if ( typeof totalCount === "number" && rows . length < totalCount ) return false ;
87+ return rows . every ( ( doc ) => {
88+ const code = doc . code ?? doc . status ?? "" ;
89+ return code === "FINISH" || code . includes ( "FAILED" ) ;
90+ } ) ;
91+ }
92+
93+ /** Per-document failure summary: lists both failed and succeeded documents so the user knows the full picture. */
7894export function importJobFailureMessage (
7995 response : RagIndexJobStatusResponse ,
8096 fallbackMessage : string ,
8197) : string {
82- const detail = failedImportDocs ( response )
98+ const rows = response . data ?. rows ?? [ ] ;
99+ const failed = failedImportDocs ( response ) ;
100+ const failedIds = new Set ( failed . map ( ( doc ) => doc . doc_id ) ) ;
101+ const succeeded = rows . filter ( ( doc ) => ! failedIds . has ( doc . doc_id ) ) ;
102+
103+ const failedDetail = failed
83104 . map ( ( doc ) => `${ doc . doc_name ?? doc . doc_id ?? "?" } : ${ doc . message ?? doc . code ?? "unknown" } ` )
84105 . join ( "; " ) ;
85- const base =
86- typeof response . message === "string" && response . message ? response . message : fallbackMessage ;
87- return detail ? `${ base } (${ detail } )` : base ;
106+ const succeededDetail = succeeded . map ( ( doc ) => doc . doc_name ?? doc . doc_id ?? "?" ) . join ( ", " ) ;
107+
108+ const parts : string [ ] = [ ] ;
109+ if ( failedDetail ) parts . push ( `failed: ${ failedDetail } ` ) ;
110+ if ( succeededDetail ) parts . push ( `succeeded: ${ succeededDetail } ` ) ;
111+
112+ return parts . length > 0 ? `${ fallbackMessage } (${ parts . join ( "; " ) } )` : fallbackMessage ;
88113}
89114
90115/** Build the index_job/status query string (both index_id and job_id are required) */
@@ -96,10 +121,13 @@ export function importJobStatusUrl(workspaceId: string, indexId: string, jobId:
96121}
97122
98123/**
99- * Poll the import job until the overall state is COMPLETED.
100- * The overall state has no FAILED value, so isFailed is always false — failures
101- * are determined by the caller after return via `failedImportDocs` (semantics:
102- * the job finished, but some documents failed to parse).
124+ * Poll the import job until the overall state is COMPLETED or every document
125+ * has reached a terminal state (FINISH or FAILED). The overall state has no
126+ * FAILED value, so isFailed is always false — failures are determined by the
127+ * caller after return via `failedImportDocs`. The server may leave
128+ * ingestion_status on RUNNING indefinitely after documents finish, so checking
129+ * rows[] mid-poll avoids a misleading timeout; callers re-check failedImportDocs
130+ * after return, keeping the error path uniform.
103131 */
104132export async function pollImportJob (
105133 client : Client ,
@@ -110,9 +138,21 @@ export async function pollImportJob(
110138 url : options . statusUrl ,
111139 intervalSec : options . intervalSec ,
112140 timeoutSec : settings . timeout ,
113- isComplete : ( data ) => importJobStatus ( data ) === "COMPLETED" ,
141+ isComplete : ( data ) => {
142+ const response = data as RagIndexJobStatusResponse ;
143+ return importJobStatus ( response ) === "COMPLETED" || allDocsTerminal ( response ) ;
144+ } ,
114145 isFailed : ( ) => false ,
115- getStatus : ( data ) => importJobStatus ( data ) ,
146+ getStatus : ( data ) => {
147+ const response = data as RagIndexJobStatusResponse ;
148+ const status = importJobStatus ( response ) ;
149+ const failedCount = failedImportDocs ( response ) . length ;
150+ const total = response . data ?. total_count ;
151+ if ( typeof total === "number" && total > 0 ) {
152+ return `${ status } (${ failedCount } /${ total } failed)` ;
153+ }
154+ return failedCount > 0 ? `${ status } (${ failedCount } failed)` : status ;
155+ } ,
116156 } ) ;
117157}
118158
0 commit comments