fix(image-generation): let LLM deliver images via message tool instead of runtime media attachment
The runtime media-attachment mechanism was broken for streaming channels (e.g. WebSocket): the _streamed flag caused _send_once to skip the final OutboundMessage that carried generated media, so images were never delivered. Rather than adding complex coordination between streaming and media delivery, delegate image delivery to the LLM: after generate_image returns artifact paths, the next_step prompt now instructs the LLM to call the message tool with the paths in the media parameter. This works uniformly across all channels, streaming or not. Remove generated_media from TurnContext, _assemble_outbound, and _state_save. Update prompts in identity.md, SKILL.md, message tool description, and artifacts.py to reflect the new flow.
This commit is contained in:
@@ -36,14 +36,12 @@ from nanobot.session.goal_state import (
|
||||
runner_wall_llm_timeout_s,
|
||||
)
|
||||
from nanobot.session.manager import Session, SessionManager
|
||||
from nanobot.utils.artifacts import generated_image_paths_from_messages
|
||||
from nanobot.utils.document import extract_documents
|
||||
from nanobot.utils.helpers import image_placeholder_text
|
||||
from nanobot.utils.helpers import truncate_text as truncate_text_fn
|
||||
from nanobot.utils.image_generation_intent import image_generation_prompt
|
||||
from nanobot.utils.llm_runtime import LLMRuntime
|
||||
from nanobot.utils.runtime import EMPTY_FINAL_RESPONSE_MESSAGE
|
||||
from nanobot.utils.session_attachments import merge_turn_media_into_last_assistant
|
||||
from nanobot.utils.webui_turn_helpers import (
|
||||
WebuiTurnCoordinator,
|
||||
build_bus_progress_callback,
|
||||
@@ -103,7 +101,6 @@ class TurnContext:
|
||||
save_skip: int = 0
|
||||
|
||||
outbound: OutboundMessage | None = None
|
||||
generated_media: list[str] = field(default_factory=list)
|
||||
|
||||
on_progress: Callable[..., Awaitable[None]] | None = None
|
||||
on_stream: Callable[[str], Awaitable[None]] | None = None
|
||||
@@ -1194,7 +1191,6 @@ class AgentLoop:
|
||||
all_msgs: list[dict[str, Any]],
|
||||
stop_reason: str,
|
||||
had_injections: bool,
|
||||
generated_media: list[str],
|
||||
on_stream: Callable[[str], Awaitable[None]] | None,
|
||||
*,
|
||||
turn_latency_ms: int | None = None,
|
||||
@@ -1218,7 +1214,6 @@ class AgentLoop:
|
||||
channel=msg.channel,
|
||||
chat_id=msg.chat_id,
|
||||
content=final_content,
|
||||
media=generated_media,
|
||||
metadata=meta,
|
||||
)
|
||||
|
||||
@@ -1348,11 +1343,6 @@ class AgentLoop:
|
||||
ctx.final_content = EMPTY_FINAL_RESPONSE_MESSAGE
|
||||
|
||||
ctx.save_skip = 1 + len(ctx.history) + (1 if ctx.user_persisted_early else 0)
|
||||
skip_msgs = ctx.all_messages[ctx.save_skip:]
|
||||
ctx.generated_media = generated_image_paths_from_messages(skip_msgs)
|
||||
mt = self.tools.get("message")
|
||||
extra = getattr(mt, "turn_delivered_media_paths", lambda: [])() if mt else []
|
||||
merge_turn_media_into_last_assistant(ctx.all_messages, ctx.generated_media, extra)
|
||||
|
||||
ctx.turn_latency_ms = max(0, int((time.time() - ctx.turn_wall_started_at) * 1000))
|
||||
self._save_turn(
|
||||
@@ -1380,7 +1370,6 @@ class AgentLoop:
|
||||
ctx.all_messages,
|
||||
ctx.stop_reason,
|
||||
ctx.had_injections,
|
||||
ctx.generated_media,
|
||||
ctx.on_stream,
|
||||
turn_latency_ms=ctx.turn_latency_ms,
|
||||
)
|
||||
|
||||
@@ -140,8 +140,8 @@ class MessageTool(Tool, ContextAware):
|
||||
"Do not use this for the normal reply in the current chat: answer naturally instead. "
|
||||
"If channel/chat_id would target the current runtime conversation, do not call this tool "
|
||||
"unless the user explicitly asked you to proactively send an existing file attachment. "
|
||||
"When generate_image creates images in the current chat, the final assistant reply "
|
||||
"automatically attaches them; do not call message just to announce or resend them. "
|
||||
"When generate_image creates images in the current chat, use the message tool "
|
||||
"with the artifact paths in the media parameter to deliver the images to the user. "
|
||||
"For proactive attachment delivery, use the 'media' parameter with file paths. "
|
||||
"Do NOT use read_file to send files — that only reads content for your own analysis."
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user