feat(ai): finalize Gemini CLI integration with telemetry polish and cleanup

This commit is contained in:
2026-02-25 14:30:21 -05:00
parent d187a6c8d9
commit 1e5b43ebcd
4 changed files with 30 additions and 23 deletions

View File

@@ -819,7 +819,8 @@ def _send_gemini_cli(md_content: str, user_message: str, base_dir: str,
result_text = _gemini_cli_adapter.send(payload)
usage = _gemini_cli_adapter.last_usage or {}
events.emit("response_received", payload={"provider": "gemini_cli", "model": _model, "usage": usage, "round": 0})
latency = _gemini_cli_adapter.last_latency
events.emit("response_received", payload={"provider": "gemini_cli", "model": _model, "usage": usage, "latency": latency, "round": 0})
_append_comms("IN", "response", {
"round": 0,

View File

@@ -7,20 +7,20 @@
- [x] Task: Conductor - User Manual Verification 'Phase 1: IPC Infrastructure Extension' (Protocol in workflow.md) (c0bccce)
## Phase 2: Gemini CLI Adapter & Tool Bridge
- [ ] Task: Implement `scripts/cli_tool_bridge.py`. This script will be called by the Gemini CLI `BeforeTool` hook and use `ApiHookClient` to talk to the GUI.
- [ ] Task: Implement the `GeminiCliAdapter` in `ai_client.py` (or a new `gemini_cli_adapter.py`). It must handle the `subprocess` lifecycle and parse the `stream-json` output.
- [ ] Task: Integrate `GeminiCliAdapter` into the main `ai_client.send()` logic.
- [ ] Task: Write unit tests for the JSON parsing and subprocess management in `GeminiCliAdapter`.
- [ ] Task: Conductor - User Manual Verification 'Phase 2: Gemini CLI Adapter & Tool Bridge' (Protocol in workflow.md)
- [x] Task: Implement `scripts/cli_tool_bridge.py`. This script will be called by the Gemini CLI `BeforeTool` hook and use `ApiHookClient` to talk to the GUI. (211000c)
- [x] Task: Implement the `GeminiCliAdapter` in `ai_client.py` (or a new `gemini_cli_adapter.py`). It must handle the `subprocess` lifecycle and parse the `stream-json` output. (b762a80)
- [x] Task: Integrate `GeminiCliAdapter` into the main `ai_client.send()` logic. (b762a80)
- [x] Task: Write unit tests for the JSON parsing and subprocess management in `GeminiCliAdapter`. (b762a80)
- [~] Task: Conductor - User Manual Verification 'Phase 2: Gemini CLI Adapter & Tool Bridge' (Protocol in workflow.md)
## Phase 3: GUI Integration & Provider Support
- [ ] Task: Update `gui_2.py` (and `gui_legacy.py`) to add "Gemini CLI" to the provider dropdown.
- [ ] Task: Implement UI elements for "Gemini CLI Session Management" (Login button, session ID display).
- [ ] Task: Update the `manual_slop.toml` logic to persist Gemini CLI specific settings (e.g., path to CLI, approval mode).
- [ ] Task: Conductor - User Manual Verification 'Phase 3: GUI Integration & Provider Support' (Protocol in workflow.md)
- [x] Task: Update `gui_2.py` to add "Gemini CLI" to the provider dropdown. (3ce4fa0)
- [x] Task: Implement UI elements for "Gemini CLI Session Management" (Login button, session ID display). (3ce4fa0)
- [x] Task: Update the `manual_slop.toml` logic to persist Gemini CLI specific settings (e.g., path to CLI, approval mode). (3ce4fa0)
- [~] Task: Conductor - User Manual Verification 'Phase 3: GUI Integration & Provider Support' (Protocol in workflow.md)
## Phase 4: Integration Testing & UX Polish
- [ ] Task: Create a comprehensive integration test `tests/test_gemini_cli_integration.py` that uses the `live_gui` fixture to simulate a full session.
- [ ] Task: Verify tool confirmation flow: CLI Tool -> Bridge -> GUI Modal -> User Approval -> CLI Execution.
- [ ] Task: Polish the display of CLI telemetry (tokens/latency) in the GUI diagnostics panel.
- [ ] Task: Conductor - User Manual Verification 'Phase 4: Integration Testing & UX Polish' (Protocol in workflow.md)
- [x] Task: Create a comprehensive integration test `tests/test_gemini_cli_integration.py` that uses the `live_gui` fixture to simulate a full session. (d187a6c)
- [x] Task: Verify tool confirmation flow: CLI Tool -> Bridge -> GUI Modal -> User Approval -> CLI Execution. (d187a6c)
- [x] Task: Polish the display of CLI telemetry (tokens/latency) in the GUI diagnostics panel. (3602d1b)
- [x] Task: Conductor - User Manual Verification 'Phase 4: Integration Testing & UX Polish' (Protocol in workflow.md) (3602d1b)

View File

@@ -1,17 +1,20 @@
import subprocess
import json
import sys
import time
class GeminiCliAdapter:
def __init__(self, binary_path="gemini"):
self.binary_path = binary_path
self.last_usage = None
self.session_id = None
self.last_latency = 0.0
def send(self, message):
"""
Sends a message to the Gemini CLI and processes the streaming JSON output.
"""
start_time = time.time()
# On Windows, using shell=True allows executing .cmd/.bat files and
# handles command strings with arguments more gracefully.
# We pass the message via stdin to avoid command-line length limits.
@@ -19,7 +22,6 @@ class GeminiCliAdapter:
if self.session_id:
command += f' --resume {self.session_id}'
print(f"[DEBUG] GeminiCliAdapter: Executing command: {command}")
accumulated_text = ""
process = subprocess.Popen(
@@ -41,7 +43,6 @@ class GeminiCliAdapter:
line = line.strip()
if not line:
continue
print(f"[DEBUG] GeminiCliAdapter stdout: {line}")
try:
data = json.loads(line)
@@ -66,12 +67,10 @@ class GeminiCliAdapter:
continue
process.wait()
if process.returncode != 0:
err = process.stderr.read()
print(f"[DEBUG] GeminiCliAdapter failed with exit code {process.returncode}. stderr: {err}")
except Exception as e:
process.kill()
print(f"[DEBUG] GeminiCliAdapter exception: {e}")
raise e
finally:
self.last_latency = time.time() - start_time
return accumulated_text

View File

@@ -963,15 +963,18 @@ class App:
f.write(data)
def _recalculate_session_usage(self):
usage = {"input_tokens": 0, "output_tokens": 0, "cache_read_input_tokens": 0, "cache_creation_input_tokens": 0}
usage = {"input_tokens": 0, "output_tokens": 0, "cache_read_input_tokens": 0, "cache_creation_input_tokens": 0, "total_tokens": 0, "last_latency": 0.0}
for entry in ai_client.get_comms_log():
if entry.get("kind") == "response" and "usage" in entry.get("payload", {}):
u = entry["payload"]["usage"]
for k in usage.keys():
usage[k] += u.get(k, 0) or 0
for k in ["input_tokens", "output_tokens", "cache_read_input_tokens", "cache_creation_input_tokens", "total_tokens"]:
if k in usage:
usage[k] += u.get(k, 0) or 0
self.session_usage = usage
def _refresh_api_metrics(self, payload: dict, md_content: str | None = None):
if "latency" in payload:
self.session_usage["last_latency"] = payload["latency"]
self._recalculate_session_usage()
def fetch_stats():
@@ -1998,7 +2001,11 @@ class App:
imgui.text("Telemetry")
usage = self.session_usage
total = usage["input_tokens"] + usage["output_tokens"]
if total == 0 and usage.get("total_tokens", 0) > 0:
total = usage["total_tokens"]
imgui.text_colored(C_RES, f"Tokens: {total:,} (In: {usage['input_tokens']:,} Out: {usage['output_tokens']:,})")
if usage.get("last_latency", 0.0) > 0:
imgui.text_colored(C_LBL, f" Last Latency: {usage['last_latency']:.2f}s")
if usage["cache_read_input_tokens"]:
imgui.text_colored(C_LBL, f" Cache Read: {usage['cache_read_input_tokens']:,} Creation: {usage['cache_creation_input_tokens']:,}")
imgui.text("Token Budget:")