๐Ÿ“„ router.py 5,941 bytes Apr 26, 2026 ๐Ÿ“‹ Raw

"""Family document router โ€” sorts classified documents by confidence/priority.

Adapts Costco router.py for family document routing:
- Zones โ†’ Members
- Items โ†’ Documents
- Zone order โ†’ Member priority

Sovereign: Zero imports from costco_route.
"""

from typing import Optional
from icarus.core.family_loader import get_family_config

Member priority order (kids first, then parents, then general family)

MEMBER_ORDER = ["sully", "harper", "aundrea", "matt", "family"]

def generate_routing(
classified: dict[str, list[dict]],
learned_overrides: Optional[dict[str, dict]] = None
) -> list[dict]:
"""Generate prioritized routing for family documents.

Args:
    classified: {member_id: [document_dicts]} from LLM or inference engine
    learned_overrides: Override from ChromaDB memory {pattern: {member_id, ...}}

Returns:
    List of member groups in priority order, each with documents.
"""
if learned_overrides:
    classified = _apply_overrides(classified, learned_overrides)

family_config = get_family_config()
route = []

for member_id in MEMBER_ORDER:
    docs = classified.get(member_id, [])
    if not docs:
        continue

    member = family_config.get_member(member_id) or {"name": member_id.title()}
    route.append({
        "member_id": member_id,
        "member_name": member.get("name", member_id.title()),
        "documents": docs,
        "priority": _calculate_priority(docs),
        "document_count": len(docs)
    })

return route

def _apply_overrides(
classified: dict[str, list[dict]],
overrides: dict[str, dict]
) -> dict[str, list[dict]]:
"""Apply learned overrides from memory.

If a document pattern was previously calibrated to a different member,
move it to the learned member.
"""
# Build lookup: pattern_lower โ†’ override member
override_map = {}
for pattern, override_info in overrides.items():
    override_map[pattern.lower().strip()] = override_info["member_id"]

# Rebuild classified dict with overrides applied
result = {}
for member_id, docs in classified.items():
    remaining = []
    for doc in docs:
        # Check document content for pattern matches
        content = doc.get("content", "").lower().strip()
        matched = False

        for pattern, target_member in override_map.items():
            if pattern in content:
                result.setdefault(target_member, []).append(doc)
                matched = True
                break

        if not matched:
            remaining.append(doc)

    if remaining:
        result[member_id] = remaining

return result

def _calculate_priority(docs: list[dict]) -> float:
"""Calculate priority score for a member's documents.

Factors:
- Urgency (deadline proximity)
- Document confidence
- Presence of deadlines/dates
"""
if not docs:
    return 0.0

scores = []
for doc in docs:
    score = 0.5  # Base priority

    # Higher confidence = higher priority
    confidence = doc.get("confidence", 0.5)
    score += confidence * 0.3

    # Deadline proximity boost
    if doc.get("deadline"):
        score += 0.2

    # Urgent keywords
    content = doc.get("content", "").lower()
    if any(word in content for word in ["urgent", "deadline", "required", "mandatory"]):
        score += 0.15

    scores.append(min(score, 1.0))

return round(sum(scores) / len(scores), 2)

def format_briefing_queue(route: list[dict], family_name: str = "Hoffmann") -> str:
"""Format routing into a Telegram-friendly briefing queue.

Args:
    route: Output from generate_routing()
    family_name: Display name for the family

Returns:
    Formatted string ready for Telegram.
"""
lines = [f"๐Ÿ“‹ Document Queue โ€” {family_name} Family"]

total_docs = sum(r["document_count"] for r in route)
member_count = len(route)

for member_group in route:
    member_id = member_group["member_id"]
    docs = member_group["documents"]
    priority = member_group["priority"]

    # Emoji based on member
    emoji = {
        "sully": "๐Ÿ‘ฆ",
        "harper": "๐Ÿ‘ง",
        "aundrea": "๐Ÿ‘ฉ",
        "matt": "๐Ÿ‘จ",
        "family": "๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ"
    }.get(member_id, "๐Ÿ“„")

    lines.append(f"\n{emoji} {member_group['member_name']} โ€” "
                f"{len(docs)} doc(s) (priority: {priority:.0%})")

    for doc in docs:
        title = doc.get("title", "Untitled")
        confidence = doc.get("confidence", 0.0)
        conf_emoji = "๐ŸŸข" if confidence > 0.9 else "๐ŸŸก" if confidence > 0.7 else "๐Ÿ”ด"
        lines.append(f"  {conf_emoji} {title}")

lines.append(f"\nโœ… {total_docs} documents across {member_count} members")

return "\n".join(lines)

def format_briefing_queue_markdown(route: list[dict], family_name: str = "Hoffmann") -> str:
"""Format routing with markdown (for CLI / non-Telegram output)."""
lines = [f"## ๐Ÿ“‹ Document Queue โ€” {family_name} Family"]

total_docs = sum(r["document_count"] for r in route)
member_count = len(route)

for member_group in route:
    member_id = member_group["member_id"]
    docs = member_group["documents"]
    priority = member_group["priority"]

    lines.append(f"\n### {member_group['member_name']} "
                f"({len(docs)} docs, priority: {priority:.0%})")

    for doc in docs:
        title = doc.get("title", "Untitled")
        confidence = doc.get("confidence", 0.0)
        lines.append(f"- [{title}](confidence: {confidence:.0%})")

lines.append(f"\n**{total_docs} documents across {member_count} members**")

return "\n".join(lines)