mirror of
https://github.com/garrytan/gbrain.git
synced 2026-08-14 00:48:18 +00:00
fix(takes): emit JSON for page extraction (#4004)
Fixes #3962 Return the structured extraction result for --json callers while preserving the existing human summary. Add a behavior-level regression test that proves stdout is parseable JSON.
This commit is contained in:
+11
-2
@@ -624,12 +624,13 @@ async function cmdExtract(engine: BrainEngine, rest: string[]): Promise<void> {
|
||||
const sub = rest[0];
|
||||
if (sub !== '--from-pages') {
|
||||
process.stderr.write(
|
||||
'Usage: gbrain takes extract --from-pages [--yes] [--dry-run] [--source-id <id>] [--max-pages N (clamped to 1000)] [--include-covered] [--holder <name>]\n' +
|
||||
'Usage: gbrain takes extract --from-pages [--yes] [--dry-run] [--json] [--source-id <id>] [--max-pages N (clamped to 1000)] [--include-covered] [--holder <name>]\n' +
|
||||
'Runs progress: pages that already hold takes are skipped, so repeat runs sweep a large corpus in slices. --include-covered rescans everything (refresh).\n',
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
const dryRun = rest.includes('--dry-run');
|
||||
const json = rest.includes('--json');
|
||||
const skipConfirm = rest.includes('--yes');
|
||||
const sourceIdx = rest.indexOf('--source-id');
|
||||
const sourceIdFilter = sourceIdx >= 0 ? rest[sourceIdx + 1] : undefined;
|
||||
@@ -667,9 +668,17 @@ async function cmdExtract(engine: BrainEngine, rest: string[]): Promise<void> {
|
||||
holder,
|
||||
});
|
||||
if (result.llm_unavailable) {
|
||||
process.stderr.write(`[takes extract] chat gateway unavailable (no API key configured).\n`);
|
||||
if (json) {
|
||||
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
||||
} else {
|
||||
process.stderr.write(`[takes extract] chat gateway unavailable (no API key configured).\n`);
|
||||
}
|
||||
process.exit(2);
|
||||
}
|
||||
if (json) {
|
||||
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
||||
return;
|
||||
}
|
||||
process.stdout.write(
|
||||
`takes extract --from-pages: ${result.claims_extracted} claim(s) from ${result.pages_scanned} page(s)` +
|
||||
(dryRun ? ' (dry-run)' : '') + '\n',
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* #3962 — `takes extract --from-pages --json` must emit the structured
|
||||
* extraction result instead of the human summary line.
|
||||
*/
|
||||
import { afterAll, beforeAll, describe, expect, test } from 'bun:test';
|
||||
import { runTakes } from '../src/commands/takes.ts';
|
||||
import {
|
||||
configureGateway,
|
||||
resetGateway,
|
||||
} from '../src/core/ai/gateway.ts';
|
||||
import type { BrainEngine } from '../src/core/engine.ts';
|
||||
|
||||
const engine = {
|
||||
getConfig: async (key: string) => key === 'takes.bootstrap_enabled' ? 'true' : null,
|
||||
executeRaw: async () => [],
|
||||
} as unknown as BrainEngine;
|
||||
|
||||
async function captureStdout(fn: () => Promise<void>): Promise<string> {
|
||||
const chunks: string[] = [];
|
||||
const originalWrite = process.stdout.write;
|
||||
process.stdout.write = ((chunk: string | Uint8Array) => {
|
||||
chunks.push(String(chunk));
|
||||
return true;
|
||||
}) as typeof process.stdout.write;
|
||||
try {
|
||||
await fn();
|
||||
} finally {
|
||||
process.stdout.write = originalWrite;
|
||||
}
|
||||
return chunks.join('');
|
||||
}
|
||||
|
||||
beforeAll(() => {
|
||||
configureGateway({
|
||||
chat_model: 'openai:gpt-test',
|
||||
env: { OPENAI_API_KEY: 'sk-test-takes-json' },
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
resetGateway();
|
||||
});
|
||||
|
||||
describe('gbrain takes extract --from-pages --json (#3962)', () => {
|
||||
test('emits the extraction result as parseable JSON', async () => {
|
||||
const stdout = await captureStdout(() =>
|
||||
runTakes(engine, ['extract', '--from-pages', '--dry-run', '--json']));
|
||||
|
||||
expect(JSON.parse(stdout)).toEqual({
|
||||
pages_scanned: 0,
|
||||
claims_extracted: 0,
|
||||
consent_gate_blocked: false,
|
||||
llm_unavailable: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user