feat(ai): finalize Gemini CLI integration with telemetry polish and cleanup
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
11
gui_2.py
11
gui_2.py
@@ -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():
|
||||
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:")
|
||||
|
||||
Reference in New Issue
Block a user