forked from cardosofelipe/fast-next-template
Phase 5 of Context Management Engine - Model Adapters: - Add ModelAdapter abstract base class with model matching - Add DefaultAdapter for unknown models (plain text) - Add ClaudeAdapter with XML-based formatting: - <system_instructions> for system context - <reference_documents>/<document> for knowledge - <conversation_history>/<message> for chat - <tool_results>/<tool_result> for tool outputs - XML escaping for special characters - Add OpenAIAdapter with markdown formatting: - ## headers for sections - ### Source headers for documents - **ROLE** bold labels for conversation - Code blocks for tool outputs - Add get_adapter() factory function for model selection Tests: 33 new tests, 256 total context tests passing 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
179 lines
4.5 KiB
Python
179 lines
4.5 KiB
Python
"""
|
|
Base Model Adapter.
|
|
|
|
Abstract base class for model-specific context formatting.
|
|
"""
|
|
|
|
from abc import ABC, abstractmethod
|
|
from typing import Any
|
|
|
|
from ..types import BaseContext, ContextType
|
|
|
|
|
|
class ModelAdapter(ABC):
|
|
"""
|
|
Abstract base adapter for model-specific context formatting.
|
|
|
|
Each adapter knows how to format contexts for optimal
|
|
understanding by a specific LLM family (Claude, OpenAI, etc.).
|
|
"""
|
|
|
|
# Model name patterns this adapter handles
|
|
MODEL_PATTERNS: list[str] = []
|
|
|
|
@classmethod
|
|
def matches_model(cls, model: str) -> bool:
|
|
"""
|
|
Check if this adapter handles the given model.
|
|
|
|
Args:
|
|
model: Model name to check
|
|
|
|
Returns:
|
|
True if this adapter handles the model
|
|
"""
|
|
model_lower = model.lower()
|
|
return any(pattern in model_lower for pattern in cls.MODEL_PATTERNS)
|
|
|
|
@abstractmethod
|
|
def format(
|
|
self,
|
|
contexts: list[BaseContext],
|
|
**kwargs: Any,
|
|
) -> str:
|
|
"""
|
|
Format contexts for the target model.
|
|
|
|
Args:
|
|
contexts: List of contexts to format
|
|
**kwargs: Additional formatting options
|
|
|
|
Returns:
|
|
Formatted context string
|
|
"""
|
|
...
|
|
|
|
@abstractmethod
|
|
def format_type(
|
|
self,
|
|
contexts: list[BaseContext],
|
|
context_type: ContextType,
|
|
**kwargs: Any,
|
|
) -> str:
|
|
"""
|
|
Format contexts of a specific type.
|
|
|
|
Args:
|
|
contexts: List of contexts of the same type
|
|
context_type: The type of contexts
|
|
**kwargs: Additional formatting options
|
|
|
|
Returns:
|
|
Formatted string for this context type
|
|
"""
|
|
...
|
|
|
|
def get_type_order(self) -> list[ContextType]:
|
|
"""
|
|
Get the preferred order of context types.
|
|
|
|
Returns:
|
|
List of context types in preferred order
|
|
"""
|
|
return [
|
|
ContextType.SYSTEM,
|
|
ContextType.TASK,
|
|
ContextType.KNOWLEDGE,
|
|
ContextType.CONVERSATION,
|
|
ContextType.TOOL,
|
|
]
|
|
|
|
def group_by_type(
|
|
self, contexts: list[BaseContext]
|
|
) -> dict[ContextType, list[BaseContext]]:
|
|
"""
|
|
Group contexts by their type.
|
|
|
|
Args:
|
|
contexts: List of contexts to group
|
|
|
|
Returns:
|
|
Dictionary mapping context type to list of contexts
|
|
"""
|
|
by_type: dict[ContextType, list[BaseContext]] = {}
|
|
for context in contexts:
|
|
ct = context.get_type()
|
|
if ct not in by_type:
|
|
by_type[ct] = []
|
|
by_type[ct].append(context)
|
|
return by_type
|
|
|
|
def get_separator(self) -> str:
|
|
"""
|
|
Get the separator between context sections.
|
|
|
|
Returns:
|
|
Separator string
|
|
"""
|
|
return "\n\n"
|
|
|
|
|
|
class DefaultAdapter(ModelAdapter):
|
|
"""
|
|
Default adapter for unknown models.
|
|
|
|
Uses simple plain-text formatting with minimal structure.
|
|
"""
|
|
|
|
MODEL_PATTERNS: list[str] = [] # Fallback adapter
|
|
|
|
@classmethod
|
|
def matches_model(cls, model: str) -> bool:
|
|
"""Always returns True as fallback."""
|
|
return True
|
|
|
|
def format(
|
|
self,
|
|
contexts: list[BaseContext],
|
|
**kwargs: Any,
|
|
) -> str:
|
|
"""Format contexts as plain text."""
|
|
if not contexts:
|
|
return ""
|
|
|
|
by_type = self.group_by_type(contexts)
|
|
parts: list[str] = []
|
|
|
|
for ct in self.get_type_order():
|
|
if ct in by_type:
|
|
formatted = self.format_type(by_type[ct], ct, **kwargs)
|
|
if formatted:
|
|
parts.append(formatted)
|
|
|
|
return self.get_separator().join(parts)
|
|
|
|
def format_type(
|
|
self,
|
|
contexts: list[BaseContext],
|
|
context_type: ContextType,
|
|
**kwargs: Any,
|
|
) -> str:
|
|
"""Format contexts of a type as plain text."""
|
|
if not contexts:
|
|
return ""
|
|
|
|
content = "\n\n".join(c.content for c in contexts)
|
|
|
|
if context_type == ContextType.SYSTEM:
|
|
return content
|
|
elif context_type == ContextType.TASK:
|
|
return f"Task:\n{content}"
|
|
elif context_type == ContextType.KNOWLEDGE:
|
|
return f"Reference Information:\n{content}"
|
|
elif context_type == ContextType.CONVERSATION:
|
|
return f"Previous Conversation:\n{content}"
|
|
elif context_type == ContextType.TOOL:
|
|
return f"Tool Results:\n{content}"
|
|
|
|
return content
|