22 model_id = state.get(
"activeModelId")
or state.get(
"model_id")
24 parts.append(f
"Model: {model_id}")
26 session_mode = state.get(
"session_mode")
27 active_model = state.get(
"activeModelId")
31 session_mode = mode_for_model_id(str(active_model))
34 if session_mode ==
"llm" or active_model:
35 parts.append(
"Mode: LLM")
37 parts.append(
"Mode: (no model loaded — ask user to run aquin load)")
39 last_prompt = state.get(
"lastPrompt")
41 parts.append(f
'Last inspected prompt: "{last_prompt}"')
43 top_features = state.get(
"lastTopFeatures")
or []
45 lines = [f
" - F{f['feature_idx']} \"{f['label']}\" (act={f['activation']:.2f})" for f
in top_features[:5]]
46 parts.append(
"Top features from last run:\n" +
"\n".join(lines))
48 memory = state.get(
"memory")
or {}
50 mem_lines = [f
" {k}: {v}" for k, v
in list(memory.items())[:10]]
51 parts.append(
"Session memory:\n" +
"\n".join(mem_lines))
54 base +=
"\n\n[Current context]\n" +
"\n".join(parts)
60 """Return OpenAI-format tool definitions for the active session mode."""
62 from aquin.session_mode import get_filtered_tool_schemas, mode_for_active_model, mode_for_model_id
85 on_token: Callable[[str],
None] |
None =
None,
86 on_tool_start: Callable[[str, dict],
None] |
None =
None,
87 on_tool_result: Callable[[str, Any, bool],
None] |
None =
None,
90 Run one user turn through the orchestrator loop.
92 Streams text as message.append events and dispatches tool calls via the
93 registry, pushing tool.start + tool.result for each. Loops until the model
94 stops requesting tool calls.
102 messages: list[dict[str, Any]] = [
103 {
"role":
"system",
"content": system_prompt},
104 {
"role":
"user",
"content": user_message},
107 agent_url = f
"{base_url.rstrip('/')}/api/sync/openai/chat"
108 assistant_msg_id = str(uuid.uuid4())
113 pending_tool_calls: list[dict[str, Any]] = []
115 request_body: dict[str, Any] = {
116 "model":
"gpt-4o-mini",
117 "messages": messages,
120 request_body[
"tools"] = tools_schema
121 request_body[
"tool_choice"] =
"auto"
127 "Authorization": f
"Bearer {api_key}",
128 "Content-Type":
"application/json",
134 resp.raise_for_status()
137 tool_call_accum: dict[int, dict[str, Any]] = {}
139 for line
in resp.iter_lines():
140 if not line.startswith(
"data: "):
142 raw = line[len(
"data: "):]
146 chunk = json.loads(raw)
147 except json.JSONDecodeError:
150 delta = chunk.get(
"choices", [{}])[0].get(
"delta", {})
153 text_piece = delta.get(
"content")
or ""
155 assistant_text += text_piece
159 print(text_piece, end=
"", flush=
True)
162 for tc_delta
in delta.get(
"tool_calls")
or []:
163 idx = tc_delta.get(
"index", 0)
164 if idx
not in tool_call_accum:
165 tool_call_accum[idx] = {
166 "id": tc_delta.get(
"id",
""),
168 "function": {
"name":
"",
"arguments":
""},
170 acc = tool_call_accum[idx]
171 if tc_delta.get(
"id"):
172 acc[
"id"] = tc_delta[
"id"]
173 fn = tc_delta.get(
"function", {})
175 acc[
"function"][
"name"] += fn[
"name"]
176 if fn.get(
"arguments"):
177 acc[
"function"][
"arguments"] += fn[
"arguments"]
179 pending_tool_calls = list(tool_call_accum.values())
184 session_id=session_id,
186 "type":
"message.append",
189 "id": assistant_msg_id,
191 "content": assistant_text,
201 if not pending_tool_calls:
203 messages.append({
"role":
"assistant",
"content": assistant_text})
209 "content": assistant_text
or None,
210 "tool_calls": pending_tool_calls,
214 for tc
in pending_tool_calls:
215 tool_name = tc[
"function"][
"name"]
216 tool_call_id = tc[
"id"]
219 args = json.loads(tc[
"function"][
"arguments"]
or "{}")
220 except json.JSONDecodeError:
224 on_tool_start(tool_name, args)
228 session_id=session_id,
230 "type":
"tool.start",
232 "tool_call_id": tool_call_id,
233 "tool_name": tool_name,
244 from aquin.session_mode import is_tool_allowed, mode_for_active_model, mode_for_model_id
246 model_id = state.get(
"activeModelId")
249 mode = mode_for_model_id(str(model_id))
251 mode = mode_for_active_model()
253 mode = mode_for_active_model()
255 raise NotImplementedError(
"Load a model first: aquin load --model <id>")
256 if not is_tool_allowed(tool_name, mode):
257 raise NotImplementedError(
258 f
"Tool '{tool_name}' is not available in {mode} session mode"
261 result = dispatch_tool(tool_name, args, {
262 "session_id": session_id,
264 "base_url": base_url,
266 "tool_call_id": tool_call_id,
268 card = result.get(
"card")
if isinstance(result, dict)
else None
269 if card
is None and isinstance(result, dict):
271 card = card_mapper.to_card(tool_name, result)
272 if isinstance(result, dict):
273 result_content = result.get(
"content")
or {
274 k: v
for k, v
in result.items()
if k
not in (
"card",
"capture")
277 result_content = result
278 if isinstance(result_content, dict):
280 result_content = slim_tool_result_for_sync(tool_name, result_content, card)
281 except NotImplementedError
as exc:
282 result_content = {
"error": str(exc)}
285 except Exception
as exc:
286 result_content = {
"error": f
"Tool '{tool_name}' failed: {exc}"}
291 is_error = isinstance(result_content, dict)
and "error" in result_content
292 on_tool_result(tool_name, result_content, is_error)
295 tool_result_payload: dict[str, Any] = {
296 "tool_call_id": tool_call_id,
297 "result": result_content,
300 tool_result_payload[
"card"] = card
302 result_events: list[dict] = [{
"type":
"tool.result",
"payload": tool_result_payload}]
303 if isinstance(result, dict)
and result.get(
"capture"):
304 result_events.append({
"type":
"capture.ready",
"payload": result[
"capture"]})
307 session_id=session_id,
308 events=result_events,
311 timeout=90.0
if tool_name ==
"ensure_umap_loaded" else 15.0,
317 "tool_call_id": tool_call_id,
318 "content": json.dumps(result_content)
if not isinstance(result_content, str)
else result_content,
322 assistant_msg_id = str(uuid.uuid4())
None run_agent_turn(str session_id, str user_message, dict[str, Any] state, str api_key, str base_url, Callable[[str], None]|None on_token=None, Callable[[str, dict], None]|None on_tool_start=None, Callable[[str, Any, bool], None]|None on_tool_result=None)