116 lines
3.6 KiB
Python
116 lines
3.6 KiB
Python
"""
|
|
ReelForge Tool Naming Convention
|
|
================================
|
|
|
|
All capability tools MUST follow this naming pattern:
|
|
{type}_{id}
|
|
|
|
Where:
|
|
- type: MUST be one of the known capability types (llm, tts, image, etc.)
|
|
- id: Unique identifier for this specific capability
|
|
|
|
Parsing strategy (Fail Fast):
|
|
- Match against known capability types only
|
|
- If no match, return None (fail early to expose configuration errors)
|
|
- No guessing or fallback - explicit is better than implicit
|
|
|
|
Examples:
|
|
✅ llm_call → type: llm, id: call
|
|
✅ tts_edge → type: tts, id: edge
|
|
✅ image_comfykit → type: image, id: comfykit
|
|
|
|
❌ call_llm → Wrong order
|
|
❌ llm-call → Use underscore, not dash
|
|
❌ LLM_call → Use lowercase
|
|
"""
|
|
|
|
from typing import Any, Optional
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
# Known capability types
|
|
CAPABILITY_TYPES = {
|
|
"llm",
|
|
"tts",
|
|
"image",
|
|
}
|
|
|
|
|
|
def parse_tool_name(tool_name: str) -> Optional[tuple[str, str]]:
|
|
"""
|
|
Parse tool name into (type, id) - Fail Fast approach
|
|
|
|
Only accepts tool names that match known capability types.
|
|
Returns None for unknown types to fail early and expose configuration errors.
|
|
|
|
Args:
|
|
tool_name: Tool name following convention (e.g., "llm_qwen", "tts_edge")
|
|
|
|
Returns:
|
|
(type, id) tuple if matches known type, None otherwise
|
|
|
|
Examples:
|
|
>>> parse_tool_name("llm_call")
|
|
('llm', 'call')
|
|
|
|
>>> parse_tool_name("tts_edge")
|
|
('tts', 'edge')
|
|
|
|
>>> parse_tool_name("unknown_type_id")
|
|
None # Fail fast - unknown type
|
|
"""
|
|
# Must contain at least one underscore
|
|
if "_" not in tool_name:
|
|
return None
|
|
|
|
# Only match against known capability types (sorted by length, longest first)
|
|
for cap_type in sorted(CAPABILITY_TYPES, key=len, reverse=True):
|
|
# Match pattern: {known_type}_{id}
|
|
if tool_name.startswith(cap_type + "_"):
|
|
capability_id = tool_name[len(cap_type) + 1:] # +1 for underscore
|
|
if capability_id: # Must have a non-empty id
|
|
return cap_type, capability_id
|
|
|
|
# No match found - return None to fail early
|
|
return None
|
|
|
|
|
|
class CapabilityInfo(BaseModel):
|
|
"""
|
|
Capability information
|
|
|
|
Required: type and id (parsed from tool_name)
|
|
Optional: everything else (from meta.reelforge)
|
|
"""
|
|
|
|
# Required (parsed from tool_name)
|
|
type: str = Field(description="Capability type (llm, tts, etc.)")
|
|
id: str = Field(description="Unique identifier for this capability")
|
|
|
|
# Optional (from meta.reelforge)
|
|
display_name: Optional[str] = Field(
|
|
default=None, description="Human-readable name for UI display"
|
|
)
|
|
description: Optional[str] = Field(default=None, description="Short description")
|
|
is_default: bool = Field(default=False, description="Whether this is the default for this type")
|
|
|
|
# Tool reference
|
|
tool_name: str = Field(description="Original tool name")
|
|
|
|
# Tool reference
|
|
tool: Optional[Any] = Field(default=None, description="Tool object reference", exclude=True)
|
|
|
|
@property
|
|
def display_label(self) -> str:
|
|
"""Get display label for UI"""
|
|
if self.display_name:
|
|
return self.display_name
|
|
# Auto-generate from id: "my_custom_v2" → "My Custom V2"
|
|
return self.id.replace("_", " ").title()
|
|
|
|
@property
|
|
def full_id(self) -> str:
|
|
"""Get full identifier: type/id"""
|
|
return f"{self.type}/{self.id}"
|
|
|