refactor(ai_client): extract _send_cli_round_result helper (Phase 10 site 6)
Site L1990: inner _send(r_idx) in _send_gemini_cli had:
try: resp_data = adapter.send(...)
except Exception as e: events.emit('response_received', {'error': str(e)}); raise
This is Re-Raise Pattern 2 (catch + emit event + raise). Per TIER1_REVIEW,
the migration is to Result[T] because the audit does not yet recognize
events.emit as a structured error carrier.
New helper _send_cli_round_result(r_idx, adapter, payload, ...) -> Result[dict]:
- Emits request_start + [CLI] comms before SDK call
- Returns Result(data=resp_data) on SDK success
- On failure: emits response_received error event + returns Result(errors=[ErrorInfo(original=e)])
Inner _send refactored:
send_result = _send_cli_round_result(r_idx, adapter, payload, ...)
if not send_result.ok:
raise cast(Exception, send_result.errors[0].original)
resp_data = send_result.data
This preserves the original re-raise behavior so the outer
_send_gemini_cli try/except still catches and converts to Result.
Audit: ai_client BC 4 -> 3.
This commit is contained in:
+26
-9
@@ -1704,6 +1704,28 @@ def _create_gemini_cache_result(sys_instr: str, tools_decl: Any, file_items: lis
|
||||
errors=[ErrorInfo(kind=ErrorKind.INTERNAL, message=f"failed to create gemini cache: {type(e).__name__}: {e}", source="ai_client._create_gemini_cache_result", original=e)],
|
||||
)
|
||||
|
||||
def _send_cli_round_result(r_idx: int, adapter: Any, payload: Any, safety_settings: list[Any], sys_instr: str, stream_callback: Optional[Callable[[str], None]]) -> Result[dict[str, Any]]:
|
||||
"""Call the Gemini CLI adapter for one round. Returns Result[resp_data].
|
||||
|
||||
On SDK failure, emits a response_received event with the error info
|
||||
(preserving the original side-effect semantics) and returns
|
||||
Result(errors=[ErrorInfo]). The caller (_send in _send_gemini_cli)
|
||||
re-raises the original exception to preserve the outer catch flow.
|
||||
"""
|
||||
events.emit("request_start", payload={"provider": "gemini_cli", "model": _model, "round": r_idx})
|
||||
if r_idx > 0:
|
||||
_append_comms("OUT", "request", {"message": f"[CLI] [round {r_idx}] [msg {len(payload)}]"})
|
||||
send_payload: Any = json.dumps(payload) if isinstance(payload, list) else payload
|
||||
try:
|
||||
resp_data = adapter.send(cast(str, send_payload), safety_settings=safety_settings, system_instruction=sys_instr, model=_model, stream_callback=stream_callback)
|
||||
return Result(data=resp_data)
|
||||
except Exception as e:
|
||||
events.emit("response_received", payload={"provider": "gemini_cli", "model": _model, "usage": {}, "latency": 0, "round": r_idx, "error": str(e)})
|
||||
return Result(
|
||||
data=None,
|
||||
errors=[ErrorInfo(kind=ErrorKind.INTERNAL, message=str(e), source="ai_client._send_cli_round_result", original=e)],
|
||||
)
|
||||
|
||||
def _extract_gemini_thoughts(resp: Any) -> str:
|
||||
"""
|
||||
Extracts concatenated thinking text from a Gemini response object's parts.
|
||||
@@ -1981,15 +2003,10 @@ def _send_gemini_cli(md_content: str, user_message: str, base_dir: str,
|
||||
def _send(r_idx: int) -> NormalizedResponse:
|
||||
if adapter is None:
|
||||
return NormalizedResponse(text="(adapter unavailable)", tool_calls=[], usage_input_tokens=0, usage_output_tokens=0, usage_cache_read_tokens=0, usage_cache_creation_tokens=0, raw_response=None)
|
||||
events.emit("request_start", payload={"provider": "gemini_cli", "model": _model, "round": r_idx})
|
||||
if r_idx > 0:
|
||||
_append_comms("OUT", "request", {"message": f"[CLI] [round {r_idx}] [msg {len(payload)}]"})
|
||||
send_payload: Any = json.dumps(payload) if isinstance(payload, list) else payload
|
||||
try:
|
||||
resp_data = adapter.send(cast(str, send_payload), safety_settings=safety_settings, system_instruction=sys_instr, model=_model, stream_callback=stream_callback)
|
||||
except Exception as e:
|
||||
events.emit("response_received", payload={"provider": "gemini_cli", "model": _model, "usage": {}, "latency": 0, "round": r_idx, "error": str(e)})
|
||||
raise
|
||||
send_result = _send_cli_round_result(r_idx, adapter, payload, safety_settings, sys_instr, stream_callback)
|
||||
if not send_result.ok:
|
||||
raise cast(Exception, send_result.errors[0].original)
|
||||
resp_data = send_result.data
|
||||
cli_stderr = resp_data.get("stderr", "")
|
||||
if cli_stderr:
|
||||
sys.stderr.write(f"\n--- Gemini CLI stderr ---\n{cli_stderr}\n-------------------------\n")
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
"""Phase 10 site 6: _send_cli_round_result helper.
|
||||
|
||||
Site L1990 (in _send_gemini_cli):
|
||||
try: resp_data = adapter.send(...)
|
||||
except Exception as e: events.emit('response_received', {'error': str(e)}); raise
|
||||
|
||||
Re-Raise Pattern 2 (catch + emit + raise). Migration: extract Result helper.
|
||||
The inner _send calls the helper; on error, re-raise original exception
|
||||
(preserving outer _send_gemini_cli catch behavior).
|
||||
"""
|
||||
import sys
|
||||
sys.path.insert(0, ".")
|
||||
|
||||
|
||||
def test_phase10_site6_send_cli_round_result_exists():
|
||||
import src.ai_client
|
||||
assert hasattr(src.ai_client, "_send_cli_round_result"), \
|
||||
"_send_cli_round_result helper missing"
|
||||
|
||||
|
||||
def test_phase10_site6_send_cli_round_result_returns_result():
|
||||
import src.ai_client
|
||||
import inspect
|
||||
fn = src.ai_client._send_cli_round_result
|
||||
sig = inspect.signature(fn)
|
||||
assert "Result" in str(sig.return_annotation), \
|
||||
f"_send_cli_round_result return must be Result, got {sig.return_annotation}"
|
||||
Reference in New Issue
Block a user