Skip to content

Commit dea459a

Browse files
committed
fix: rewrite pollResearch response parsing for actual API format
The live API returns a triple-nested structure: [[[reportId, taskInfo, startTs, updateTs]]] where taskInfo = [notebookId, [query, srcType], mode, sources, statusCode, researchMeta] The old parsing expected a different nesting and couldn't find the task data, always returning 'no_research'. Now correctly unwraps the triple-nesting and extracts statusCode from taskInfo[4] (1=in_progress, 2=completed) and taskId from researchMeta[0].
1 parent ac794b8 commit dea459a

File tree

1 file changed

+84
-71
lines changed

1 file changed

+84
-71
lines changed

lib/services/notebooklm/client.ts

Lines changed: 84 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -395,87 +395,100 @@ export class NotebookLMClient {
395395
return emptyResult;
396396
}
397397

398-
// Response format (from notebooklm-py _research.py):
399-
// result = [[task_data, ...], ...] or [task_data, ...]
400-
// task_data = [task_id, task_info]
401-
// task_info = [?, query_info, ?, sources_and_summary, status_code]
402-
// query_info = [query_text, ...]
403-
// sources_and_summary = [sources_array, summary_text]
404-
// Research status: 1=in_progress, 2=completed
398+
// Response format (from live API debugging):
399+
// result = [[[reportId, taskInfo, startTimestamp, updateTimestamp]]]
400+
// taskInfo = [notebookId, [query, sourceType], mode, null, statusCode, researchMeta]
401+
// researchMeta = [taskId, null, ?, null, "deep_research.v3p1s.prod"]
402+
// statusCode: 1=in_progress, 2=completed
403+
//
404+
// When completed, taskInfo has additional data:
405+
// taskInfo = [notebookId, [query, sourceType], mode, sourcesAndSummary, statusCode, researchMeta]
406+
// sourcesAndSummary = [sourcesArray, summaryText]
407+
408+
// Unwrap the triple-nesting to get to the task array
409+
// result = [[[...]]] → task = result[0][0]
410+
let task: unknown[] | null = null;
405411

406-
// Unwrap if double-nested
407-
let tasks = resultArr;
408412
if (
409-
Array.isArray(tasks[0]) &&
410-
tasks[0].length > 0 &&
411-
Array.isArray(tasks[0][0]) &&
412-
tasks[0][0].length > 0 &&
413-
Array.isArray(tasks[0][0][0])
414-
) {
415-
tasks = tasks[0] as unknown[];
416-
} else if (
417-
Array.isArray(tasks[0]) &&
418-
tasks[0].length > 0 &&
419-
typeof tasks[0][0] === 'string'
413+
Array.isArray(resultArr[0]) &&
414+
Array.isArray((resultArr[0] as unknown[])[0])
420415
) {
421-
// Already at task level: [[taskId, taskInfo], ...]
422-
// wrap in array for uniform processing
423-
tasks = [tasks];
416+
const inner = (resultArr[0] as unknown[])[0] as unknown[];
417+
if (inner.length >= 2 && typeof inner[0] === 'string') {
418+
task = inner;
419+
}
424420
}
425421

426-
// Find the most recent task
427-
for (const taskData of tasks) {
428-
if (!Array.isArray(taskData) || taskData.length < 2) continue;
429-
430-
const taskId = taskData[0];
431-
const taskInfo = taskData[1];
432-
433-
if (typeof taskId !== 'string' || !Array.isArray(taskInfo)) continue;
434-
435-
const queryInfo = taskInfo[1];
436-
const sourcesAndSummary = taskInfo[3];
437-
const statusCode = taskInfo[4];
438-
439-
const queryText =
440-
Array.isArray(queryInfo) && typeof queryInfo[0] === 'string'
441-
? queryInfo[0]
442-
: '';
443-
444-
// Parse sources
445-
const sources: Array<{ url: string; title: string }> = [];
446-
let summary = '';
447-
448-
if (Array.isArray(sourcesAndSummary) && sourcesAndSummary.length >= 1) {
449-
const sourcesData = Array.isArray(sourcesAndSummary[0])
450-
? sourcesAndSummary[0]
451-
: [];
452-
if (
453-
sourcesAndSummary.length >= 2 &&
454-
typeof sourcesAndSummary[1] === 'string'
455-
) {
456-
summary = sourcesAndSummary[1];
457-
}
458-
459-
for (const src of sourcesData) {
460-
if (!Array.isArray(src) || src.length < 2) continue;
461-
const url =
462-
typeof src[0] === 'string' ? src[0] : '';
463-
const title =
464-
typeof src[1] === 'string' ? src[1] : '';
465-
if (title || url) {
466-
sources.push({ url, title });
467-
}
468-
}
422+
if (!task) {
423+
// Try single-nested: result = [[taskId, taskInfo, ...]]
424+
if (
425+
Array.isArray(resultArr[0]) &&
426+
typeof (resultArr[0] as unknown[])[0] === 'string'
427+
) {
428+
task = resultArr[0] as unknown[];
469429
}
430+
}
431+
432+
if (!task || task.length < 2) {
433+
return emptyResult;
434+
}
470435

471-
// Research status: 1=in_progress, 2=completed
472-
const status: ResearchResult['status'] =
473-
statusCode === 2 ? 'completed' : 'in_progress';
436+
const reportId = task[0] as string;
437+
const taskInfo = task[1] as unknown[];
474438

475-
return { taskId, status, query: queryText, sources, summary };
439+
if (!Array.isArray(taskInfo) || taskInfo.length < 5) {
440+
return emptyResult;
441+
}
442+
443+
// taskInfo = [notebookId, [query, sourceType], mode, sourcesAndSummary, statusCode, researchMeta]
444+
const queryInfo = taskInfo[1];
445+
const sourcesAndSummary = taskInfo[3];
446+
const statusCode = taskInfo[4];
447+
const researchMeta = taskInfo[5];
448+
449+
const queryText =
450+
Array.isArray(queryInfo) && typeof queryInfo[0] === 'string'
451+
? queryInfo[0]
452+
: '';
453+
454+
// Extract the research task ID from researchMeta
455+
let taskId = reportId; // fallback to reportId
456+
if (Array.isArray(researchMeta) && typeof researchMeta[0] === 'string') {
457+
taskId = researchMeta[0];
476458
}
477459

478-
return emptyResult;
460+
// Parse sources
461+
const sources: Array<{ url: string; title: string }> = [];
462+
let summary = '';
463+
464+
if (Array.isArray(sourcesAndSummary) && sourcesAndSummary.length >= 1) {
465+
const sourcesData = Array.isArray(sourcesAndSummary[0])
466+
? sourcesAndSummary[0]
467+
: [];
468+
if (
469+
sourcesAndSummary.length >= 2 &&
470+
typeof sourcesAndSummary[1] === 'string'
471+
) {
472+
summary = sourcesAndSummary[1];
473+
}
474+
475+
for (const src of sourcesData) {
476+
if (!Array.isArray(src) || src.length < 2) continue;
477+
const url =
478+
typeof src[0] === 'string' ? src[0] : '';
479+
const title =
480+
typeof src[1] === 'string' ? src[1] : '';
481+
if (title || url) {
482+
sources.push({ url, title });
483+
}
484+
}
485+
}
486+
487+
// Research status: 1=in_progress, 2=completed
488+
const status: ResearchResult['status'] =
489+
statusCode === 2 ? 'completed' : 'in_progress';
490+
491+
return { taskId, status, query: queryText, sources, summary };
479492
}
480493

481494
/**

0 commit comments

Comments
 (0)