diff --git a/packages/cli/src/__snapshots__/nonInteractiveCli.test.ts.snap b/packages/cli/src/__snapshots__/nonInteractiveCli.test.ts.snap index dcf6268194..8c1a85cdd7 100644 --- a/packages/cli/src/__snapshots__/nonInteractiveCli.test.ts.snap +++ b/packages/cli/src/__snapshots__/nonInteractiveCli.test.ts.snap @@ -3,8 +3,8 @@ exports[`runNonInteractive > should emit appropriate error event in streaming JSON mode: 'loop detected' 1`] = ` "{"type":"init","timestamp":"","session_id":"test-session-id","model":"test-model"} {"type":"message","timestamp":"","role":"user","content":"Loop test"} -{"type":"loop_detected","timestamp":""} -{"type":"result","timestamp":"","status":"success","stats":{"total_tokens":0,"input_tokens":0,"output_tokens":0,"cached":0,"input":0,"duration_ms":,"tool_calls":0,"api_requests":0,"api_errors":0}} +{"type":"error","timestamp":"","severity":"warning","message":"Loop detected, stopping execution"} +{"type":"result","timestamp":"","status":"success","stats":{"total_tokens":0,"input_tokens":0,"output_tokens":0,"cached":0,"input":0,"duration_ms":,"tool_calls":0}} " `; @@ -12,7 +12,7 @@ exports[`runNonInteractive > should emit appropriate error event in streaming JS "{"type":"init","timestamp":"","session_id":"test-session-id","model":"test-model"} {"type":"message","timestamp":"","role":"user","content":"Max turns test"} {"type":"error","timestamp":"","severity":"error","message":"Maximum session turns exceeded"} -{"type":"result","timestamp":"","status":"success","stats":{"total_tokens":0,"input_tokens":0,"output_tokens":0,"cached":0,"input":0,"duration_ms":,"tool_calls":0,"api_requests":0,"api_errors":0}} +{"type":"result","timestamp":"","status":"success","stats":{"total_tokens":0,"input_tokens":0,"output_tokens":0,"cached":0,"input":0,"duration_ms":,"tool_calls":0}} " `; @@ -23,7 +23,7 @@ exports[`runNonInteractive > should emit appropriate events for streaming JSON o {"type":"tool_use","timestamp":"","tool_name":"testTool","tool_id":"tool-1","parameters":{"arg1":"value1"}} {"type":"tool_result","timestamp":"","tool_id":"tool-1","status":"success","output":"Tool executed successfully"} {"type":"message","timestamp":"","role":"assistant","content":"Final answer","delta":true} -{"type":"result","timestamp":"","status":"success","stats":{"total_tokens":0,"input_tokens":0,"output_tokens":0,"cached":0,"input":0,"duration_ms":,"tool_calls":0,"api_requests":0,"api_errors":0}} +{"type":"result","timestamp":"","status":"success","stats":{"total_tokens":0,"input_tokens":0,"output_tokens":0,"cached":0,"input":0,"duration_ms":,"tool_calls":0}} " `; diff --git a/packages/cli/src/nonInteractiveCli.ts b/packages/cli/src/nonInteractiveCli.ts index 097f7a22a5..669e945269 100644 --- a/packages/cli/src/nonInteractiveCli.ts +++ b/packages/cli/src/nonInteractiveCli.ts @@ -96,6 +96,7 @@ export async function runNonInteractive({ const startTime = Date.now(); let retryCount = 0; + const debugMode = config.getDebugMode(); const streamFormatter = config.getOutputFormat() === OutputFormat.STREAM_JSON ? new StreamJsonFormatter() @@ -185,6 +186,7 @@ export async function runNonInteractive({ const handleRetryAttempt = (payload: RetryAttemptPayload) => { retryCount++; + if (!debugMode) return; if (streamFormatter) { streamFormatter.emitEvent({ type: JsonStreamEventType.RETRY, @@ -252,8 +254,10 @@ export async function runNonInteractive({ } // Emit init event for streaming JSON - const authMethod = config.getContentGeneratorConfig()?.authType; - const userTier = config.getUserTierName(); + const authMethod = debugMode + ? config.getContentGeneratorConfig()?.authType + : undefined; + const userTier = debugMode ? config.getUserTierName() : undefined; if (streamFormatter) { streamFormatter.emitEvent({ type: JsonStreamEventType.INIT, @@ -373,18 +377,27 @@ export async function runNonInteractive({ } toolCallRequests.push(event.value); } else if (event.type === GeminiEventType.LoopDetected) { - const loopType = event.value?.loopType; - if (streamFormatter) { + if (debugMode) { + const loopType = event.value?.loopType; + if (streamFormatter) { + streamFormatter.emitEvent({ + type: JsonStreamEventType.LOOP_DETECTED, + timestamp: new Date().toISOString(), + ...(loopType && { loop_type: loopType }), + }); + } else if (config.getOutputFormat() === OutputFormat.TEXT) { + const loopTypeStr = loopType ? ` (${loopType})` : ''; + process.stderr.write( + `[WARNING] Loop detected${loopTypeStr}, stopping execution\n`, + ); + } + } else if (streamFormatter) { streamFormatter.emitEvent({ - type: JsonStreamEventType.LOOP_DETECTED, + type: JsonStreamEventType.ERROR, timestamp: new Date().toISOString(), - ...(loopType && { loop_type: loopType }), + severity: 'warning', + message: 'Loop detected, stopping execution', }); - } else if (config.getOutputFormat() === OutputFormat.TEXT) { - const loopTypeStr = loopType ? ` (${loopType})` : ''; - process.stderr.write( - `[WARNING] Loop detected${loopTypeStr}, stopping execution\n`, - ); } } else if (event.type === GeminiEventType.MaxSessionTurns) { if (streamFormatter) { @@ -413,7 +426,7 @@ export async function runNonInteractive({ stats: streamFormatter.convertToStreamStats( metrics, durationMs, - retryCount, + { retryCount, includeDiagnostics: debugMode }, ), }); } @@ -513,7 +526,7 @@ export async function runNonInteractive({ stats: streamFormatter.convertToStreamStats( metrics, durationMs, - retryCount, + { retryCount, includeDiagnostics: debugMode }, ), }); } else if (config.getOutputFormat() === OutputFormat.JSON) { @@ -545,11 +558,10 @@ export async function runNonInteractive({ type: JsonStreamEventType.RESULT, timestamp: new Date().toISOString(), status: 'success', - stats: streamFormatter.convertToStreamStats( - metrics, - durationMs, + stats: streamFormatter.convertToStreamStats(metrics, durationMs, { retryCount, - ), + includeDiagnostics: debugMode, + }), }); } else if (config.getOutputFormat() === OutputFormat.JSON) { const formatter = new JsonFormatter(); diff --git a/packages/core/src/output/stream-json-formatter.ts b/packages/core/src/output/stream-json-formatter.ts index 0029c8209a..fb57154f6d 100644 --- a/packages/core/src/output/stream-json-formatter.ts +++ b/packages/core/src/output/stream-json-formatter.ts @@ -39,25 +39,21 @@ export class StreamJsonFormatter { convertToStreamStats( metrics: SessionMetrics, durationMs: number, - retryCount?: number, + options?: { retryCount?: number; includeDiagnostics?: boolean }, ): StreamStats { let totalTokens = 0; let inputTokens = 0; let outputTokens = 0; let cached = 0; let input = 0; - let apiRequests = 0; - let apiErrors = 0; - // Aggregate token counts and API stats across all models + // Aggregate token counts across all models for (const modelMetrics of Object.values(metrics.models)) { totalTokens += modelMetrics.tokens.total; inputTokens += modelMetrics.tokens.prompt; outputTokens += modelMetrics.tokens.candidates; cached += modelMetrics.tokens.cached; input += modelMetrics.tokens.input; - apiRequests += modelMetrics.api.totalRequests; - apiErrors += modelMetrics.api.totalErrors; } const stats: StreamStats = { @@ -68,12 +64,21 @@ export class StreamJsonFormatter { input, duration_ms: durationMs, tool_calls: metrics.tools.totalCalls, - api_requests: apiRequests, - api_errors: apiErrors, }; - if (retryCount && retryCount > 0) { - stats.retry_count = retryCount; + if (options?.includeDiagnostics) { + let apiRequests = 0; + let apiErrors = 0; + for (const modelMetrics of Object.values(metrics.models)) { + apiRequests += modelMetrics.api.totalRequests; + apiErrors += modelMetrics.api.totalErrors; + } + stats.api_requests = apiRequests; + stats.api_errors = apiErrors; + + if (options.retryCount && options.retryCount > 0) { + stats.retry_count = options.retryCount; + } } return stats; diff --git a/scripts/test_gemini.sh b/scripts/test_gemini.sh index eaff8d6399..dc562101cf 100755 --- a/scripts/test_gemini.sh +++ b/scripts/test_gemini.sh @@ -96,8 +96,8 @@ for model in "${MODELS[@]}"; do STDERRFILE=$(mktemp) exit_code=0 - echo -e " ${DIM}Running with -o stream-json ...${RESET}" - node "$CLI" -p "$PROMPT" -y -m "$model" -o stream-json \ + echo -e " ${DIM}Running with -o stream-json -d ...${RESET}" + node "$CLI" -p "$PROMPT" -y -m "$model" -o stream-json -d \ >"$TMPFILE" 2>"$STDERRFILE" || exit_code=$? if [[ $exit_code -ne 0 ]]; then