working on ai client

This commit is contained in:
ed
2026-06-13 17:46:38 -04:00
parent 3b6a9dd0dc
commit 6caa6680bf
+21 -33
View File
@@ -64,7 +64,6 @@ PROVIDERS: List[str] = ["gemini", "anthropic", "gemini_cli", "deepseek", "minima
from src.module_loader import _require_warmed # noqa: E402,F401 from src.module_loader import _require_warmed # noqa: E402,F401
from src.result_types import ErrorInfo, ErrorKind, Result # noqa: E402,F401 from src.result_types import ErrorInfo, ErrorKind, Result # noqa: E402,F401
_provider: str = "gemini" _provider: str = "gemini"
_model: str = "gemini-2.5-flash-lite" _model: str = "gemini-2.5-flash-lite"
_temperature: float = 0.0 _temperature: float = 0.0
@@ -271,6 +270,7 @@ def get_credentials_path() -> Path:
def _load_credentials() -> dict[str, Any]: def _load_credentials() -> dict[str, Any]:
cred_path = get_credentials_path() cred_path = get_credentials_path()
#TODO(Ed): Exception(Review)
try: try:
with open(cred_path, "rb") as f: with open(cred_path, "rb") as f:
return tomllib.load(f) return tomllib.load(f)
@@ -409,6 +409,7 @@ def cleanup() -> None:
"""Performs cleanup operations like deleting server-side Gemini caches.""" """Performs cleanup operations like deleting server-side Gemini caches."""
global _gemini_client, _gemini_cache, _gemini_cached_file_paths global _gemini_client, _gemini_cache, _gemini_cached_file_paths
if _gemini_client and _gemini_cache: if _gemini_client and _gemini_cache:
#TODO(Ed): Exception(Review)
try: try:
_gemini_client.caches.delete(name=_gemini_cache.name) _gemini_client.caches.delete(name=_gemini_cache.name)
except Exception: except Exception:
@@ -426,6 +427,7 @@ def reset_session() -> None:
global _CACHED_ANTHROPIC_TOOLS, _CACHED_DEEPSEEK_TOOLS global _CACHED_ANTHROPIC_TOOLS, _CACHED_DEEPSEEK_TOOLS
global _gemini_cli_adapter global _gemini_cli_adapter
if _gemini_client and _gemini_cache: if _gemini_client and _gemini_cache:
#TODO(Ed): Review(Exception)
try: try:
_gemini_client.caches.delete(name=_gemini_cache.name) _gemini_client.caches.delete(name=_gemini_cache.name)
except Exception: except Exception:
@@ -466,9 +468,6 @@ def reset_session() -> None:
file_cache.reset_client() file_cache.reset_client()
def list_models(provider: str) -> list[str]: def list_models(provider: str) -> list[str]:
"""
[C: src/app_controller.py:AppController.do_fetch, tests/test_agent_capabilities.py:test_agent_capabilities_listing, tests/test_ai_client_list_models.py:test_list_models_gemini_cli, tests/test_deepseek_infra.py:test_deepseek_model_listing, tests/test_minimax_provider.py:test_minimax_list_models]
"""
creds = _load_credentials() creds = _load_credentials()
if provider == "gemini": return _list_gemini_models(creds["gemini"]["api_key"]) if provider == "gemini": return _list_gemini_models(creds["gemini"]["api_key"])
elif provider == "anthropic": return _list_anthropic_models() elif provider == "anthropic": return _list_anthropic_models()
@@ -489,20 +488,14 @@ _agent_tools: dict[str, bool] = {}
#region: Tool Configuration #region: Tool Configuration
def set_agent_tools(tools: dict[str, bool]) -> None: def set_agent_tools(tools: dict[str, bool]) -> None:
""" """Configures which tools are enabled for the AI agent."""
Configures which tools are enabled for the AI agent.
[C: src/app_controller.py:AppController._handle_request_event, src/app_controller.py:_api_generate, tests/test_agent_tools_wiring.py:test_build_anthropic_tools_conversion, tests/test_agent_tools_wiring.py:test_set_agent_tools, tests/test_tool_access_exclusion.py:test_build_anthropic_tools_excludes_disabled, tests/test_tool_access_exclusion.py:test_build_deepseek_tools_excludes_disabled, tests/test_tool_access_exclusion.py:test_gemini_tool_declaration_excludes_disabled, tests/test_tool_access_exclusion.py:test_set_agent_tools_clears_caches]
"""
global _agent_tools, _CACHED_ANTHROPIC_TOOLS, _CACHED_DEEPSEEK_TOOLS global _agent_tools, _CACHED_ANTHROPIC_TOOLS, _CACHED_DEEPSEEK_TOOLS
_agent_tools = tools _agent_tools = tools
_CACHED_ANTHROPIC_TOOLS = None _CACHED_ANTHROPIC_TOOLS = None
_CACHED_DEEPSEEK_TOOLS = None _CACHED_DEEPSEEK_TOOLS = None
def set_tool_preset(preset_name: Optional[str]) -> None: def set_tool_preset(preset_name: Optional[str]) -> None:
""" """Loads a tool preset and applies it via set_agent_tools."""
Loads a tool preset and applies it via set_agent_tools.
[C: src/app_controller.py:AppController.init_state, src/gui_2.py:App._render_persona_selector_panel, src/multi_agent_conductor.py:run_worker_lifecycle, tests/test_bias_integration.py:test_set_tool_preset_with_objects, tests/test_tool_preset_env.py:test_tool_preset_env_loading, tests/test_tool_preset_env.py:test_tool_preset_env_no_var, tests/test_tool_presets_execution.py:test_tool_ask_approval, tests/test_tool_presets_execution.py:test_tool_auto_approval, tests/test_tool_presets_execution.py:test_tool_rejection]
"""
global _agent_tools, _CACHED_ANTHROPIC_TOOLS, _CACHED_DEEPSEEK_TOOLS, _tool_approval_modes, _active_tool_preset global _agent_tools, _CACHED_ANTHROPIC_TOOLS, _CACHED_DEEPSEEK_TOOLS, _tool_approval_modes, _active_tool_preset
_tool_approval_modes = {} _tool_approval_modes = {}
if not preset_name or preset_name == "None": if not preset_name or preset_name == "None":
@@ -532,11 +525,7 @@ def set_tool_preset(preset_name: Optional[str]) -> None:
_CACHED_DEEPSEEK_TOOLS = None _CACHED_DEEPSEEK_TOOLS = None
def set_bias_profile(profile_name: Optional[str]) -> None: def set_bias_profile(profile_name: Optional[str]) -> None:
""" """Sets the active tool bias profile for tuning model behavior."""
Sets the active tool bias profile for tuning model behavior.
[C: src/app_controller.py:AppController.init_state, src/gui_2.py:App._render_agent_tools_panel, src/gui_2.py:App._render_persona_selector_panel, src/multi_agent_conductor.py:run_worker_lifecycle]
"""
global _active_bias_profile global _active_bias_profile
if not profile_name or profile_name == "None": if not profile_name or profile_name == "None":
_active_bias_profile = None _active_bias_profile = None
@@ -1191,7 +1180,7 @@ def _repair_anthropic_history(history: list[dict[str, Any]]) -> None:
], ],
}) })
def _send_anthropic_result(md_content: str, user_message: str, base_dir: str, file_items: list[dict[str, Any]] | None = None, discussion_history: str = "", pre_tool_callback: Optional[Callable[[str, str, Optional[Callable[[str], str]]], Optional[str]]] = None, qa_callback: Optional[Callable[[str], str]] = None, stream_callback: Optional[Callable[[str], None]] = None, patch_callback: Optional[Callable[[str, str], Optional[str]]] = None) -> Result[str]: def _send_anthropic(md_content: str, user_message: str, base_dir: str, file_items: list[dict[str, Any]] | None = None, discussion_history: str = "", pre_tool_callback: Optional[Callable[[str, str, Optional[Callable[[str], str]]], Optional[str]]] = None, qa_callback: Optional[Callable[[str], str]] = None, stream_callback: Optional[Callable[[str], None]] = None, patch_callback: Optional[Callable[[str, str], Optional[str]]] = None) -> Result[str]:
""" """
[C: src/ai_server.py:_handle_send] [C: src/ai_server.py:_handle_send]
""" """
@@ -1434,7 +1423,7 @@ def _get_gemini_history_list(chat: Any | None) -> list[Any]:
return cast(list[Any], chat.get_history()) return cast(list[Any], chat.get_history())
return [] return []
def _send_gemini_result(md_content: str, user_message: str, base_dir: str, def _send_gemini(md_content: str, user_message: str, base_dir: str,
file_items: list[dict[str, Any]] | None = None, file_items: list[dict[str, Any]] | None = None,
discussion_history: str = "", discussion_history: str = "",
pre_tool_callback: Optional[Callable[[str, str, Optional[Callable[[str], str]]], Optional[str]]] = None, pre_tool_callback: Optional[Callable[[str, str, Optional[Callable[[str], str]]], Optional[str]]] = None,
@@ -1670,7 +1659,7 @@ def _send_gemini_result(md_content: str, user_message: str, base_dir: str,
if monitor.enabled: monitor.end_component("ai_client._send_gemini") if monitor.enabled: monitor.end_component("ai_client._send_gemini")
return Result(data="", errors=[_classify_gemini_error(e, source="ai_client.gemini")]) return Result(data="", errors=[_classify_gemini_error(e, source="ai_client.gemini")])
def _send_gemini_cli_result(md_content: str, user_message: str, base_dir: str, def _send_gemini_cli(md_content: str, user_message: str, base_dir: str,
file_items: list[dict[str, Any]] | None = None, file_items: list[dict[str, Any]] | None = None,
discussion_history: str = "", discussion_history: str = "",
pre_tool_callback: Optional[Callable[[str, str, Optional[Callable[[str], str]]], Optional[str]]] = None, pre_tool_callback: Optional[Callable[[str, str, Optional[Callable[[str], str]]], Optional[str]]] = None,
@@ -1815,7 +1804,7 @@ def _ensure_deepseek_client() -> None:
_load_credentials() _load_credentials()
pass pass
def _send_deepseek_result(md_content: str, user_message: str, base_dir: str, def _send_deepseek(md_content: str, user_message: str, base_dir: str,
file_items: list[dict[str, Any]] | None = None, file_items: list[dict[str, Any]] | None = None,
discussion_history: str = "", discussion_history: str = "",
stream: bool = False, stream: bool = False,
@@ -2076,6 +2065,7 @@ def _send_deepseek_result(md_content: str, user_message: str, base_dir: str,
#region: MiniMax Provider #region: MiniMax Provider
#TODO(Ed): This causes a pause on gui thread, this should be cached.
def _list_minimax_models(api_key: str) -> list[str]: def _list_minimax_models(api_key: str) -> list[str]:
try: try:
openai = _require_warmed("openai") openai = _require_warmed("openai")
@@ -2090,14 +2080,11 @@ def _list_minimax_models(api_key: str) -> list[str]:
return ["MiniMax-M2.7", "MiniMax-M2.5", "MiniMax-M2.1", "MiniMax-M2"] return ["MiniMax-M2.7", "MiniMax-M2.5", "MiniMax-M2.1", "MiniMax-M2"]
def _repair_minimax_history(history: list[dict[str, Any]]) -> None: def _repair_minimax_history(history: list[dict[str, Any]]) -> None:
if not history: if not history: return
return
last = history[-1] last = history[-1]
if last.get("role") != "assistant": if last.get("role") != "assistant": return
return
tool_calls = last.get("tool_calls", []) tool_calls = last.get("tool_calls", [])
if not tool_calls: if not tool_calls: return
return
call_ids = [] call_ids = []
for tc in tool_calls: for tc in tool_calls:
if hasattr(tc, "id"): call_ids.append(tc.id) if hasattr(tc, "id"): call_ids.append(tc.id)
@@ -2332,7 +2319,7 @@ def _list_qwen_models() -> list[str]:
from src.vendor_capabilities import list_models_for_vendor from src.vendor_capabilities import list_models_for_vendor
return list_models_for_vendor("qwen") return list_models_for_vendor("qwen")
def _send_qwen_result(md_content: str, user_message: str, base_dir: str, def _send_qwen(md_content: str, user_message: str, base_dir: str,
file_items: list[dict[str, Any]] | None = None, file_items: list[dict[str, Any]] | None = None,
discussion_history: str = "", discussion_history: str = "",
stream: bool = False, stream: bool = False,
@@ -2481,6 +2468,7 @@ def _send_llama_native(md_content: str, user_message: str, base_dir: str,
return Result(data=(f"<thinking>\n{thinking}\n</thinking>\n" if thinking else "") + text) return Result(data=(f"<thinking>\n{thinking}\n</thinking>\n" if thinking else "") + text)
except Exception as exc: except Exception as exc:
return Result(data="", errors=[ErrorInfo(kind=ErrorKind.INTERNAL, message=str(exc), source="ai_client.llama_native", original=exc)]) return Result(data="", errors=[ErrorInfo(kind=ErrorKind.INTERNAL, message=str(exc), source="ai_client.llama_native", original=exc)])
def _list_llama_models() -> list[str]: def _list_llama_models() -> list[str]:
from src.vendor_capabilities import list_models_for_vendor from src.vendor_capabilities import list_models_for_vendor
return list_models_for_vendor("llama") return list_models_for_vendor("llama")
@@ -2682,22 +2670,22 @@ def send_result(
p = str(_provider).lower().strip() p = str(_provider).lower().strip()
try: try:
if p == "gemini": if p == "gemini":
res = _send_gemini_result( res = _send_gemini(
md_content, user_message, base_dir, file_items, discussion_history, md_content, user_message, base_dir, file_items, discussion_history,
pre_tool_callback, qa_callback, enable_tools, stream_callback, patch_callback pre_tool_callback, qa_callback, enable_tools, stream_callback, patch_callback
) )
elif p == "gemini_cli": elif p == "gemini_cli":
res = _send_gemini_cli_result( res = _send_gemini_cli(
md_content, user_message, base_dir, file_items, discussion_history, md_content, user_message, base_dir, file_items, discussion_history,
pre_tool_callback, qa_callback, stream_callback, patch_callback pre_tool_callback, qa_callback, stream_callback, patch_callback
) )
elif p == "anthropic": elif p == "anthropic":
res = _send_anthropic_result( res = _send_anthropic(
md_content, user_message, base_dir, file_items, discussion_history, md_content, user_message, base_dir, file_items, discussion_history,
pre_tool_callback, qa_callback, stream_callback=stream_callback, patch_callback=patch_callback pre_tool_callback, qa_callback, stream_callback=stream_callback, patch_callback=patch_callback
) )
elif p == "deepseek": elif p == "deepseek":
res = _send_deepseek_result( res = _send_deepseek(
md_content, user_message, base_dir, file_items, discussion_history, md_content, user_message, base_dir, file_items, discussion_history,
stream, pre_tool_callback, qa_callback, stream_callback, patch_callback stream, pre_tool_callback, qa_callback, stream_callback, patch_callback
) )
@@ -2707,7 +2695,7 @@ def send_result(
stream, pre_tool_callback, qa_callback, stream_callback, patch_callback stream, pre_tool_callback, qa_callback, stream_callback, patch_callback
) )
elif p == "qwen": elif p == "qwen":
res = _send_qwen_result( res = _send_qwen(
md_content, user_message, base_dir, file_items, discussion_history, md_content, user_message, base_dir, file_items, discussion_history,
stream, pre_tool_callback, qa_callback, stream_callback, patch_callback stream, pre_tool_callback, qa_callback, stream_callback, patch_callback
) )