feat: add image generation tool and WebUI mode
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
committed by
Xubin Ren
co-authored by
Cursor
parent
3a2f47d720
commit
e936ed48bd
+35
-3
@@ -30,6 +30,7 @@ from nanobot.agent.tools.ask import (
|
||||
from nanobot.agent.tools.cron import CronTool
|
||||
from nanobot.agent.tools.file_state import FileStateStore, bind_file_states, reset_file_states
|
||||
from nanobot.agent.tools.filesystem import EditFileTool, ListDirTool, ReadFileTool, WriteFileTool
|
||||
from nanobot.agent.tools.image_generation import ImageGenerationTool
|
||||
from nanobot.agent.tools.message import MessageTool
|
||||
from nanobot.agent.tools.notebook import NotebookEditTool
|
||||
from nanobot.agent.tools.registry import ToolRegistry
|
||||
@@ -45,9 +46,11 @@ from nanobot.config.schema import AgentDefaults
|
||||
from nanobot.providers.base import LLMProvider
|
||||
from nanobot.providers.factory import ProviderSnapshot
|
||||
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.progress_events import (
|
||||
build_tool_event_finish_payloads,
|
||||
build_tool_event_start_payload,
|
||||
@@ -58,7 +61,13 @@ from nanobot.utils.runtime import EMPTY_FINAL_RESPONSE_MESSAGE
|
||||
from nanobot.utils.webui_titles import mark_webui_session, maybe_generate_webui_title_after_turn
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from nanobot.config.schema import ChannelsConfig, ExecToolConfig, ToolsConfig, WebToolsConfig
|
||||
from nanobot.config.schema import (
|
||||
ChannelsConfig,
|
||||
ExecToolConfig,
|
||||
ProviderConfig,
|
||||
ToolsConfig,
|
||||
WebToolsConfig,
|
||||
)
|
||||
from nanobot.cron.service import CronService
|
||||
|
||||
|
||||
@@ -215,6 +224,8 @@ class AgentLoop:
|
||||
unified_session: bool = False,
|
||||
disabled_skills: list[str] | None = None,
|
||||
tools_config: ToolsConfig | None = None,
|
||||
image_generation_provider_config: ProviderConfig | None = None,
|
||||
image_generation_provider_configs: dict[str, ProviderConfig] | None = None,
|
||||
provider_snapshot_loader: Callable[[], ProviderSnapshot] | None = None,
|
||||
provider_signature: tuple[object, ...] | None = None,
|
||||
):
|
||||
@@ -250,6 +261,13 @@ class AgentLoop:
|
||||
)
|
||||
self.web_config = web_config or WebToolsConfig()
|
||||
self.exec_config = exec_config or ExecToolConfig()
|
||||
self.tools_config = _tc
|
||||
self._image_generation_provider_configs = dict(image_generation_provider_configs or {})
|
||||
if (
|
||||
image_generation_provider_config is not None
|
||||
and "openrouter" not in self._image_generation_provider_configs
|
||||
):
|
||||
self._image_generation_provider_configs["openrouter"] = image_generation_provider_config
|
||||
self.cron_service = cron_service
|
||||
self.restrict_to_workspace = restrict_to_workspace
|
||||
self._start_time = time.time()
|
||||
@@ -404,6 +422,14 @@ class AgentLoop:
|
||||
user_agent=self.web_config.user_agent,
|
||||
)
|
||||
)
|
||||
if self.tools_config.image_generation.enabled:
|
||||
self.tools.register(
|
||||
ImageGenerationTool(
|
||||
workspace=self.workspace,
|
||||
config=self.tools_config.image_generation,
|
||||
provider_configs=self._image_generation_provider_configs,
|
||||
)
|
||||
)
|
||||
self.tools.register(MessageTool(send_callback=self.bus.publish_outbound, workspace=self.workspace))
|
||||
self.tools.register(SpawnTool(manager=self.subagents))
|
||||
if self.cron_service:
|
||||
@@ -1063,12 +1089,12 @@ class AgentLoop:
|
||||
self.context.build_system_prompt(channel=msg.channel),
|
||||
history,
|
||||
pending_ask_id,
|
||||
msg.content,
|
||||
image_generation_prompt(msg.content, msg.metadata),
|
||||
)
|
||||
else:
|
||||
initial_messages = self.context.build_messages(
|
||||
history=history,
|
||||
current_message=msg.content,
|
||||
current_message=image_generation_prompt(msg.content, msg.metadata),
|
||||
session_summary=pending,
|
||||
media=msg.media if msg.media else None,
|
||||
channel=msg.channel,
|
||||
@@ -1143,6 +1169,11 @@ class AgentLoop:
|
||||
|
||||
# Skip the already-persisted user message when saving the turn
|
||||
save_skip = 1 + len(history) + (1 if user_persisted_early else 0)
|
||||
generated_media = generated_image_paths_from_messages(all_msgs[save_skip:])
|
||||
if generated_media and all_msgs and all_msgs[-1].get("role") == "assistant":
|
||||
existing_media = all_msgs[-1].get("media")
|
||||
media = existing_media if isinstance(existing_media, list) else []
|
||||
all_msgs[-1]["media"] = list(dict.fromkeys([*media, *generated_media]))
|
||||
self._save_turn(session, all_msgs, save_skip)
|
||||
session.enforce_file_cap(on_archive=self.context.memory.raw_archive)
|
||||
self._clear_pending_user_turn(session)
|
||||
@@ -1175,6 +1206,7 @@ class AgentLoop:
|
||||
channel=msg.channel,
|
||||
chat_id=msg.chat_id,
|
||||
content=final_content,
|
||||
media=generated_media,
|
||||
metadata=meta,
|
||||
buttons=buttons,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user