Files
DemonGatanjieu-Anomalous_Mo…/api/ai_task_links.py
T
DemonGatanjieuandClaude Opus 5.5 fad6f98121 Name an unnamed task in the page's words, say plainly what the short tool set is for
A task the AI never names (mostly one asked for in the AI app's own chat, with no begin_task) took
the summary of its first step that did something as its title, so the list read "run_workflow"
or "new_workflow: anima test". ai_task_title.js shows such a title (title_from "auto", now in the
list's summaries and the task links) with the tool in the page's words, "新建了工作流:anima test";
a title the user or the AI gave stays as written, and every page naming a task uses it. Renaming
starts from that name.

The short set's note said "drawing in the AI app's own chat", which the user read as the picture
being drawn in the AI app. It now says it is for telling the AI to draw right in the app's chat,
the picture still drawn by ComfyUI, and that a task started with New task cannot use it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 23:04:35 +08:00

72 lines
3.2 KiB
Python

"""Which task a task goes on from ("接着"), so work split over tasks is found together: a task an
AI begins as new while on another (ai_tasks.begin_task), or one the user starts while looking at
another (ai_tasks.start_task), keeps that one as its "from". The user can link a task to another
or take the link off (set_from); links() gives a task's own "from" and the tasks that go on from it,
and an AI beginning a task that goes on from another hears what was decided there. A task the user
removes (remove) leaves no gap: those that went on from it go on from what it went on from."""
from . import ai_decisions, ai_tasks
class LinkError(ValueError):
pass
def set_from(task_id, parent_id):
"""Task `task_id` goes on from `parent_id` (None: from none). Refuses a loop. Returns the parent id."""
with ai_tasks._lock:
tasks = ai_tasks._load()
task = tasks.get(task_id)
if not task:
raise LinkError("No such task")
if parent_id is not None:
if parent_id not in tasks or parent_id == task_id:
raise LinkError("No such other task")
seen, at = set(), parent_id
while at and at not in seen: # going up from the parent must not come back here
if at == task_id:
raise LinkError("That task already goes on from this one")
seen.add(at)
at = (tasks.get(at) or {}).get("from")
task["from"] = parent_id
else:
task.pop("from", None)
ai_tasks._save(task)
ai_tasks._notify(task_id)
return parent_id
def remove(task_id):
"""Takes task `task_id` off the list; those that went on from it now go on from what it went on from."""
with ai_tasks._lock:
tasks = ai_tasks._load()
parent = (tasks.get(task_id) or {}).get("from")
for task in tasks.values():
if task.get("from") == task_id:
if parent:
task["from"] = parent
else:
task.pop("from")
ai_tasks._save(task)
return ai_tasks.remove_task(task_id)
def links(task_id):
"""{from: {id, title} | None, next: [{id, title}]} of task `task_id`, the next oldest first."""
with ai_tasks._lock:
tasks = ai_tasks._load()
task = tasks.get(task_id) or {}
brief = lambda item: {"id": item["id"], "title": item.get("title") or "", "title_from": item.get("title_from") or ""} # noqa: E731
parent = tasks.get(task.get("from"))
later = sorted((item for item in tasks.values() if item.get("from") == task_id), key=lambda item: item.get("started", 0))
return {"from": brief(parent) if parent else None, "next": [brief(item) for item in later]}
def carried(task):
"""For an AI beginning `task`: the task it goes on from and what was decided there, or None."""
parent = ai_tasks.get_task(task.get("from")) if task.get("from") else None
if not parent:
return None
return {"task": parent["id"], "title": parent.get("title") or "", "decisions": ai_decisions.lines(parent),
"note": "This task goes on from that one; keep to what was decided there unless the user says otherwise."}