mirror of
https://gitea.toothfairyai.com/ToothFairyAI/tf_code.git
synced 2026-04-09 18:29:39 +00:00
feat: tfcode
This commit is contained in:
282
packages/tfcode/python/tf_sync/tools.py
Normal file
282
packages/tfcode/python/tf_sync/tools.py
Normal file
@@ -0,0 +1,282 @@
|
||||
"""
|
||||
Tool sync module for tfcode.
|
||||
Syncs tools from ToothFairyAI workspace using the official SDK.
|
||||
|
||||
SDK Structure:
|
||||
- agent_functions: API Functions (with request_type)
|
||||
- connections: Provider connections (openai, anthropic, etc.)
|
||||
- agents: TF workspace agents
|
||||
- prompts: Prompt templates (with available_to_agents mapping)
|
||||
"""
|
||||
|
||||
from typing import Any, Optional, List
|
||||
|
||||
from pydantic import BaseModel
|
||||
from toothfairyai.types import AgentFunction
|
||||
|
||||
from tf_sync.config import TFConfig, ToolType, FunctionRequestType
|
||||
|
||||
|
||||
class SyncedTool(BaseModel):
|
||||
"""A tool synced from ToothFairyAI workspace."""
|
||||
|
||||
id: str
|
||||
name: str
|
||||
description: Optional[str] = None
|
||||
tool_type: ToolType
|
||||
|
||||
is_mcp_server: bool = False
|
||||
is_agent_skill: bool = False
|
||||
is_database_script: bool = False
|
||||
|
||||
request_type: Optional[FunctionRequestType] = None
|
||||
url: Optional[str] = None
|
||||
tools: list[str] = []
|
||||
|
||||
authorisation_type: Optional[str] = None
|
||||
|
||||
auth_via: str = "tf_proxy"
|
||||
|
||||
# Coder agent specific fields for prompting/model configuration
|
||||
interpolation_string: Optional[str] = None
|
||||
goals: Optional[str] = None
|
||||
temperature: Optional[float] = None
|
||||
max_tokens: Optional[int] = None
|
||||
llm_base_model: Optional[str] = None
|
||||
llm_provider: Optional[str] = None
|
||||
|
||||
|
||||
class SyncedPrompt(BaseModel):
|
||||
"""A prompt template synced from ToothFairyAI workspace."""
|
||||
|
||||
id: str
|
||||
label: str
|
||||
interpolation_string: str
|
||||
prompt_type: Optional[str] = None
|
||||
available_to_agents: Optional[List[str]] = None
|
||||
description: Optional[str] = None
|
||||
|
||||
|
||||
class ToolSyncResult(BaseModel):
|
||||
"""Result of tool sync operation."""
|
||||
|
||||
success: bool
|
||||
tools: list[SyncedTool] = []
|
||||
prompts: list[SyncedPrompt] = []
|
||||
by_type: dict[str, int] = {}
|
||||
error: Optional[str] = None
|
||||
|
||||
|
||||
def classify_tool(func: AgentFunction) -> ToolType:
|
||||
"""
|
||||
Classify a tool based on its properties.
|
||||
|
||||
Types:
|
||||
- AGENT_SKILL: is_agent_skill=True
|
||||
- API_FUNCTION: has request_type
|
||||
|
||||
Args:
|
||||
func: AgentFunction from TF SDK
|
||||
|
||||
Returns:
|
||||
ToolType enum value
|
||||
"""
|
||||
# Agent skills have is_agent_skill=True
|
||||
if getattr(func, 'is_agent_skill', None) is True:
|
||||
return ToolType.AGENT_SKILL
|
||||
|
||||
# All agent_functions with request_type are API Functions
|
||||
if func.request_type:
|
||||
return ToolType.API_FUNCTION
|
||||
|
||||
return ToolType.API_FUNCTION
|
||||
|
||||
|
||||
def parse_function(func: AgentFunction) -> SyncedTool:
|
||||
"""
|
||||
Parse AgentFunction from SDK into SyncedTool.
|
||||
|
||||
Args:
|
||||
func: AgentFunction from TF SDK
|
||||
|
||||
Returns:
|
||||
SyncedTool instance
|
||||
"""
|
||||
tool_type = classify_tool(func)
|
||||
|
||||
request_type_enum = None
|
||||
if func.request_type:
|
||||
try:
|
||||
request_type_enum = FunctionRequestType(func.request_type)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# API Functions may have user-provided auth (authorisation_type)
|
||||
# or may use TF proxy
|
||||
auth_via = "user_provided" if func.authorisation_type == "api_key" else "tf_proxy"
|
||||
|
||||
# Agent skills use skill script
|
||||
if tool_type == ToolType.AGENT_SKILL:
|
||||
auth_via = "tf_skill"
|
||||
|
||||
return SyncedTool(
|
||||
id=func.id,
|
||||
name=func.name,
|
||||
description=func.description,
|
||||
tool_type=tool_type,
|
||||
request_type=request_type_enum,
|
||||
url=func.url,
|
||||
authorisation_type=func.authorisation_type,
|
||||
auth_via=auth_via,
|
||||
is_agent_skill=tool_type == ToolType.AGENT_SKILL,
|
||||
)
|
||||
|
||||
|
||||
def parse_agent(agent) -> SyncedTool:
|
||||
"""
|
||||
Parse Agent from SDK into SyncedTool.
|
||||
|
||||
Coder agents (mode='coder') are CODER_AGENT type, not skills.
|
||||
|
||||
Args:
|
||||
agent: Agent from TF SDK
|
||||
|
||||
Returns:
|
||||
SyncedTool instance with full agent configuration
|
||||
"""
|
||||
return SyncedTool(
|
||||
id=agent.id,
|
||||
name=agent.label or f"agent_{agent.id[:8]}",
|
||||
description=agent.description,
|
||||
tool_type=ToolType.CODER_AGENT,
|
||||
is_agent_skill=False,
|
||||
auth_via="tf_agent",
|
||||
# Agent prompting configuration
|
||||
interpolation_string=getattr(agent, 'interpolation_string', None),
|
||||
goals=getattr(agent, 'goals', None),
|
||||
# Agent model configuration
|
||||
temperature=getattr(agent, 'temperature', None),
|
||||
max_tokens=getattr(agent, 'max_tokens', None),
|
||||
llm_base_model=getattr(agent, 'llm_base_model', None),
|
||||
llm_provider=getattr(agent, 'llm_provider', None),
|
||||
)
|
||||
|
||||
|
||||
def parse_prompt(prompt) -> SyncedPrompt:
|
||||
"""
|
||||
Parse Prompt from SDK into SyncedPrompt.
|
||||
|
||||
Args:
|
||||
prompt: Prompt from TF SDK
|
||||
|
||||
Returns:
|
||||
SyncedPrompt instance
|
||||
"""
|
||||
return SyncedPrompt(
|
||||
id=prompt.id,
|
||||
label=prompt.label,
|
||||
interpolation_string=prompt.interpolation_string,
|
||||
prompt_type=getattr(prompt, 'prompt_type', None),
|
||||
available_to_agents=getattr(prompt, 'available_to_agents', None),
|
||||
description=getattr(prompt, 'description', None),
|
||||
)
|
||||
|
||||
|
||||
def sync_tools(config: TFConfig) -> ToolSyncResult:
|
||||
"""
|
||||
Sync all tools from ToothFairyAI workspace using SDK.
|
||||
|
||||
Includes:
|
||||
- Agent Functions (API Functions with request_type)
|
||||
- Agent Skills (functions with is_agent_skill=True)
|
||||
- Coder Agents (agents with mode='coder')
|
||||
- Prompts (prompt templates with available_to_agents mapping)
|
||||
|
||||
Args:
|
||||
config: TFConfig instance
|
||||
|
||||
Returns:
|
||||
ToolSyncResult with synced tools and prompts
|
||||
"""
|
||||
try:
|
||||
client = config.get_client()
|
||||
|
||||
# Sync agent functions (API auto-paginates up to 5000)
|
||||
func_result = client.agent_functions.list()
|
||||
tools = [parse_function(f) for f in func_result.items]
|
||||
|
||||
# Sync coder agents (API auto-paginates up to 5000)
|
||||
try:
|
||||
agents_result = client.agents.list()
|
||||
for agent in agents_result.items:
|
||||
if getattr(agent, 'mode', None) == 'coder':
|
||||
tools.append(parse_agent(agent))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Sync prompts (API auto-paginates up to 5000)
|
||||
prompts = []
|
||||
try:
|
||||
prompts_result = client.prompts.list()
|
||||
prompts = [parse_prompt(p) for p in prompts_result.items]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
by_type = {}
|
||||
for tool in tools:
|
||||
type_name = tool.tool_type.value
|
||||
by_type[type_name] = by_type.get(type_name, 0) + 1
|
||||
|
||||
if prompts:
|
||||
by_type['prompt'] = len(prompts)
|
||||
|
||||
return ToolSyncResult(
|
||||
success=True,
|
||||
tools=tools,
|
||||
prompts=prompts,
|
||||
by_type=by_type,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return ToolSyncResult(
|
||||
success=False,
|
||||
error=f"Sync failed: {str(e)}",
|
||||
)
|
||||
|
||||
|
||||
def sync_tools_by_type(
|
||||
config: TFConfig,
|
||||
tool_types: Optional[list[ToolType]] = None,
|
||||
) -> ToolSyncResult:
|
||||
"""
|
||||
Sync tools of specific types from ToothFairyAI workspace.
|
||||
|
||||
Args:
|
||||
config: TFConfig instance
|
||||
tool_types: List of ToolType to sync (None = all)
|
||||
|
||||
Returns:
|
||||
ToolSyncResult with filtered tools
|
||||
"""
|
||||
result = sync_tools(config)
|
||||
|
||||
if not result.success or not tool_types:
|
||||
return result
|
||||
|
||||
filtered = [t for t in result.tools if t.tool_type in tool_types]
|
||||
|
||||
by_type = {}
|
||||
for tool in filtered:
|
||||
type_name = tool.tool_type.value
|
||||
by_type[type_name] = by_type.get(type_name, 0) + 1
|
||||
|
||||
return ToolSyncResult(
|
||||
success=True,
|
||||
tools=filtered,
|
||||
by_type=by_type,
|
||||
)
|
||||
|
||||
|
||||
def sync_api_functions_only(config: TFConfig) -> ToolSyncResult:
|
||||
"""Sync only API Functions (has requestType)."""
|
||||
return sync_tools_by_type(config, [ToolType.API_FUNCTION])
|
||||
Reference in New Issue
Block a user