xml-pipeline/xml_pipeline/message_bus/steps/thread_assignment.py
dullfig e653d63bc1 Rename agentserver to xml_pipeline, add console example
OSS restructuring for open-core model:
- Rename package from agentserver/ to xml_pipeline/
- Update all imports (44 Python files, 31 docs/configs)
- Update pyproject.toml for OSS distribution (v0.3.0)
- Move prompt_toolkit from core to optional [console] extra
- Remove auth/server/lsp from core optional deps (-> Nextra)

New console example in examples/console/:
- Self-contained demo with handlers and config
- Uses prompt_toolkit (optional, falls back to input())
- No password auth, no TUI, no LSP — just the basics
- Shows how to use xml-pipeline as a library

Import changes:
- from agentserver.* -> from xml_pipeline.*
- CLI entry points updated: xml_pipeline.cli:main

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 21:41:19 -08:00

57 lines
No EOL
1.8 KiB
Python

"""
thread_assignment.py — Ensure every message has a valid opaque thread UUID.
The envelope.xsd requires <thread>, but external clients may:
- Omit it (first message)
- Send invalid format
- Send duplicate/malformed UUID
This step enforces:
- Presence of a valid UUID v4 string in <thread>
- If missing or invalid → generate a new one (new root thread)
- Store it in state.thread_id for all downstream use
This guarantees thread continuity and privacy (external parties never see internal hierarchy).
Part of AgentServer v2.1 message pump.
"""
import uuid
from xml_pipeline.message_bus.message_state import MessageState
def _is_valid_uuid(val: str) -> bool:
"""Simple UUID v4 validation — accepts standard string formats."""
try:
uuid_obj = uuid.UUID(val, version=4)
return str(uuid_obj) == val # Ensures canonical lowercase format
except ValueError:
return False
async def thread_assignment_step(state: MessageState) -> MessageState:
"""
Assign or validate the thread UUID.
- If state.thread_id is already set and valid → keep it
- Else → generate new UUID v4
- Always normalizes to lowercase canonical string
This is the source of truth for thread identity throughout the organism.
"""
if state.thread_id and _is_valid_uuid(state.thread_id):
# Already valid — nothing to do
return state
# Invalid, missing, or malformed — generate new root thread
new_thread_id = str(uuid.uuid4())
# Optional: log warning if external client sent bad thread
if state.thread_id:
state.metadata.setdefault("diagnostics", []).append(
f"Invalid external thread ID '{state.thread_id}' — replaced with new root thread"
)
state.thread_id = new_thread_id
return state