Interface Path System

The Interface Path system is a unified hierarchical addressing mechanism used throughout Synthetic Heart to consistently identify and route messages across different chat platforms and conversation contexts.

Overview

Interface paths provide a standardized way to address conversations regardless of the underlying platform (Telegram, Discord, Matrix, etc.). This abstraction layer enables:

  • Consistent Conversation Identification: Same addressing format across all interfaces

  • Thread Support: Hierarchical paths support nested conversations (threads, topics)

  • Platform Agnostic Routing: Components can work with any interface without platform-specific code

  • Backward Compatibility: Legacy chat_id/thread_id systems are supported through conversion utilities

Path Format

Interface paths follow a hierarchical structure separated by forward slashes:

interface_name/target_identifier[/thread_identifier]

Components:

  • interface_name: The interface type (e.g., telegram_bot, discord_bot, matrix)

  • target_identifier: Primary conversation identifier (chat_id, channel_id, room_id, etc.)

  • thread_identifier (optional): Thread or sub-conversation identifier

Examples:

  • telegram_bot/-1001234567890 - Telegram channel/group chat

  • telegram_bot/123456789/987 - Telegram chat with thread

  • discord_bot/987654321/123456 - Discord channel with thread

  • matrix/!room:matrix.org/$event - Matrix room with thread event

Core Utilities

The core/interface_path_utils.py module provides essential functions for working with interface paths:

Building Paths

from core.interface_path_utils import build_interface_path

# Build from components
path = build_interface_path('telegram_bot', '123456789', '987')
# Result: 'telegram_bot/123456789/987'

# Build without thread
path = build_interface_path('telegram_bot', '123456789')
# Result: 'telegram_bot/123456789'

Legacy Conversion

from core.interface_path_utils import build_interface_path_from_legacy

# Convert old chat_id/thread_id format
path = build_interface_path_from_legacy('telegram_bot', '123456789', '987')
# Result: 'telegram_bot/123456789/987'

Parsing Paths

from core.interface_path_utils import parse_interface_path

# Parse path into components
components = parse_interface_path('telegram_bot/123456789/987')
# Result: {'interface': 'telegram_bot', 'target': '123456789', 'thread': '987'}

# Parse without thread
components = parse_interface_path('telegram_bot/123456789')
# Result: {'interface': 'telegram_bot', 'target': '123456789', 'thread': None}

Validation

from core.interface_path_utils import is_valid_interface_path

# Validate path format
valid = is_valid_interface_path('telegram_bot/123456789/987')  # True
valid = is_valid_interface_path('invalid_path')  # False

Database Integration

The chat history cache has been updated to use interface paths as the primary key:

Schema Changes:

  • Removed separate chat_id, interface, thread_id columns

  • Added single interface_path VARCHAR(512) column

  • Updated all indexes and constraints

Migration Impact:

All chat history operations now use interface paths:

  • save_chat_message(interface_path, ...)

  • load_chat_history(interface_path, ...)

  • clear_chat_history(interface_path, ...)

Message Flow Integration

Interface paths are integrated throughout the message processing pipeline:

  1. Interface Reception: Each interface generates an interface path when receiving messages

  2. Context Building: Paths are included in message context dictionaries

  3. LLM Processing: Prompts include interface path information for context-aware responses

  4. Action Routing: Message plugins reconstruct paths for proper delivery

  5. Response Sending: Interfaces parse paths to extract platform-specific identifiers

Example Flow (Telegram):

# 1. Interface generates path
interface_path = build_interface_path('telegram_bot', str(chat_id), str(thread_id))

# 2. Context includes path
context = {
    'interface_path': interface_path,
    'message': user_message,
    # ... other context
}

# 3. LLM receives path in prompt
prompt = f"Respond to message from {interface_path}: {user_message}"

# 4. Action includes path
action = {
    'action': 'message_telegram_bot',
    'text': response,
    'interface_path': interface_path  # Critical for routing
}

# 5. Plugin reconstructs and sends
send_payload = {
    'text': response,
    'interface_path': interface_path
}

Interface-Specific Implementations

Telegram Bot

  • Path Format: telegram_bot/chat_id[/thread_id]

  • Generation: build_interface_path('telegram_bot', str(message.chat_id), str(thread_id))

  • Validation: Requires interface_path in message actions

  • Thread Support: Handles Telegram topic threads

Matrix Interface

  • Path Format: matrix/room_identifier[/thread_event_id]

  • Generation: build_interface_path('matrix', room_identifier, thread_event_id)

  • Thread Support: Uses Matrix thread events

Ollama Server

  • Path Format: ollama_serve/conversation_id

  • Generation: build_interface_path('ollama_serve', chat_id)

  • Thread Support: Single-level conversations

Best Practices

Always Use Interface Paths:

  • Never pass separate chat_id and thread_id parameters

  • Include interface_path in all message-related actions

  • Use input.payload.source.interface_path for replies

Validation:

  • Validate paths before using: is_valid_interface_path(path)

  • Handle invalid paths gracefully with fallback mechanisms

Logging:

  • Include interface paths in debug logs for traceability

  • Use paths instead of legacy identifiers in error messages

Error Message Routing

Changed in version 1.0: LLM error messages now correctly follow the interface_path from the original message.

When LLM processing fails (JSON parsing errors, timeouts, validation failures), the system sends fallback error messages. These messages are automatically routed to the same interface and conversation using the preserved interface_path.

Implementation:

The error routing is handled in core/message_chain.pysend_llm_fallback_message():

# Extract interface_path from message or context
interface_path = getattr(message, 'interface_path', None)
if not interface_path and context:
    interface_path = context.get('interface_path')

# Route through transport layer with preserved path
await universal_send(
    bot.send_message,
    chat_id,
    text=fallback_text,
    interface_path=interface_path,  # Ensures correct routing
    is_llm_response=True
)

Benefits:

  • Consistent Error Delivery: Errors appear in the same chat where the problem occurred

  • Context Preservation: Users see errors in the correct conversation thread

  • Interface Agnostic: Works across Telegram, Discord, Matrix, etc.

Migration:

  • Use build_interface_path_from_legacy() for gradual migration

  • Update components incrementally to avoid breaking changes

Troubleshooting

Common Issues:

  • Missing interface_path in actions: Ensure LLM prompts include path extraction instructions

  • Path reconstruction failures: Verify message_plugin payload building includes path

  • Thread context loss: Check that thread identifiers are properly preserved

  • Interface routing errors: Validate path parsing in interface handlers

Debug Commands:

# Check interface path parsing
python3 -c "from core.interface_path_utils import parse_interface_path; print(parse_interface_path('telegram_bot/123/456'))"

# Validate path format
python3 -c "from core.interface_path_utils import is_valid_interface_path; print(is_valid_interface_path('telegram_bot/123/456'))"