feat(assistant): #91 steps humains en direct + outils web_search/fetch_url (labels backend, SSE step, garde SSRF)
This commit is contained in:
+33
-2
@@ -30,6 +30,7 @@ from backend.tools.api import (
|
||||
call_tool,
|
||||
get_tool_schemas,
|
||||
)
|
||||
from backend.tools.labels import thought_step_label, tool_step_label
|
||||
|
||||
logger = logging.getLogger("obsigate.agent.loop")
|
||||
|
||||
@@ -54,6 +55,9 @@ class ToolCallRecord:
|
||||
arguments: dict[str, Any]
|
||||
ok: bool
|
||||
result: Any
|
||||
# Human-readable « step » label for the Notion-style UI
|
||||
# ({key, params} — see backend.tools.labels).
|
||||
step: dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -63,6 +67,9 @@ class AgentResult:
|
||||
content: str = ""
|
||||
messages: list[dict[str, Any]] = field(default_factory=list)
|
||||
tool_calls: list[ToolCallRecord] = field(default_factory=list)
|
||||
# Ordered Notion-style step descriptors ({key, params}); tool steps and
|
||||
# intermediate reasoning notes interleaved by execution order.
|
||||
steps: list[dict[str, Any]] = field(default_factory=list)
|
||||
iterations: int = 0
|
||||
stopped: str = STOP_DONE
|
||||
pending: dict[str, Any] | None = None
|
||||
@@ -142,7 +149,10 @@ def _execute_confirmed(
|
||||
payload = e.to_dict()
|
||||
ok = False
|
||||
|
||||
record = ToolCallRecord(name=name, arguments=arguments, ok=ok, result=payload)
|
||||
record = ToolCallRecord(
|
||||
name=name, arguments=arguments, ok=ok, result=payload,
|
||||
step=tool_step_label(name, arguments),
|
||||
)
|
||||
executed.append(record)
|
||||
if on_tool_call is not None:
|
||||
on_tool_call(record)
|
||||
@@ -164,6 +174,7 @@ async def run_agent(
|
||||
max_iterations: int = DEFAULT_MAX_ITERATIONS,
|
||||
max_tool_calls: int | None = None,
|
||||
on_tool_call: Callable[[ToolCallRecord], None] | None = None,
|
||||
on_thought: Callable[[dict[str, Any]], None] | None = None,
|
||||
resume_messages: list[dict[str, Any]] | None = None,
|
||||
confirm_pending: dict[str, Any] | None = None,
|
||||
) -> AgentResult:
|
||||
@@ -195,6 +206,15 @@ async def run_agent(
|
||||
if tools is None:
|
||||
tools = get_tool_schemas(scope=ToolScope.IN_APP)
|
||||
quota = DEFAULT_MAX_TOOL_CALLS if max_tool_calls is None else max_tool_calls
|
||||
steps: list[dict[str, Any]] = []
|
||||
|
||||
def _emit_note(text: str) -> None:
|
||||
"""Record an intermediate reasoning note as a visible step."""
|
||||
note = thought_step_label(text)
|
||||
if note["params"]["value"]:
|
||||
steps.append(note)
|
||||
if on_thought is not None:
|
||||
on_thought(note)
|
||||
|
||||
convo = [dict(m) for m in (resume_messages if resume_messages is not None else messages)]
|
||||
executed: list[ToolCallRecord] = []
|
||||
@@ -205,6 +225,7 @@ async def run_agent(
|
||||
content="",
|
||||
messages=convo,
|
||||
tool_calls=executed,
|
||||
steps=steps,
|
||||
iterations=0,
|
||||
stopped=STOP_QUOTA_EXCEEDED,
|
||||
)
|
||||
@@ -218,10 +239,13 @@ async def run_agent(
|
||||
content=response.content or "",
|
||||
messages=convo,
|
||||
tool_calls=executed,
|
||||
steps=steps,
|
||||
iterations=iteration,
|
||||
stopped=STOP_DONE,
|
||||
)
|
||||
|
||||
# Intermediate reasoning shown alongside tool calls → a "thought" step.
|
||||
_emit_note(response.content or "")
|
||||
convo.append(_assistant_tool_message(response.content, response.tool_calls))
|
||||
|
||||
for call in response.tool_calls:
|
||||
@@ -231,6 +255,7 @@ async def run_agent(
|
||||
content=response.content or "",
|
||||
messages=convo,
|
||||
tool_calls=executed,
|
||||
steps=steps,
|
||||
iterations=iteration,
|
||||
stopped=STOP_QUOTA_EXCEEDED,
|
||||
)
|
||||
@@ -247,6 +272,7 @@ async def run_agent(
|
||||
content=response.content or "",
|
||||
messages=convo,
|
||||
tool_calls=executed,
|
||||
steps=steps,
|
||||
iterations=iteration,
|
||||
stopped=STOP_CONFIRMATION_REQUIRED,
|
||||
pending=pending,
|
||||
@@ -255,8 +281,12 @@ async def run_agent(
|
||||
payload = e.to_dict()
|
||||
ok = False
|
||||
|
||||
record = ToolCallRecord(name=call.name, arguments=call.arguments, ok=ok, result=payload)
|
||||
record = ToolCallRecord(
|
||||
name=call.name, arguments=call.arguments, ok=ok, result=payload,
|
||||
step=tool_step_label(call.name, call.arguments),
|
||||
)
|
||||
executed.append(record)
|
||||
steps.append(record.step)
|
||||
if on_tool_call is not None:
|
||||
on_tool_call(record)
|
||||
|
||||
@@ -272,6 +302,7 @@ async def run_agent(
|
||||
content="",
|
||||
messages=convo,
|
||||
tool_calls=executed,
|
||||
steps=steps,
|
||||
iterations=max_iterations,
|
||||
stopped=STOP_MAX_ITERATIONS,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user