feat(channels): split send_progress into send_progress + send_tool_hints
This commit is contained in:
@@ -27,7 +27,7 @@ from nanobot.providers.base import LLMProvider
|
|||||||
from nanobot.session.manager import Session, SessionManager
|
from nanobot.session.manager import Session, SessionManager
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from nanobot.config.schema import ExecToolConfig
|
from nanobot.config.schema import ChannelsConfig, ExecToolConfig
|
||||||
from nanobot.cron.service import CronService
|
from nanobot.cron.service import CronService
|
||||||
|
|
||||||
|
|
||||||
@@ -59,9 +59,11 @@ class AgentLoop:
|
|||||||
restrict_to_workspace: bool = False,
|
restrict_to_workspace: bool = False,
|
||||||
session_manager: SessionManager | None = None,
|
session_manager: SessionManager | None = None,
|
||||||
mcp_servers: dict | None = None,
|
mcp_servers: dict | None = None,
|
||||||
|
channels_config: ChannelsConfig | None = None,
|
||||||
):
|
):
|
||||||
from nanobot.config.schema import ExecToolConfig
|
from nanobot.config.schema import ExecToolConfig
|
||||||
self.bus = bus
|
self.bus = bus
|
||||||
|
self.channels_config = channels_config
|
||||||
self.provider = provider
|
self.provider = provider
|
||||||
self.workspace = workspace
|
self.workspace = workspace
|
||||||
self.model = model or provider.get_default_model()
|
self.model = model or provider.get_default_model()
|
||||||
@@ -172,7 +174,7 @@ class AgentLoop:
|
|||||||
async def _run_agent_loop(
|
async def _run_agent_loop(
|
||||||
self,
|
self,
|
||||||
initial_messages: list[dict],
|
initial_messages: list[dict],
|
||||||
on_progress: Callable[[str], Awaitable[None]] | None = None,
|
on_progress: Callable[..., Awaitable[None]] | None = None,
|
||||||
) -> tuple[str | None, list[str]]:
|
) -> tuple[str | None, list[str]]:
|
||||||
"""Run the agent iteration loop. Returns (final_content, tools_used)."""
|
"""Run the agent iteration loop. Returns (final_content, tools_used)."""
|
||||||
messages = initial_messages
|
messages = initial_messages
|
||||||
@@ -196,8 +198,7 @@ class AgentLoop:
|
|||||||
clean = self._strip_think(response.content)
|
clean = self._strip_think(response.content)
|
||||||
if clean:
|
if clean:
|
||||||
await on_progress(clean)
|
await on_progress(clean)
|
||||||
else:
|
await on_progress(self._tool_hint(response.tool_calls), tool_hint=True)
|
||||||
await on_progress(self._tool_hint(response.tool_calls))
|
|
||||||
|
|
||||||
tool_call_dicts = [
|
tool_call_dicts = [
|
||||||
{
|
{
|
||||||
@@ -383,9 +384,10 @@ class AgentLoop:
|
|||||||
channel=msg.channel, chat_id=msg.chat_id,
|
channel=msg.channel, chat_id=msg.chat_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def _bus_progress(content: str) -> None:
|
async def _bus_progress(content: str, *, tool_hint: bool = False) -> None:
|
||||||
meta = dict(msg.metadata or {})
|
meta = dict(msg.metadata or {})
|
||||||
meta["_progress"] = True
|
meta["_progress"] = True
|
||||||
|
meta["_tool_hint"] = tool_hint
|
||||||
await self.bus.publish_outbound(OutboundMessage(
|
await self.bus.publish_outbound(OutboundMessage(
|
||||||
channel=msg.channel, chat_id=msg.chat_id, content=content, metadata=meta,
|
channel=msg.channel, chat_id=msg.chat_id, content=content, metadata=meta,
|
||||||
))
|
))
|
||||||
|
|||||||
@@ -193,8 +193,11 @@ class ChannelManager:
|
|||||||
timeout=1.0
|
timeout=1.0
|
||||||
)
|
)
|
||||||
|
|
||||||
if msg.metadata.get("_progress") and not self.config.channels.send_progress:
|
if msg.metadata.get("_progress"):
|
||||||
continue
|
if msg.metadata.get("_tool_hint") and not self.config.channels.send_tool_hints:
|
||||||
|
continue
|
||||||
|
if not msg.metadata.get("_tool_hint") and not self.config.channels.send_progress:
|
||||||
|
continue
|
||||||
|
|
||||||
channel = self.channels.get(msg.channel)
|
channel = self.channels.get(msg.channel)
|
||||||
if channel:
|
if channel:
|
||||||
|
|||||||
@@ -368,6 +368,7 @@ def gateway(
|
|||||||
restrict_to_workspace=config.tools.restrict_to_workspace,
|
restrict_to_workspace=config.tools.restrict_to_workspace,
|
||||||
session_manager=session_manager,
|
session_manager=session_manager,
|
||||||
mcp_servers=config.tools.mcp_servers,
|
mcp_servers=config.tools.mcp_servers,
|
||||||
|
channels_config=config.channels,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Set cron callback (needs agent)
|
# Set cron callback (needs agent)
|
||||||
@@ -484,6 +485,7 @@ def agent(
|
|||||||
cron_service=cron,
|
cron_service=cron,
|
||||||
restrict_to_workspace=config.tools.restrict_to_workspace,
|
restrict_to_workspace=config.tools.restrict_to_workspace,
|
||||||
mcp_servers=config.tools.mcp_servers,
|
mcp_servers=config.tools.mcp_servers,
|
||||||
|
channels_config=config.channels,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Show spinner when logs are off (no output to miss); skip when logs are on
|
# Show spinner when logs are off (no output to miss); skip when logs are on
|
||||||
@@ -494,7 +496,12 @@ def agent(
|
|||||||
# Animated spinner is safe to use with prompt_toolkit input handling
|
# Animated spinner is safe to use with prompt_toolkit input handling
|
||||||
return console.status("[dim]nanobot is thinking...[/dim]", spinner="dots")
|
return console.status("[dim]nanobot is thinking...[/dim]", spinner="dots")
|
||||||
|
|
||||||
async def _cli_progress(content: str) -> None:
|
async def _cli_progress(content: str, *, tool_hint: bool = False) -> None:
|
||||||
|
ch = agent_loop.channels_config
|
||||||
|
if ch and tool_hint and not ch.send_tool_hints:
|
||||||
|
return
|
||||||
|
if ch and not tool_hint and not ch.send_progress:
|
||||||
|
return
|
||||||
console.print(f" [dim]↳ {content}[/dim]")
|
console.print(f" [dim]↳ {content}[/dim]")
|
||||||
|
|
||||||
if message:
|
if message:
|
||||||
@@ -535,7 +542,14 @@ def agent(
|
|||||||
try:
|
try:
|
||||||
msg = await asyncio.wait_for(bus.consume_outbound(), timeout=1.0)
|
msg = await asyncio.wait_for(bus.consume_outbound(), timeout=1.0)
|
||||||
if msg.metadata.get("_progress"):
|
if msg.metadata.get("_progress"):
|
||||||
console.print(f" [dim]↳ {msg.content}[/dim]")
|
is_tool_hint = msg.metadata.get("_tool_hint", False)
|
||||||
|
ch = agent_loop.channels_config
|
||||||
|
if ch and is_tool_hint and not ch.send_tool_hints:
|
||||||
|
pass
|
||||||
|
elif ch and not is_tool_hint and not ch.send_progress:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
console.print(f" [dim]↳ {msg.content}[/dim]")
|
||||||
elif not turn_done.is_set():
|
elif not turn_done.is_set():
|
||||||
if msg.content:
|
if msg.content:
|
||||||
turn_response.append(msg.content)
|
turn_response.append(msg.content)
|
||||||
@@ -961,6 +975,7 @@ def cron_run(
|
|||||||
exec_config=config.tools.exec,
|
exec_config=config.tools.exec,
|
||||||
restrict_to_workspace=config.tools.restrict_to_workspace,
|
restrict_to_workspace=config.tools.restrict_to_workspace,
|
||||||
mcp_servers=config.tools.mcp_servers,
|
mcp_servers=config.tools.mcp_servers,
|
||||||
|
channels_config=config.channels,
|
||||||
)
|
)
|
||||||
|
|
||||||
store_path = get_data_dir() / "cron" / "jobs.json"
|
store_path = get_data_dir() / "cron" / "jobs.json"
|
||||||
|
|||||||
@@ -168,7 +168,8 @@ class QQConfig(Base):
|
|||||||
class ChannelsConfig(Base):
|
class ChannelsConfig(Base):
|
||||||
"""Configuration for chat channels."""
|
"""Configuration for chat channels."""
|
||||||
|
|
||||||
send_progress: bool = False
|
send_progress: bool = True # stream agent's text progress to the channel
|
||||||
|
send_tool_hints: bool = False # stream tool-call hints (e.g. read_file("…"))
|
||||||
whatsapp: WhatsAppConfig = Field(default_factory=WhatsAppConfig)
|
whatsapp: WhatsAppConfig = Field(default_factory=WhatsAppConfig)
|
||||||
telegram: TelegramConfig = Field(default_factory=TelegramConfig)
|
telegram: TelegramConfig = Field(default_factory=TelegramConfig)
|
||||||
discord: DiscordConfig = Field(default_factory=DiscordConfig)
|
discord: DiscordConfig = Field(default_factory=DiscordConfig)
|
||||||
|
|||||||
Reference in New Issue
Block a user