feat(ai_client): add send_result() public API returning Result[str]
This commit is contained in:
+11
-11
@@ -26,7 +26,7 @@ separate_tool_calls_panel = true
|
|||||||
bg_shader_enabled = false
|
bg_shader_enabled = false
|
||||||
crt_filter_enabled = false
|
crt_filter_enabled = false
|
||||||
separate_task_dag = false
|
separate_task_dag = false
|
||||||
separate_usage_analytics = true
|
separate_usage_analytics = false
|
||||||
separate_tier1 = false
|
separate_tier1 = false
|
||||||
separate_tier2 = false
|
separate_tier2 = false
|
||||||
separate_tier3 = false
|
separate_tier3 = false
|
||||||
@@ -53,7 +53,7 @@ separate_external_tools = false
|
|||||||
Message = false
|
Message = false
|
||||||
Response = false
|
Response = false
|
||||||
"Tool Calls" = true
|
"Tool Calls" = true
|
||||||
"Text Viewer" = true
|
"Text Viewer" = false
|
||||||
Theme = true
|
Theme = true
|
||||||
"Log Management" = true
|
"Log Management" = true
|
||||||
Diagnostics = false
|
Diagnostics = false
|
||||||
@@ -75,26 +75,26 @@ brightness = 0.7699999809265137
|
|||||||
contrast = 0.7200000286102295
|
contrast = 0.7200000286102295
|
||||||
gamma = 0.6899999976158142
|
gamma = 0.6899999976158142
|
||||||
|
|
||||||
[theme.tone_mapping."Solarized Light"]
|
[theme.tone_mapping.Binks]
|
||||||
brightness = 0.4699999988079071
|
brightness = 0.47999998927116394
|
||||||
contrast = 0.800000011920929
|
contrast = 0.8399999737739563
|
||||||
gamma = 0.6700000166893005
|
gamma = 2.2100000381469727
|
||||||
|
|
||||||
[theme.tone_mapping.solarized_light]
|
[theme.tone_mapping.solarized_light]
|
||||||
brightness = 0.6899999976158142
|
brightness = 0.6899999976158142
|
||||||
contrast = 0.8600000143051147
|
contrast = 0.8600000143051147
|
||||||
gamma = 0.7699999809265137
|
gamma = 0.7699999809265137
|
||||||
|
|
||||||
|
[theme.tone_mapping."Solarized Light"]
|
||||||
|
brightness = 0.4699999988079071
|
||||||
|
contrast = 0.800000011920929
|
||||||
|
gamma = 0.6700000166893005
|
||||||
|
|
||||||
[theme.tone_mapping.moss]
|
[theme.tone_mapping.moss]
|
||||||
brightness = 0.7699999809265137
|
brightness = 0.7699999809265137
|
||||||
contrast = 0.8700000047683716
|
contrast = 0.8700000047683716
|
||||||
gamma = 1.0
|
gamma = 1.0
|
||||||
|
|
||||||
[theme.tone_mapping.Binks]
|
|
||||||
brightness = 0.47999998927116394
|
|
||||||
contrast = 0.8399999737739563
|
|
||||||
gamma = 2.2100000381469727
|
|
||||||
|
|
||||||
[mma]
|
[mma]
|
||||||
max_workers = 4
|
max_workers = 4
|
||||||
|
|
||||||
|
|||||||
+11
-11
@@ -57,7 +57,7 @@ DockId=0x00000010,5
|
|||||||
|
|
||||||
[Window][Tool Calls]
|
[Window][Tool Calls]
|
||||||
Pos=106,92
|
Pos=106,92
|
||||||
Size=1560,1096
|
Size=1560,1108
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002,1
|
DockId=0x00000002,1
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ DockId=0xAFC85805,2
|
|||||||
|
|
||||||
[Window][Theme]
|
[Window][Theme]
|
||||||
Pos=0,28
|
Pos=0,28
|
||||||
Size=104,1160
|
Size=104,1172
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000010,0
|
DockId=0x00000010,0
|
||||||
|
|
||||||
@@ -106,25 +106,25 @@ DockId=0x0000000D,0
|
|||||||
|
|
||||||
[Window][Discussion Hub]
|
[Window][Discussion Hub]
|
||||||
Pos=106,92
|
Pos=106,92
|
||||||
Size=1560,1096
|
Size=1560,1108
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002,0
|
DockId=0x00000002,0
|
||||||
|
|
||||||
[Window][Operations Hub]
|
[Window][Operations Hub]
|
||||||
Pos=0,28
|
Pos=0,28
|
||||||
Size=104,1160
|
Size=104,1172
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000010,4
|
DockId=0x00000010,4
|
||||||
|
|
||||||
[Window][Files & Media]
|
[Window][Files & Media]
|
||||||
Pos=0,28
|
Pos=0,28
|
||||||
Size=104,1160
|
Size=104,1172
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000010,2
|
DockId=0x00000010,2
|
||||||
|
|
||||||
[Window][AI Settings]
|
[Window][AI Settings]
|
||||||
Pos=0,28
|
Pos=0,28
|
||||||
Size=104,1160
|
Size=104,1172
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000010,3
|
DockId=0x00000010,3
|
||||||
|
|
||||||
@@ -410,7 +410,7 @@ DockId=0x00000002,1
|
|||||||
|
|
||||||
[Window][Project Settings]
|
[Window][Project Settings]
|
||||||
Pos=0,28
|
Pos=0,28
|
||||||
Size=104,1160
|
Size=104,1172
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000010,1
|
DockId=0x00000010,1
|
||||||
|
|
||||||
@@ -541,8 +541,8 @@ Size=186,192
|
|||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
[Window][###Text_Viewer_Unified]
|
[Window][###Text_Viewer_Unified]
|
||||||
Pos=443,513
|
Pos=466,615
|
||||||
Size=1021,1293
|
Size=1021,918
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
[Table][0xFB6E3870,4]
|
[Table][0xFB6E3870,4]
|
||||||
@@ -870,11 +870,11 @@ Column 4 Weight=1.0000
|
|||||||
DockNode ID=0x00000008 Pos=3125,170 Size=593,1157 Split=Y
|
DockNode ID=0x00000008 Pos=3125,170 Size=593,1157 Split=Y
|
||||||
DockNode ID=0x00000009 Parent=0x00000008 SizeRef=1029,147 Selected=0x0469CA7A
|
DockNode ID=0x00000009 Parent=0x00000008 SizeRef=1029,147 Selected=0x0469CA7A
|
||||||
DockNode ID=0x0000000A Parent=0x00000008 SizeRef=1029,145 Selected=0xDF822E02
|
DockNode ID=0x0000000A Parent=0x00000008 SizeRef=1029,145 Selected=0xDF822E02
|
||||||
DockSpace ID=0xAFC85805 Window=0x079D3A04 Pos=0,28 Size=1666,1160 Split=X
|
DockSpace ID=0xAFC85805 Window=0x079D3A04 Pos=0,28 Size=1666,1172 Split=X
|
||||||
DockNode ID=0x00000003 Parent=0xAFC85805 SizeRef=2357,1183 Split=X
|
DockNode ID=0x00000003 Parent=0xAFC85805 SizeRef=2357,1183 Split=X
|
||||||
DockNode ID=0x0000000B Parent=0x00000003 SizeRef=404,1186 Split=X Selected=0xF4139CA2
|
DockNode ID=0x0000000B Parent=0x00000003 SizeRef=404,1186 Split=X Selected=0xF4139CA2
|
||||||
DockNode ID=0x00000005 Parent=0x0000000B SizeRef=1426,1681 Split=Y Selected=0x3F1379AF
|
DockNode ID=0x00000005 Parent=0x0000000B SizeRef=1426,1681 Split=Y Selected=0x3F1379AF
|
||||||
DockNode ID=0x00000010 Parent=0x00000005 SizeRef=983,1140 CentralNode=1 Selected=0x7BD57D6A
|
DockNode ID=0x00000010 Parent=0x00000005 SizeRef=983,1140 CentralNode=1 Selected=0x418C7449
|
||||||
DockNode ID=0x00000011 Parent=0x00000005 SizeRef=983,184 Selected=0x432BAE4E
|
DockNode ID=0x00000011 Parent=0x00000005 SizeRef=983,184 Selected=0x432BAE4E
|
||||||
DockNode ID=0x00000006 Parent=0x0000000B SizeRef=1560,1681 Split=Y Selected=0x6F2B5B04
|
DockNode ID=0x00000006 Parent=0x0000000B SizeRef=1560,1681 Split=Y Selected=0x6F2B5B04
|
||||||
DockNode ID=0x00000001 Parent=0x00000006 SizeRef=1560,107 Selected=0x2C0206CE
|
DockNode ID=0x00000001 Parent=0x00000006 SizeRef=1560,107 Selected=0x2C0206CE
|
||||||
|
|||||||
@@ -9,5 +9,5 @@ active = "main"
|
|||||||
|
|
||||||
[discussions.main]
|
[discussions.main]
|
||||||
git_commit = ""
|
git_commit = ""
|
||||||
last_updated = "2026-06-11T21:21:04"
|
last_updated = "2026-06-12T06:55:16"
|
||||||
history = []
|
history = []
|
||||||
|
|||||||
+88
-1
@@ -61,7 +61,7 @@ PROVIDERS: List[str] = ["gemini", "anthropic", "gemini_cli", "deepseek", "minima
|
|||||||
# existing call sites and the T3.1 test (which asserts
|
# existing call sites and the T3.1 test (which asserts
|
||||||
# hasattr(src.ai_client, '_require_warmed')) continue to work.
|
# hasattr(src.ai_client, '_require_warmed')) continue to work.
|
||||||
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 # noqa: E402,F401
|
from src.result_types import ErrorInfo, ErrorKind, Result # noqa: E402,F401
|
||||||
|
|
||||||
|
|
||||||
_provider: str = "gemini"
|
_provider: str = "gemini"
|
||||||
@@ -2768,6 +2768,93 @@ def send(
|
|||||||
if monitor.enabled: monitor.end_component("ai_client.send")
|
if monitor.enabled: monitor.end_component("ai_client.send")
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
def send_result(
|
||||||
|
md_content: str,
|
||||||
|
user_message: str,
|
||||||
|
base_dir: str = ".",
|
||||||
|
file_items: list[dict[str, Any]] | None = None,
|
||||||
|
discussion_history: str = "",
|
||||||
|
stream: bool = False,
|
||||||
|
pre_tool_callback: Optional[Callable[[str, str, Optional[Callable[[str], str]]], Optional[str]]] = None,
|
||||||
|
qa_callback: Optional[Callable[[str], str]] = None,
|
||||||
|
enable_tools: bool = True,
|
||||||
|
stream_callback: Optional[Callable[[str], None]] = None,
|
||||||
|
patch_callback: Optional[Callable[[str, str], Optional[str]]] = None,
|
||||||
|
rag_engine: Optional[Any] = None,
|
||||||
|
) -> Result[str]:
|
||||||
|
"""
|
||||||
|
[NEW] The Result-based public API. Returns Result[str, ErrorInfo].
|
||||||
|
data is the response text on success; errors contains ErrorInfo on failure.
|
||||||
|
[C: tests/test_ai_client_result.py:test_send_result_public_api_returns_result, tests/test_ai_client_result.py:test_send_result_preserves_errors, tests/test_deprecation_warnings.py:test_send_result_does_not_emit_deprecation]
|
||||||
|
"""
|
||||||
|
monitor = performance_monitor.get_monitor()
|
||||||
|
if monitor.enabled: monitor.start_component("ai_client.send_result")
|
||||||
|
|
||||||
|
if rag_engine and getattr(rag_engine.config, "enabled", False) and "## Retrieved Context" not in user_message:
|
||||||
|
chunks = rag_engine.search(user_message)
|
||||||
|
if chunks:
|
||||||
|
context_block = "## Retrieved Context\n\n"
|
||||||
|
for i, chunk in enumerate(chunks):
|
||||||
|
path = chunk.get("metadata", {}).get("path", "unknown")
|
||||||
|
context_block += f"### Chunk {i+1} (Source: {path})\n{chunk.get('document', '')}\n\n"
|
||||||
|
user_message = context_block + user_message
|
||||||
|
|
||||||
|
_append_comms("OUT", "request", {"message": user_message, "system": _get_combined_system_prompt(_active_tool_preset, _active_bias_profile)})
|
||||||
|
with _send_lock:
|
||||||
|
p = str(_provider).lower().strip()
|
||||||
|
try:
|
||||||
|
if p == "gemini":
|
||||||
|
res = _send_gemini_result(
|
||||||
|
md_content, user_message, base_dir, file_items, discussion_history,
|
||||||
|
pre_tool_callback, qa_callback, enable_tools, stream_callback, patch_callback
|
||||||
|
)
|
||||||
|
elif p == "gemini_cli":
|
||||||
|
res = _send_gemini_cli_result(
|
||||||
|
md_content, user_message, base_dir, file_items, discussion_history,
|
||||||
|
pre_tool_callback, qa_callback, stream_callback, patch_callback
|
||||||
|
)
|
||||||
|
elif p == "anthropic":
|
||||||
|
res = _send_anthropic_result(
|
||||||
|
md_content, user_message, base_dir, file_items, discussion_history,
|
||||||
|
pre_tool_callback, qa_callback, stream_callback=stream_callback, patch_callback=patch_callback
|
||||||
|
)
|
||||||
|
elif p == "deepseek":
|
||||||
|
res = _send_deepseek_result(
|
||||||
|
md_content, user_message, base_dir, file_items, discussion_history,
|
||||||
|
stream, pre_tool_callback, qa_callback, stream_callback, patch_callback
|
||||||
|
)
|
||||||
|
elif p == "minimax":
|
||||||
|
res = _send_minimax_result(
|
||||||
|
md_content, user_message, base_dir, file_items, discussion_history,
|
||||||
|
stream, pre_tool_callback, qa_callback, stream_callback, patch_callback
|
||||||
|
)
|
||||||
|
elif p == "qwen":
|
||||||
|
res = _send_qwen_result(
|
||||||
|
md_content, user_message, base_dir, file_items, discussion_history,
|
||||||
|
stream, pre_tool_callback, qa_callback, stream_callback, patch_callback
|
||||||
|
)
|
||||||
|
elif p == "llama":
|
||||||
|
res = _send_llama_result(
|
||||||
|
md_content, user_message, base_dir, file_items, discussion_history,
|
||||||
|
stream, pre_tool_callback, qa_callback, stream_callback, patch_callback
|
||||||
|
)
|
||||||
|
elif p == "grok":
|
||||||
|
res = _send_grok_result(
|
||||||
|
md_content, user_message, base_dir, file_items, discussion_history,
|
||||||
|
stream, pre_tool_callback, qa_callback, stream_callback, patch_callback
|
||||||
|
)
|
||||||
|
elif p == "llama_native":
|
||||||
|
res = _send_llama_native_result(
|
||||||
|
md_content, user_message, base_dir, file_items, discussion_history,
|
||||||
|
stream, pre_tool_callback, qa_callback, stream_callback, patch_callback
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
res = Result(data="", errors=[ErrorInfo(kind=ErrorKind.CONFIG, message=f"unknown provider: {_provider}", source="ai_client.send_result")])
|
||||||
|
except Exception as exc:
|
||||||
|
res = Result(data="", errors=[ErrorInfo(kind=ErrorKind.INTERNAL, message=str(exc), source="ai_client.send_result", original=exc)])
|
||||||
|
if monitor.enabled: monitor.end_component("ai_client.send_result")
|
||||||
|
return res
|
||||||
|
|
||||||
def _add_bleed_derived(d: dict[str, Any], sys_tok: int = 0, tool_tok: int = 0) -> dict[str, Any]:
|
def _add_bleed_derived(d: dict[str, Any], sys_tok: int = 0, tool_tok: int = 0) -> dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
[C: tests/test_token_viz.py:test_add_bleed_derived_aliases, tests/test_token_viz.py:test_add_bleed_derived_breakdown, tests/test_token_viz.py:test_add_bleed_derived_headroom, tests/test_token_viz.py:test_add_bleed_derived_headroom_clamped_to_zero, tests/test_token_viz.py:test_add_bleed_derived_history_clamped_to_zero, tests/test_token_viz.py:test_add_bleed_derived_would_trim_false, tests/test_token_viz.py:test_add_bleed_derived_would_trim_true, tests/test_token_viz.py:test_would_trim_boundary_exact, tests/test_token_viz.py:test_would_trim_just_above_threshold, tests/test_token_viz.py:test_would_trim_just_below_threshold]
|
[C: tests/test_token_viz.py:test_add_bleed_derived_aliases, tests/test_token_viz.py:test_add_bleed_derived_breakdown, tests/test_token_viz.py:test_add_bleed_derived_headroom, tests/test_token_viz.py:test_add_bleed_derived_headroom_clamped_to_zero, tests/test_token_viz.py:test_add_bleed_derived_history_clamped_to_zero, tests/test_token_viz.py:test_add_bleed_derived_would_trim_false, tests/test_token_viz.py:test_add_bleed_derived_would_trim_true, tests/test_token_viz.py:test_would_trim_boundary_exact, tests/test_token_viz.py:test_would_trim_just_above_threshold, tests/test_token_viz.py:test_would_trim_just_below_threshold]
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from src.result_types import Result, ErrorInfo, ErrorKind
|
|||||||
|
|
||||||
def test_send_result_public_api_returns_result() -> None:
|
def test_send_result_public_api_returns_result() -> None:
|
||||||
with patch.object(ai_client, "set_provider"):
|
with patch.object(ai_client, "set_provider"):
|
||||||
with patch.object(ai_client, "_send_anthropic_result", return_value=Result(data="hello")) as mock_send:
|
with patch.object(ai_client, "_send_gemini_result", return_value=Result(data="hello")) as mock_send:
|
||||||
r = ai_client.send_result("system", "user")
|
r = ai_client.send_result("system", "user")
|
||||||
assert isinstance(r, Result)
|
assert isinstance(r, Result)
|
||||||
assert r.ok
|
assert r.ok
|
||||||
@@ -27,7 +27,7 @@ def test_send_deprecated_emits_warning() -> None:
|
|||||||
def test_send_result_preserves_errors() -> None:
|
def test_send_result_preserves_errors() -> None:
|
||||||
err = ErrorInfo(kind=ErrorKind.RATE_LIMIT, message="slow down", source="test")
|
err = ErrorInfo(kind=ErrorKind.RATE_LIMIT, message="slow down", source="test")
|
||||||
with patch.object(ai_client, "set_provider"):
|
with patch.object(ai_client, "set_provider"):
|
||||||
with patch.object(ai_client, "_send_anthropic_result", return_value=Result(data="", errors=[err])):
|
with patch.object(ai_client, "_send_gemini_result", return_value=Result(data="", errors=[err])):
|
||||||
r = ai_client.send_result("system", "user")
|
r = ai_client.send_result("system", "user")
|
||||||
assert not r.ok
|
assert not r.ok
|
||||||
assert r.errors == [err]
|
assert r.errors == [err]
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ def test_send_deprecated_warning_emitted_once_per_site() -> None:
|
|||||||
|
|
||||||
def test_send_result_does_not_emit_deprecation() -> None:
|
def test_send_result_does_not_emit_deprecation() -> None:
|
||||||
with patch.object(ai_client, "set_provider"):
|
with patch.object(ai_client, "set_provider"):
|
||||||
with patch.object(ai_client, "_send_anthropic_result", return_value=Result(data="x")):
|
with patch.object(ai_client, "_send_gemini_result", return_value=Result(data="x")):
|
||||||
with warnings.catch_warnings(record=True) as w:
|
with warnings.catch_warnings(record=True) as w:
|
||||||
warnings.simplefilter("always")
|
warnings.simplefilter("always")
|
||||||
ai_client.send_result("s", "u")
|
ai_client.send_result("s", "u")
|
||||||
|
|||||||
Reference in New Issue
Block a user