94 lines
3.0 KiB
Python
94 lines
3.0 KiB
Python
"""Interaction commands and user intent for Dvādaśa UX."""
|
|
|
|
import re
|
|
from enum import Enum
|
|
from typing import Any
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
from fusionagi.schemas.head import HeadId
|
|
|
|
|
|
class UserIntent(str, Enum):
|
|
"""User intent derived from commands or natural input."""
|
|
|
|
NORMAL = "normal"
|
|
HEAD_STRATEGY = "head_strategy"
|
|
SHOW_DISSENT = "show_dissent"
|
|
RERUN_RISK = "rerun_risk"
|
|
EXPLAIN_REASONING = "explain_reasoning"
|
|
SOURCES = "sources"
|
|
|
|
|
|
# Command patterns: /command [arg] or /command \n prompt
|
|
_COMMAND_PATTERNS: list[tuple[re.Pattern[str], UserIntent]] = [
|
|
(re.compile(r"^/head\s+(\w+)(?:\s+(.+))?\s*$", re.I | re.DOTALL), UserIntent.HEAD_STRATEGY),
|
|
(re.compile(r"^/show\s+dissent(?:\s+(.+))?\s*$", re.I | re.DOTALL), UserIntent.SHOW_DISSENT),
|
|
(re.compile(r"^/rerun\s+risk(?:\s+(.+))?\s*$", re.I | re.DOTALL), UserIntent.RERUN_RISK),
|
|
(re.compile(r"^/explain\s+reasoning(?:\s+(.+))?\s*$", re.I | re.DOTALL), UserIntent.EXPLAIN_REASONING),
|
|
(re.compile(r"^/sources(?:\s+(.+))?\s*$", re.I | re.DOTALL), UserIntent.SOURCES),
|
|
]
|
|
|
|
|
|
class ParsedCommand(BaseModel):
|
|
"""Parsed user command with intent and optional arguments."""
|
|
|
|
intent: UserIntent = Field(..., description="Detected intent")
|
|
head_id: HeadId | None = Field(default=None, description="For HEAD_STRATEGY: which head")
|
|
raw_input: str = Field(default="", description="Original user input")
|
|
cleaned_prompt: str = Field(default="", description="Prompt with command stripped if applicable")
|
|
|
|
|
|
def parse_user_input(text: str) -> ParsedCommand:
|
|
"""
|
|
Parse user input to detect commands and extract intent.
|
|
|
|
Examples:
|
|
"/head strategy" -> HEAD_STRATEGY, head_id=STRATEGY
|
|
"/show dissent" -> SHOW_DISSENT
|
|
"What is X?" -> NORMAL, cleaned_prompt="What is X?"
|
|
"""
|
|
stripped = (text or "").strip()
|
|
if not stripped:
|
|
return ParsedCommand(
|
|
intent=UserIntent.NORMAL,
|
|
raw_input=stripped,
|
|
cleaned_prompt=stripped,
|
|
)
|
|
|
|
for pattern, intent in _COMMAND_PATTERNS:
|
|
match = pattern.match(stripped)
|
|
if match:
|
|
head_id = None
|
|
cleaned = ""
|
|
if intent == UserIntent.HEAD_STRATEGY:
|
|
arg = (match.group(1) or "").lower()
|
|
rest = match.group(2) if match.lastindex and match.lastindex >= 2 else None
|
|
cleaned = (rest or "").strip()
|
|
try:
|
|
head_id = HeadId(arg)
|
|
except ValueError:
|
|
head_id = None
|
|
else:
|
|
rest = match.group(1) if match.lastindex and match.lastindex >= 1 else None
|
|
cleaned = (rest or "").strip()
|
|
return ParsedCommand(
|
|
intent=intent,
|
|
head_id=head_id,
|
|
raw_input=stripped,
|
|
cleaned_prompt=cleaned,
|
|
)
|
|
|
|
return ParsedCommand(
|
|
intent=UserIntent.NORMAL,
|
|
raw_input=stripped,
|
|
cleaned_prompt=stripped,
|
|
)
|
|
|
|
|
|
__all__ = [
|
|
"UserIntent",
|
|
"ParsedCommand",
|
|
"parse_user_input",
|
|
]
|