Files
DemonGatanjieu-Anomalous_Mo…/api/ai_answer_wait.py
T
DemonGatanjieuandClaude Opus 5.5 cb523930da Guide the AI with fewer detours and guesses
- An AI app calling by an older tool list (tool_help or use_tool not offered,
  use_tool for a tool offered directly, a tool the list lacks) is told so, and
  the task header shows how to reconnect until the app fetches tools/list again.
- add_lora / set_model compare the base models on the model line
  (model_advice.py; the page reports the line) and add base_model_note.
- get_model gives author_examples (sampler, steps, CFG, size, a LoRA's weight
  from the Civitai record's example pictures) and the version name.
- run_workflow says ComfyUI's refusal as which node, what is wrong and the tool
  that fixes it (run_refusal.py; mcp_run.js passes the refusal as data).
- Gates "When I don't answer": wait for me, or go with the recommendation once
  the waiting time runs out (choices with a recommended option only), handed to
  the AI in the same call and shown on the stage and under Decisions.
- A tool budget test (count, size, description length, required arguments) and
  the exam tasks with their scorecard (docs/architecture/tool-exam.md,
  tools/tool_exam.mjs). The tool set's text recommends Full for Antigravity.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 14:26:41 +08:00

78 lines
4.1 KiB
Python

"""How long an AI waits for the user to answer its question (ask_user, a question on a panel).
One tool call waits at most ai_panels.MAX_WAIT (55 s, under the minute AI apps give a call), so
the answer to a question still open says whether to wait on with get_answers: until the time the
user set in the Gates window (ai_gates.answer_wait, from when it asked), and while the user is
typing an answer (the page posts POST /anomalous/ai_tasks/{id}/typing as they type) until a while
after their last key. Then the AI ends its turn and the user brings it back with a line to paste
(web/modules/ai_call_lines.js); or, when the user set so (ai_gates.no_answer "recommended"), each
open choice the AI marked a recommended option for takes it, marked as such for the user and the
AI (recommended_on_timeout), and the AI goes on. Typing is kept in memory only.
"""
import time
from . import ai_gates, ai_panels, ai_tasks
AFTER_TYPING = 60 # the user typed this recently: their answer is on its way
TOOK = ("The user did not answer in time and set that your recommended option goes then: Anomalous took it for "
"them (recommended_on_timeout). Go on with it; a question without a recommended option stays open for the user.")
_typed = {} # task id -> time of the user's last key
def take_recommended(task_id, step):
"""Answers each open choice of panel step `step` that has a recommended option with it. Returns how many."""
taken = 0
for block in step["panel"].get("blocks") or []:
picked = [option["id"] for option in block.get("options") or [] if option.get("recommended")]
if block.get("type") != "choices" or not picked or block.get("id") in (step.get("answers") or {}):
continue
try:
ai_panels.answer(task_id, step["id"], block["id"], choice=picked if block.get("multiple") else picked[:1],
extra={"recommended_on_timeout": True})
taken += 1
except ai_panels.PanelError:
pass
return taken
def typing(task_id, now=None):
"""The user is typing in task `task_id` (an answer or a message). False for no such task."""
if not ai_tasks.get_task(task_id):
return False
_typed[task_id] = time.time() if now is None else now
return True
def wait_on(panel_id, now=None):
"""For a question of panel `panel_id` still open: {keep_waiting, seconds_left?, typing?, note}, or {}."""
with ai_tasks._lock:
task, step = ai_panels._find(panel_id)
if not step:
return {}
now = time.time() if now is None else now
typed = _typed.get(task["id"], 0)
left = max(step["at"] + ai_gates.answer_wait(), typed + AFTER_TYPING) - now
if left <= 0:
if ai_gates.no_answer() == "recommended" and take_recommended(task["id"], step):
return {"keep_waiting": False, "took_recommended": True, "note": TOOK}
return {"keep_waiting": False, "note": "You waited as long as the user set: end your turn now. They answer in "
"Anomalous and tell you in your chat to go on."}
typing_now = now - typed < AFTER_TYPING
return {"keep_waiting": True, "seconds_left": round(left), **({"typing": True} if typing_now else {}),
"note": ("The user is typing their answer" if typing_now else "The user has not answered yet")
+ ": call get_answers(panel, wait_seconds=55) now, and again while keep_waiting is true. Do not "
"end your turn or ask the same in your own chat."}
def for_answer(answer, panel_id=None, now=None):
"""get_answers' `answer`, with how to wait on when nothing came back but questions still open:
for panel `panel_id`, or without one for the newest open question while the user's time runs."""
waiting = answer.get("still_waiting") or []
if not waiting or answer.get("answers") or answer.get("messages"):
return answer
if panel_id is None:
found = [found for item in waiting if (found := wait_on(item["panel"], now)).get("keep_waiting")]
return {**answer, **max(found, key=lambda item: item["seconds_left"])} if found else answer
return {**answer, **wait_on(panel_id, now)}