xml-pipeline/handlers/hello.py
dullfig f75aa3eee6 Add SecureConsole v3.0 with password authentication
- SecureConsole with Argon2id password hashing
- Password stored in ~/.xml-pipeline/console.key
- Protected commands require password re-entry
- Attach/detach model with idle timeout
- Fallback to simple input for incompatible terminals
- @listener message injection into pump
- Boot handler no longer sends to old console
- Response handler prints to stdout with prompt refresh

Dependencies: argon2-cffi, prompt_toolkit

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 18:24:24 -08:00

140 lines
4.6 KiB
Python

"""
hello.py — Multi-agent hello world handlers for testing the message pump.
This module demonstrates a multi-agent flow:
user -> greeter -> shouter -> user
Payload classes:
- Greeting: Initial request with a name
- GreetingResponse: Greeter's response
- ShoutedResponse: Shouter's ALL CAPS version
Handlers:
- handle_greeting: Receives Greeting, sends GreetingResponse to shouter
- handle_shout: Receives GreetingResponse, sends ShoutedResponse to original sender
Usage in organism.yaml:
listeners:
- name: greeter
payload_class: handlers.hello.Greeting
handler: handlers.hello.handle_greeting
- name: shouter
payload_class: handlers.hello.GreetingResponse
handler: handlers.hello.handle_shout
"""
from dataclasses import dataclass
from third_party.xmlable import xmlify
from agentserver.message_bus.message_state import HandlerMetadata, HandlerResponse
@xmlify
@dataclass
class Greeting:
"""Incoming greeting request."""
name: str
@xmlify
@dataclass
class GreetingResponse:
"""Greeter's response - will be forwarded to shouter."""
message: str
original_sender: str # Track who started the conversation
@xmlify
@dataclass
class ShoutedResponse:
"""Shouter's ALL CAPS response - sent back to original sender."""
message: str
async def handle_greeting(payload: Greeting, metadata: HandlerMetadata) -> HandlerResponse:
"""
Handle an incoming Greeting and forward GreetingResponse to shouter.
Flow: console-router -> greeter -> shouter -> response-handler
Demonstrates TodoUntil pattern:
1. Register a watcher for ShoutedResponse from shouter
2. Send GreetingResponse to shouter
3. When ShoutedResponse appears, eyebrow is raised
4. On next invocation, greeter sees nudge and can close the todo
"""
from agentserver.llm import complete
from agentserver.message_bus.todo_registry import get_todo_registry
# Check for any raised todos and close them
todo_registry = get_todo_registry()
if metadata.todo_nudge:
# We have raised todos - check and close them
raised = todo_registry.get_raised_for(metadata.thread_id, metadata.own_name or "greeter")
for watcher in raised:
todo_registry.close(watcher.id)
# In a real scenario, we might log or react to the completed todo
# Register a todo watcher - we want to know when shouter responds
# This demonstrates the "await confirmation" pattern
todo_registry.register(
thread_id=metadata.thread_id,
issuer=metadata.own_name or "greeter",
wait_for="ShoutedResponse",
from_listener="shouter",
description=f"waiting for shouter to process greeting for {payload.name}",
)
# Build system prompt with peer awareness
system_prompt = "You are a friendly greeter. Respond with ONLY a single short enthusiastic greeting sentence. No XML, no markup, just the greeting text."
if metadata.usage_instructions:
system_prompt = metadata.usage_instructions + "\n\n" + system_prompt
# Include any todo nudges in the prompt (for LLM awareness)
if metadata.todo_nudge:
system_prompt = system_prompt + "\n\n" + metadata.todo_nudge
# Use LLM to generate a creative greeting
llm_response = await complete(
model="grok-3-mini-beta",
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": f"Greet {payload.name} enthusiastically."},
],
agent_id=metadata.own_name,
temperature=0.9,
)
# Return clean dataclass + target - pump handles envelope
return HandlerResponse(
payload=GreetingResponse(
message=llm_response.content,
original_sender="response-handler",
),
to="shouter",
)
async def handle_shout(payload: GreetingResponse, metadata: HandlerMetadata) -> HandlerResponse:
"""
Handle GreetingResponse by shouting it back to original sender.
Flow: greeter -> shouter -> original_sender (response-handler)
"""
# Return clean dataclass + target - pump handles envelope
return HandlerResponse(
payload=ShoutedResponse(message=payload.message.upper()),
to=payload.original_sender,
)
async def handle_response_print(payload: ShoutedResponse, metadata: HandlerMetadata) -> None:
"""
Print the final response to stdout.
This is a simple terminal handler for the SecureConsole flow.
"""
# Print on fresh line with color formatting, then reprint prompt
print(f"\n\033[36m[response] {payload.message}\033[0m")
print("> ", end="", flush=True) # Reprint prompt
return None