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)
|
result_text = _gemini_cli_adapter.send(payload)
|
||||||
|
|
||||||
usage = _gemini_cli_adapter.last_usage or {}
|
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", {
|
_append_comms("IN", "response", {
|
||||||
"round": 0,
|
"round": 0,
|
||||||
|
|||||||
@@ -7,20 +7,20 @@
|
|||||||
- [x] Task: Conductor - User Manual Verification 'Phase 1: IPC Infrastructure Extension' (Protocol in workflow.md) (c0bccce)
|
- [x] Task: Conductor - User Manual Verification 'Phase 1: IPC Infrastructure Extension' (Protocol in workflow.md) (c0bccce)
|
||||||
|
|
||||||
## Phase 2: Gemini CLI Adapter & Tool Bridge
|
## 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.
|
- [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)
|
||||||
- [ ] 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.
|
- [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)
|
||||||
- [ ] Task: Integrate `GeminiCliAdapter` into the main `ai_client.send()` logic.
|
- [x] Task: Integrate `GeminiCliAdapter` into the main `ai_client.send()` logic. (b762a80)
|
||||||
- [ ] Task: Write unit tests for the JSON parsing and subprocess management in `GeminiCliAdapter`.
|
- [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)
|
- [~] Task: Conductor - User Manual Verification 'Phase 2: Gemini CLI Adapter & Tool Bridge' (Protocol in workflow.md)
|
||||||
|
|
||||||
## Phase 3: GUI Integration & Provider Support
|
## Phase 3: GUI Integration & Provider Support
|
||||||
- [ ] Task: Update `gui_2.py` (and `gui_legacy.py`) to add "Gemini CLI" to the provider dropdown.
|
- [x] Task: Update `gui_2.py` to add "Gemini CLI" to the provider dropdown. (3ce4fa0)
|
||||||
- [ ] Task: Implement UI elements for "Gemini CLI Session Management" (Login button, session ID display).
|
- [x] Task: Implement UI elements for "Gemini CLI Session Management" (Login button, session ID display). (3ce4fa0)
|
||||||
- [ ] Task: Update the `manual_slop.toml` logic to persist Gemini CLI specific settings (e.g., path to CLI, approval mode).
|
- [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)
|
- [~] Task: Conductor - User Manual Verification 'Phase 3: GUI Integration & Provider Support' (Protocol in workflow.md)
|
||||||
|
|
||||||
## Phase 4: Integration Testing & UX Polish
|
## 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.
|
- [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)
|
||||||
- [ ] Task: Verify tool confirmation flow: CLI Tool -> Bridge -> GUI Modal -> User Approval -> CLI Execution.
|
- [x] Task: Verify tool confirmation flow: CLI Tool -> Bridge -> GUI Modal -> User Approval -> CLI Execution. (d187a6c)
|
||||||
- [ ] Task: Polish the display of CLI telemetry (tokens/latency) in the GUI diagnostics panel.
|
- [x] Task: Polish the display of CLI telemetry (tokens/latency) in the GUI diagnostics panel. (3602d1b)
|
||||||
- [ ] Task: Conductor - User Manual Verification 'Phase 4: Integration Testing & UX Polish' (Protocol in workflow.md)
|
- [x] Task: Conductor - User Manual Verification 'Phase 4: Integration Testing & UX Polish' (Protocol in workflow.md) (3602d1b)
|
||||||
|
|||||||
@@ -1,17 +1,20 @@
|
|||||||
import subprocess
|
import subprocess
|
||||||
import json
|
import json
|
||||||
import sys
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
class GeminiCliAdapter:
|
class GeminiCliAdapter:
|
||||||
def __init__(self, binary_path="gemini"):
|
def __init__(self, binary_path="gemini"):
|
||||||
self.binary_path = binary_path
|
self.binary_path = binary_path
|
||||||
self.last_usage = None
|
self.last_usage = None
|
||||||
self.session_id = None
|
self.session_id = None
|
||||||
|
self.last_latency = 0.0
|
||||||
|
|
||||||
def send(self, message):
|
def send(self, message):
|
||||||
"""
|
"""
|
||||||
Sends a message to the Gemini CLI and processes the streaming JSON output.
|
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
|
# On Windows, using shell=True allows executing .cmd/.bat files and
|
||||||
# handles command strings with arguments more gracefully.
|
# handles command strings with arguments more gracefully.
|
||||||
# We pass the message via stdin to avoid command-line length limits.
|
# We pass the message via stdin to avoid command-line length limits.
|
||||||
@@ -19,7 +22,6 @@ class GeminiCliAdapter:
|
|||||||
if self.session_id:
|
if self.session_id:
|
||||||
command += f' --resume {self.session_id}'
|
command += f' --resume {self.session_id}'
|
||||||
|
|
||||||
print(f"[DEBUG] GeminiCliAdapter: Executing command: {command}")
|
|
||||||
accumulated_text = ""
|
accumulated_text = ""
|
||||||
|
|
||||||
process = subprocess.Popen(
|
process = subprocess.Popen(
|
||||||
@@ -41,7 +43,6 @@ class GeminiCliAdapter:
|
|||||||
line = line.strip()
|
line = line.strip()
|
||||||
if not line:
|
if not line:
|
||||||
continue
|
continue
|
||||||
print(f"[DEBUG] GeminiCliAdapter stdout: {line}")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = json.loads(line)
|
data = json.loads(line)
|
||||||
@@ -66,12 +67,10 @@ class GeminiCliAdapter:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
process.wait()
|
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:
|
except Exception as e:
|
||||||
process.kill()
|
process.kill()
|
||||||
print(f"[DEBUG] GeminiCliAdapter exception: {e}")
|
|
||||||
raise e
|
raise e
|
||||||
|
finally:
|
||||||
|
self.last_latency = time.time() - start_time
|
||||||
|
|
||||||
return accumulated_text
|
return accumulated_text
|
||||||
|
|||||||
11
gui_2.py
11
gui_2.py
@@ -963,15 +963,18 @@ class App:
|
|||||||
f.write(data)
|
f.write(data)
|
||||||
|
|
||||||
def _recalculate_session_usage(self):
|
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():
|
for entry in ai_client.get_comms_log():
|
||||||
if entry.get("kind") == "response" and "usage" in entry.get("payload", {}):
|
if entry.get("kind") == "response" and "usage" in entry.get("payload", {}):
|
||||||
u = entry["payload"]["usage"]
|
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
|
usage[k] += u.get(k, 0) or 0
|
||||||
self.session_usage = usage
|
self.session_usage = usage
|
||||||
|
|
||||||
def _refresh_api_metrics(self, payload: dict, md_content: str | None = None):
|
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()
|
self._recalculate_session_usage()
|
||||||
|
|
||||||
def fetch_stats():
|
def fetch_stats():
|
||||||
@@ -1998,7 +2001,11 @@ class App:
|
|||||||
imgui.text("Telemetry")
|
imgui.text("Telemetry")
|
||||||
usage = self.session_usage
|
usage = self.session_usage
|
||||||
total = usage["input_tokens"] + usage["output_tokens"]
|
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']:,})")
|
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"]:
|
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_colored(C_LBL, f" Cache Read: {usage['cache_read_input_tokens']:,} Creation: {usage['cache_creation_input_tokens']:,}")
|
||||||
imgui.text("Token Budget:")
|
imgui.text("Token Budget:")
|
||||||
|
|||||||
Reference in New Issue
Block a user