Enable --enable-api-proxy flag for Gemini engine and add parse_gemini_log.cjs#17245
Conversation
…_log.cjs Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
|
@copilot output format should be streaming JSON like claude |
…eps and add to TestSupportsLLMGateway Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
|
Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details. Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
…ike Claude) Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Done in b9eeee2 — changed Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
|
✨ The prophecy is fulfilled... Smoke Codex has completed its mystical journey. The stars align. 🌟 |
|
🧪 Smoke Project is now testing project operations... |
|
🧪 Smoke Temporary ID is now testing temporary ID functionality... |
|
📰 BREAKING: Smoke Copilot is now investigating this pull request. Sources say the story is developing... |
|
🎬 THE END — Smoke Claude MISSION: ACCOMPLISHED! The hero saves the day! ✨ |
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Done in 1d1d9f2. The step 28 "Run Gemini" failure showed an Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
|
@copilot make sure to configure the Gemini base url to point to the gateway url |
…bled Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Done in 34fca18 — when the firewall is enabled, Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
|
🧪 Smoke Project is now testing project operations... |
|
✨ The prophecy is fulfilled... Smoke Codex has completed its mystical journey. The stars align. 🌟 |
|
📰 BREAKING: Smoke Copilot is now investigating this pull request. Sources say the story is developing... |
|
🎬 THE END — Smoke Claude MISSION: ACCOMPLISHED! The hero saves the day! ✨ |
|
🧪 Smoke Temporary ID is now testing temporary ID functionality... |
|
✅ Smoke Project completed successfully. All project operations validated. |
|
PR titles: #17285 update CentralRepoOps docs; #17280 feat: wildcard support for allowed-repos and standardize target-repo helpers
|
|
Smoke Test Results [
Overall: PASS (10/11)
|
There was a problem hiding this comment.
Smoke test review pass. Minor observations: model pinning in smoke-gemini.md is a good practice, and the streaming JSON parser handles non-JSON lines gracefully. Changes look good overall.
📰 BREAKING: Report filed by Smoke Copilot for issue #17245
| pull-requests: read | ||
| name: Smoke Gemini | ||
| engine: gemini | ||
| engine: |
There was a problem hiding this comment.
Pinning to a specific model (gemini-2.0-flash-lite) is a good practice for reproducibility. This ensures consistent behavior across runs rather than relying on whatever "latest" model is available.
| } | ||
| if (typeof modelStats.output_tokens === "number") { | ||
| totalOutputTokens += modelStats.output_tokens; | ||
| } |
There was a problem hiding this comment.
Silently skipping non-JSON lines is the right approach here since Gemini CLI may mix JSON with other output. The _e naming convention for ignored errors is consistent with the codebase style.
|
📰 VERDICT: Smoke Copilot has concluded. All systems operational. This is a developing story. 🎤 |
There was a problem hiding this comment.
Pull request overview
This PR enables API proxy support for the Gemini engine and adds the missing log parser to fix smoke test failures. The changes bring Gemini's AWF integration in line with Claude and Codex patterns by properly configuring the LLM gateway proxy.
Changes:
- Added
--enable-api-proxyflag andGEMINI_API_BASE_URLenvironment variable to Gemini's AWF invocations when firewall is enabled - Created
parse_gemini_log.cjsto parse Gemini's streaming JSON output format - Updated AWF helpers to enable host access for API proxy sidecars (not just MCP gateways)
- Switched Gemini output format from
jsontostream-jsonfor consistency with Claude - Updated smoke-gemini workflow to use
gemini-2.0-flash-litemodel
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| smoke_test_push_22239107636.txt | Temporary test file for smoke test run validation |
| pkg/workflow/strict_mode_llm_gateway_test.go | Added test case verifying Gemini uses port 10003 for LLM gateway |
| pkg/workflow/gemini_engine_test.go | Updated assertions to verify stream-json output format and API proxy configuration |
| pkg/workflow/gemini_engine.go | Enabled API proxy support with explicit variables, switched to stream-json output, and configured GEMINI_API_BASE_URL for firewall mode |
| pkg/workflow/enable_api_proxy_test.go | Added test case verifying Gemini AWF command includes --enable-api-proxy flag |
| pkg/workflow/awf_helpers.go | Extended --enable-host-access logic to support API proxy in addition to MCP servers |
| actions/setup/js/parse_gemini_log.cjs | New log parser for Gemini's JSONL output format, extracting response text and token usage |
| .github/workflows/smoke-gemini.md | Configured explicit model (gemini-2.0-flash-lite) in engine config |
| .github/workflows/smoke-gemini.lock.yml | Regenerated workflow with API proxy enabled, stream-json output, model configuration, and GEMINI_API_BASE_URL |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| continue; | ||
| } | ||
|
|
||
| // Try to parse each line as a JSON object (Gemini --output-format json output) |
There was a problem hiding this comment.
The comment incorrectly refers to "Gemini --output-format json output" but the code is actually parsing output from "--output-format stream-json" (as correctly stated in the docstring at line 14). This should be updated to match the actual output format being parsed.
| // Try to parse each line as a JSON object (Gemini --output-format json output) | |
| // Try to parse each line as a JSON object from Gemini --output-format stream-json output (JSONL) |
| // OR when the API proxy sidecar is enabled (needs to reach host.docker.internal:<port>) | ||
| if HasMCPServers(config.WorkflowData) || config.UsesAPIProxy { | ||
| awfArgs = append(awfArgs, "--enable-host-access") | ||
| awfHelpersLog.Print("Added --enable-host-access for MCP gateway communication") |
There was a problem hiding this comment.
The log message "Added --enable-host-access for MCP gateway communication" is now potentially misleading since this block executes both when MCP servers are present AND when UsesAPIProxy is true (line 174). When --enable-host-access is added only for API proxy support (without MCP servers), the message incorrectly suggests it's for MCP gateway communication. Consider updating the message to reflect both use cases, such as "Added --enable-host-access for MCP gateway and/or API proxy communication".
| awfHelpersLog.Print("Added --enable-host-access for MCP gateway communication") | |
| awfHelpersLog.Print("Added --enable-host-access for MCP gateway and/or API proxy communication") |
| // @ts-check | ||
| /// <reference types="@actions/github-script" /> | ||
|
|
||
| const { createEngineLogParser } = require("./log_parser_shared.cjs"); | ||
|
|
||
| const main = createEngineLogParser({ | ||
| parserName: "Gemini", | ||
| parseFunction: parseGeminiLog, | ||
| supportsDirectories: false, | ||
| }); | ||
|
|
||
| /** | ||
| * Parse Gemini CLI streaming JSON log output and format as markdown. | ||
| * Gemini CLI outputs one JSON object per line when using --output-format stream-json (JSONL). | ||
| * @param {string} logContent - The raw log content to parse | ||
| * @returns {{markdown: string, logEntries: Array, mcpFailures: Array<string>, maxTurnsHit: boolean}} Parsed log data | ||
| */ | ||
| function parseGeminiLog(logContent) { | ||
| if (!logContent) { | ||
| return { | ||
| markdown: "## 🤖 Gemini\n\nNo log content provided.\n\n", | ||
| logEntries: [], | ||
| mcpFailures: [], | ||
| maxTurnsHit: false, | ||
| }; | ||
| } | ||
|
|
||
| let markdown = ""; | ||
| let totalInputTokens = 0; | ||
| let totalOutputTokens = 0; | ||
| let lastResponse = ""; | ||
|
|
||
| const lines = logContent.split("\n"); | ||
| for (const line of lines) { | ||
| const trimmed = line.trim(); | ||
| if (!trimmed) { | ||
| continue; | ||
| } | ||
|
|
||
| // Try to parse each line as a JSON object (Gemini --output-format json output) | ||
| try { | ||
| const parsed = JSON.parse(trimmed); | ||
|
|
||
| if (parsed.response) { | ||
| lastResponse = parsed.response; | ||
| } | ||
|
|
||
| // Aggregate token usage from stats | ||
| if (parsed.stats && parsed.stats.models) { | ||
| for (const modelStats of Object.values(parsed.stats.models)) { | ||
| if (modelStats && typeof modelStats === "object") { | ||
| if (typeof modelStats.input_tokens === "number") { | ||
| totalInputTokens += modelStats.input_tokens; | ||
| } | ||
| if (typeof modelStats.output_tokens === "number") { | ||
| totalOutputTokens += modelStats.output_tokens; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } catch (_e) { | ||
| // Not JSON - skip non-JSON lines | ||
| } | ||
| } | ||
|
|
||
| // Build markdown output | ||
| if (lastResponse) { | ||
| markdown += "## 🤖 Reasoning\n\n"; | ||
| markdown += lastResponse + "\n\n"; | ||
| } | ||
|
|
||
| markdown += "## 📊 Information\n\n"; | ||
| const totalTokens = totalInputTokens + totalOutputTokens; | ||
| if (totalTokens > 0) { | ||
| markdown += `**Total Tokens Used:** ${totalTokens.toLocaleString()}\n\n`; | ||
| if (totalInputTokens > 0) { | ||
| markdown += `**Input Tokens:** ${totalInputTokens.toLocaleString()}\n\n`; | ||
| } | ||
| if (totalOutputTokens > 0) { | ||
| markdown += `**Output Tokens:** ${totalOutputTokens.toLocaleString()}\n\n`; | ||
| } | ||
| } | ||
|
|
||
| return { | ||
| markdown, | ||
| logEntries: [], | ||
| mcpFailures: [], | ||
| maxTurnsHit: false, | ||
| }; | ||
| } | ||
|
|
||
| // Export for testing | ||
| if (typeof module !== "undefined" && module.exports) { | ||
| module.exports = { | ||
| main, | ||
| parseGeminiLog, | ||
| }; | ||
| } |
There was a problem hiding this comment.
The newly added parse_gemini_log.cjs is missing a corresponding test file. All other log parsers in this directory have associated test files (e.g., parse_claude_log.test.cjs, parse_codex_log.test.cjs, parse_copilot_log.test.cjs). A parse_gemini_log.test.cjs file should be added to maintain consistency with the established testing pattern for log parsers.
Gemini's AWF command was missing
--enable-api-proxydespite the engine declaring LLM gateway support (port 10003). Additionally, the "Parse agent logs" step insmoke-geminiwas failing becauseparse_gemini_log.cjsdidn't exist.Changes
gemini_engine.go: Use explicitllmGatewayPort/usesAPIProxyvariables (matching Claude/Codex pattern) so--enable-api-proxyis correctly included in AWF invocations; switch output format from--output-format jsonto--output-format stream-json(streaming JSONL, consistent with Claude); fix non-AWF path to usetee -a; setGEMINI_API_BASE_URL=http://host.docker.internal:10003when the firewall is enabled so the Gemini CLI routes LLM calls through the AWF api-proxy sidecarawf_helpers.go: Add--enable-host-accessto AWF args whenUsesAPIProxyis true (in addition to when MCP servers are present), ensuring the container can always reachhost.docker.internalwhen the API proxy is in useparse_gemini_log.cjs: Add log parser for Gemini's--output-format stream-jsonoutput, extracting response text and token usage fromstats.modelsstrict_mode_llm_gateway_test.go: Add Gemini toTestSupportsLLMGatewaywithconstants.GeminiLLMGatewayPortgemini_engine_test.go: Update assertions to check for--output-format stream-json,--enable-api-proxy, andGEMINI_API_BASE_URLwhen firewall is enabledsmoke-gemini.md: Usegemini-2.0-flash-litemodel (cheaper/faster equivalent of Claude Haiku) to reduce API costssmoke-gemini.lock.yml: Recompiled to include--enable-api-proxy,--model gemini-2.0-flash-lite,--output-format stream-json, andGEMINI_API_BASE_URL: http://host.docker.internal:10003in the AWF invocation🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.
✨ PR Review Safe Output Test - Run 22239107636