traia-iatp 0.1.29__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of traia-iatp might be problematic. Click here for more details.
- traia_iatp/README.md +368 -0
- traia_iatp/__init__.py +54 -0
- traia_iatp/cli/__init__.py +5 -0
- traia_iatp/cli/main.py +483 -0
- traia_iatp/client/__init__.py +10 -0
- traia_iatp/client/a2a_client.py +274 -0
- traia_iatp/client/crewai_a2a_tools.py +335 -0
- traia_iatp/client/d402_a2a_client.py +293 -0
- traia_iatp/client/grpc_a2a_tools.py +349 -0
- traia_iatp/client/root_path_a2a_client.py +1 -0
- traia_iatp/contracts/__init__.py +12 -0
- traia_iatp/contracts/iatp_contracts_config.py +263 -0
- traia_iatp/contracts/wallet_creator.py +255 -0
- traia_iatp/core/__init__.py +43 -0
- traia_iatp/core/models.py +172 -0
- traia_iatp/d402/__init__.py +55 -0
- traia_iatp/d402/chains.py +102 -0
- traia_iatp/d402/client.py +150 -0
- traia_iatp/d402/clients/__init__.py +7 -0
- traia_iatp/d402/clients/base.py +218 -0
- traia_iatp/d402/clients/httpx.py +219 -0
- traia_iatp/d402/common.py +114 -0
- traia_iatp/d402/encoding.py +28 -0
- traia_iatp/d402/examples/client_example.py +197 -0
- traia_iatp/d402/examples/server_example.py +171 -0
- traia_iatp/d402/facilitator.py +453 -0
- traia_iatp/d402/fastapi_middleware/__init__.py +6 -0
- traia_iatp/d402/fastapi_middleware/middleware.py +225 -0
- traia_iatp/d402/fastmcp_middleware.py +147 -0
- traia_iatp/d402/mcp_middleware.py +434 -0
- traia_iatp/d402/middleware.py +193 -0
- traia_iatp/d402/models.py +116 -0
- traia_iatp/d402/networks.py +98 -0
- traia_iatp/d402/path.py +43 -0
- traia_iatp/d402/payment_introspection.py +104 -0
- traia_iatp/d402/payment_signing.py +178 -0
- traia_iatp/d402/paywall.py +119 -0
- traia_iatp/d402/starlette_middleware.py +326 -0
- traia_iatp/d402/template.py +1 -0
- traia_iatp/d402/types.py +300 -0
- traia_iatp/mcp/__init__.py +18 -0
- traia_iatp/mcp/client.py +201 -0
- traia_iatp/mcp/d402_mcp_tool_adapter.py +361 -0
- traia_iatp/mcp/mcp_agent_template.py +481 -0
- traia_iatp/mcp/templates/Dockerfile.j2 +80 -0
- traia_iatp/mcp/templates/README.md.j2 +310 -0
- traia_iatp/mcp/templates/cursor-rules.md.j2 +520 -0
- traia_iatp/mcp/templates/deployment_params.json.j2 +20 -0
- traia_iatp/mcp/templates/docker-compose.yml.j2 +32 -0
- traia_iatp/mcp/templates/dockerignore.j2 +47 -0
- traia_iatp/mcp/templates/env.example.j2 +57 -0
- traia_iatp/mcp/templates/gitignore.j2 +77 -0
- traia_iatp/mcp/templates/mcp_health_check.py.j2 +150 -0
- traia_iatp/mcp/templates/pyproject.toml.j2 +32 -0
- traia_iatp/mcp/templates/pyrightconfig.json.j2 +22 -0
- traia_iatp/mcp/templates/run_local_docker.sh.j2 +390 -0
- traia_iatp/mcp/templates/server.py.j2 +175 -0
- traia_iatp/mcp/traia_mcp_adapter.py +543 -0
- traia_iatp/preview_diagrams.html +181 -0
- traia_iatp/registry/__init__.py +26 -0
- traia_iatp/registry/atlas_search_indexes.json +280 -0
- traia_iatp/registry/embeddings.py +298 -0
- traia_iatp/registry/iatp_search_api.py +846 -0
- traia_iatp/registry/mongodb_registry.py +771 -0
- traia_iatp/registry/readmes/ATLAS_SEARCH_INDEXES.md +252 -0
- traia_iatp/registry/readmes/ATLAS_SEARCH_SETUP.md +134 -0
- traia_iatp/registry/readmes/AUTHENTICATION_UPDATE.md +124 -0
- traia_iatp/registry/readmes/EMBEDDINGS_SETUP.md +172 -0
- traia_iatp/registry/readmes/IATP_SEARCH_API_GUIDE.md +257 -0
- traia_iatp/registry/readmes/MONGODB_X509_AUTH.md +208 -0
- traia_iatp/registry/readmes/README.md +251 -0
- traia_iatp/registry/readmes/REFACTORING_SUMMARY.md +191 -0
- traia_iatp/scripts/__init__.py +2 -0
- traia_iatp/scripts/create_wallet.py +244 -0
- traia_iatp/server/__init__.py +15 -0
- traia_iatp/server/a2a_server.py +219 -0
- traia_iatp/server/example_template_usage.py +72 -0
- traia_iatp/server/iatp_server_agent_generator.py +237 -0
- traia_iatp/server/iatp_server_template_generator.py +235 -0
- traia_iatp/server/templates/.dockerignore.j2 +48 -0
- traia_iatp/server/templates/Dockerfile.j2 +49 -0
- traia_iatp/server/templates/README.md +137 -0
- traia_iatp/server/templates/README.md.j2 +425 -0
- traia_iatp/server/templates/__init__.py +1 -0
- traia_iatp/server/templates/__main__.py.j2 +565 -0
- traia_iatp/server/templates/agent.py.j2 +94 -0
- traia_iatp/server/templates/agent_config.json.j2 +22 -0
- traia_iatp/server/templates/agent_executor.py.j2 +279 -0
- traia_iatp/server/templates/docker-compose.yml.j2 +23 -0
- traia_iatp/server/templates/env.example.j2 +84 -0
- traia_iatp/server/templates/gitignore.j2 +78 -0
- traia_iatp/server/templates/grpc_server.py.j2 +218 -0
- traia_iatp/server/templates/pyproject.toml.j2 +78 -0
- traia_iatp/server/templates/run_local_docker.sh.j2 +103 -0
- traia_iatp/server/templates/server.py.j2 +243 -0
- traia_iatp/special_agencies/__init__.py +4 -0
- traia_iatp/special_agencies/registry_search_agency.py +392 -0
- traia_iatp/utils/__init__.py +10 -0
- traia_iatp/utils/docker_utils.py +251 -0
- traia_iatp/utils/general.py +64 -0
- traia_iatp/utils/iatp_utils.py +126 -0
- traia_iatp-0.1.29.dist-info/METADATA +423 -0
- traia_iatp-0.1.29.dist-info/RECORD +107 -0
- traia_iatp-0.1.29.dist-info/WHEEL +5 -0
- traia_iatp-0.1.29.dist-info/entry_points.txt +2 -0
- traia_iatp-0.1.29.dist-info/licenses/LICENSE +21 -0
- traia_iatp-0.1.29.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""Example usage of the utility agent template generator.
|
|
2
|
+
|
|
3
|
+
This script demonstrates how to generate a utility agent from an MCP server.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from ..core.models import MCPServer, MCPServerType
|
|
8
|
+
from .iatp_server_agent_generator import IATPServerAgentGenerator
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def main():
|
|
12
|
+
"""Example of generating a utility agent."""
|
|
13
|
+
|
|
14
|
+
# Create an MCP server specification
|
|
15
|
+
mcp_server = MCPServer(
|
|
16
|
+
id="hyperliquid-mcp-001",
|
|
17
|
+
name="hyperliquid-mcp",
|
|
18
|
+
url="http://localhost:3000/mcp",
|
|
19
|
+
server_type=MCPServerType.STREAMABLE_HTTP,
|
|
20
|
+
description="Implements comprehensive trading tools for Hyperliquid L1",
|
|
21
|
+
capabilities=[
|
|
22
|
+
"market_data",
|
|
23
|
+
"place_order",
|
|
24
|
+
"cancel_order",
|
|
25
|
+
"get_positions",
|
|
26
|
+
"get_account_info"
|
|
27
|
+
],
|
|
28
|
+
metadata={
|
|
29
|
+
"version": "1.0.0",
|
|
30
|
+
"author": "Traia"
|
|
31
|
+
}
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
# Create the generator
|
|
35
|
+
generator = IATPServerAgentGenerator(
|
|
36
|
+
output_base_dir=Path("generated_agents")
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
# Generate the utility agent
|
|
40
|
+
agent = generator.generate_agent(
|
|
41
|
+
mcp_server=mcp_server,
|
|
42
|
+
agent_name="Hyperliquid Trading",
|
|
43
|
+
agent_id="hyperliquid-mcp-traia-utility-agent",
|
|
44
|
+
agent_description="A utility agent that exposes Hyperliquid trading capabilities via A2A protocol",
|
|
45
|
+
expose_individual_tools=False, # Just expose one main skill
|
|
46
|
+
auth_required=False,
|
|
47
|
+
use_simple_server=True, # Use the simplified server template
|
|
48
|
+
skill_examples=[
|
|
49
|
+
"Get current market data for ETH-USD",
|
|
50
|
+
"Place a limit order to buy 1 ETH at $3000",
|
|
51
|
+
"Show my current positions",
|
|
52
|
+
"Cancel order with ID 12345",
|
|
53
|
+
"Get my account balance and margin"
|
|
54
|
+
]
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
print(f"Generated utility agent: {agent.name}")
|
|
58
|
+
print(f"ID: {agent.id}")
|
|
59
|
+
print(f"Status: {agent.status}")
|
|
60
|
+
print(f"Code path: {agent.code_path}")
|
|
61
|
+
print(f"Capabilities: {agent.capabilities}")
|
|
62
|
+
|
|
63
|
+
# The generated agent can now be:
|
|
64
|
+
# 1. Pushed to GitHub using push_agency_to_repo
|
|
65
|
+
# 2. Deployed to Cloud Run using cloudrun_deployer
|
|
66
|
+
# 3. Registered in MongoDB for discovery
|
|
67
|
+
|
|
68
|
+
return agent
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
if __name__ == "__main__":
|
|
72
|
+
main()
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
"""Utility agent generator for A2A servers.
|
|
2
|
+
|
|
3
|
+
This module generates utility agents that wrap MCP servers and expose them via A2A protocol.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
import logging
|
|
8
|
+
import shutil
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Dict, Any, Optional
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
|
|
13
|
+
from ..core.models import MCPServer, UtilityAgent, UtilityAgentStatus
|
|
14
|
+
from .iatp_server_template_generator import IATPServerTemplateGenerator
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class IATPServerAgentGenerator:
|
|
20
|
+
"""Generate utility agents from MCP servers using templates."""
|
|
21
|
+
|
|
22
|
+
def __init__(self, output_base_dir: Optional[Path] = None):
|
|
23
|
+
"""Initialize the generator.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
output_base_dir: Base directory for generated agents
|
|
27
|
+
"""
|
|
28
|
+
self.output_base_dir = output_base_dir or Path("generated_agents")
|
|
29
|
+
self.template_generator = IATPServerTemplateGenerator()
|
|
30
|
+
|
|
31
|
+
def _convert_datetime_to_string(self, obj: Any) -> Any:
|
|
32
|
+
"""Convert datetime objects to strings recursively.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
obj: Object that may contain datetime objects
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
Object with datetime objects converted to strings
|
|
39
|
+
"""
|
|
40
|
+
if isinstance(obj, datetime):
|
|
41
|
+
return obj.isoformat()
|
|
42
|
+
elif isinstance(obj, dict):
|
|
43
|
+
return {key: self._convert_datetime_to_string(value) for key, value in obj.items()}
|
|
44
|
+
elif isinstance(obj, list):
|
|
45
|
+
return [self._convert_datetime_to_string(item) for item in obj]
|
|
46
|
+
elif isinstance(obj, tuple):
|
|
47
|
+
return tuple(self._convert_datetime_to_string(item) for item in obj)
|
|
48
|
+
else:
|
|
49
|
+
return obj
|
|
50
|
+
|
|
51
|
+
def generate_agent(
|
|
52
|
+
self,
|
|
53
|
+
mcp_server: MCPServer,
|
|
54
|
+
agent_name: Optional[str] = None,
|
|
55
|
+
agent_id: Optional[str] = None,
|
|
56
|
+
agent_description: Optional[str] = None,
|
|
57
|
+
expose_individual_tools: bool = False,
|
|
58
|
+
auth_required: bool = False,
|
|
59
|
+
auth_schemes: Optional[list] = None,
|
|
60
|
+
use_simple_server: bool = True,
|
|
61
|
+
**kwargs
|
|
62
|
+
) -> UtilityAgent:
|
|
63
|
+
"""Generate a utility agent from an MCP server.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
mcp_server: MCP server to wrap
|
|
67
|
+
agent_name: Name for the agent (defaults to MCP server name)
|
|
68
|
+
agent_id: Unique ID for the agent (defaults to generated)
|
|
69
|
+
agent_description: Description of the agent
|
|
70
|
+
expose_individual_tools: Whether to expose individual MCP tools
|
|
71
|
+
auth_required: Whether authentication is required
|
|
72
|
+
auth_schemes: Authentication schemes if required
|
|
73
|
+
use_simple_server: Whether to use simplified server template
|
|
74
|
+
**kwargs: Additional template variables
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
Generated UtilityAgent object
|
|
78
|
+
"""
|
|
79
|
+
# Generate defaults
|
|
80
|
+
if agent_name is None:
|
|
81
|
+
agent_name = f"{mcp_server.name} Utility Agent"
|
|
82
|
+
|
|
83
|
+
if agent_id is None:
|
|
84
|
+
# Generate a clean ID based on MCP server name without timestamp
|
|
85
|
+
base_id = mcp_server.name.lower().replace(' ', '-').replace('_', '-')
|
|
86
|
+
agent_id = f"{base_id}-traia-utility-agent"
|
|
87
|
+
|
|
88
|
+
if agent_description is None:
|
|
89
|
+
agent_description = f"A2A utility agent that exposes {mcp_server.name} capabilities"
|
|
90
|
+
|
|
91
|
+
# Prepare capabilities list
|
|
92
|
+
capabilities = []
|
|
93
|
+
if hasattr(mcp_server, 'capabilities') and mcp_server.capabilities:
|
|
94
|
+
if isinstance(mcp_server.capabilities, dict):
|
|
95
|
+
capabilities = list(mcp_server.capabilities.keys())
|
|
96
|
+
elif isinstance(mcp_server.capabilities, list):
|
|
97
|
+
capabilities = mcp_server.capabilities
|
|
98
|
+
|
|
99
|
+
# Prepare skill examples based on MCP server type
|
|
100
|
+
skill_examples = kwargs.get('skill_examples', [])
|
|
101
|
+
if not skill_examples:
|
|
102
|
+
skill_examples = self._generate_skill_examples(mcp_server)
|
|
103
|
+
|
|
104
|
+
# Generate output directory path
|
|
105
|
+
output_dir = self.output_base_dir / agent_id
|
|
106
|
+
|
|
107
|
+
# Get MCP server metadata and convert datetime objects to strings
|
|
108
|
+
mcp_server_metadata = getattr(mcp_server, 'metadata', {})
|
|
109
|
+
mcp_server_metadata_serializable = self._convert_datetime_to_string(mcp_server_metadata)
|
|
110
|
+
|
|
111
|
+
# Use template generator to create the agent
|
|
112
|
+
generated_path = self.template_generator.generate_agent(
|
|
113
|
+
output_dir=output_dir,
|
|
114
|
+
agent_name=agent_name,
|
|
115
|
+
agent_id=agent_id,
|
|
116
|
+
agent_description=agent_description,
|
|
117
|
+
agent_version="0.1.0",
|
|
118
|
+
mcp_server_name=mcp_server.name,
|
|
119
|
+
mcp_server_url=mcp_server.url,
|
|
120
|
+
mcp_server_description=mcp_server.description,
|
|
121
|
+
mcp_server_type=getattr(mcp_server, 'server_type', 'streamable-http'),
|
|
122
|
+
mcp_server_capabilities=capabilities,
|
|
123
|
+
mcp_server_metadata=mcp_server_metadata_serializable,
|
|
124
|
+
expose_individual_tools=expose_individual_tools,
|
|
125
|
+
auth_required=auth_required,
|
|
126
|
+
auth_schemes=auth_schemes or [],
|
|
127
|
+
skill_examples=skill_examples,
|
|
128
|
+
use_simple_server=use_simple_server,
|
|
129
|
+
**kwargs
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# Create UtilityAgent object
|
|
133
|
+
utility_agent = UtilityAgent(
|
|
134
|
+
id=agent_id,
|
|
135
|
+
name=agent_name,
|
|
136
|
+
description=agent_description,
|
|
137
|
+
mcp_server_id=mcp_server.id,
|
|
138
|
+
capabilities=capabilities,
|
|
139
|
+
status=UtilityAgentStatus.GENERATED,
|
|
140
|
+
code_path=str(generated_path),
|
|
141
|
+
created_at=datetime.now(),
|
|
142
|
+
metadata={
|
|
143
|
+
"generated_from": mcp_server.name,
|
|
144
|
+
"use_simple_server": use_simple_server,
|
|
145
|
+
"auth_required": auth_required,
|
|
146
|
+
}
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
logger.info(f"Generated utility agent '{agent_name}' at {generated_path}")
|
|
150
|
+
return utility_agent
|
|
151
|
+
|
|
152
|
+
def _generate_skill_examples(self, mcp_server: MCPServer) -> list:
|
|
153
|
+
"""Generate example prompts based on MCP server type.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
mcp_server: MCP server object
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
List of example prompts
|
|
160
|
+
"""
|
|
161
|
+
# Default examples
|
|
162
|
+
examples = [
|
|
163
|
+
f"Help me use {mcp_server.name}",
|
|
164
|
+
f"What can {mcp_server.name} do?",
|
|
165
|
+
]
|
|
166
|
+
|
|
167
|
+
# Add type-specific examples
|
|
168
|
+
server_type = mcp_server.name.lower()
|
|
169
|
+
|
|
170
|
+
if 'search' in server_type:
|
|
171
|
+
examples.extend([
|
|
172
|
+
"Search for information about AI",
|
|
173
|
+
"Find recent news about technology"
|
|
174
|
+
])
|
|
175
|
+
elif 'file' in server_type or 'filesystem' in server_type:
|
|
176
|
+
examples.extend([
|
|
177
|
+
"List files in the current directory",
|
|
178
|
+
"Read the contents of a file"
|
|
179
|
+
])
|
|
180
|
+
elif 'database' in server_type or 'db' in server_type:
|
|
181
|
+
examples.extend([
|
|
182
|
+
"Query the database for user information",
|
|
183
|
+
"Show me the database schema"
|
|
184
|
+
])
|
|
185
|
+
elif 'api' in server_type:
|
|
186
|
+
examples.extend([
|
|
187
|
+
"Call the API to get data",
|
|
188
|
+
"Send a request to the endpoint"
|
|
189
|
+
])
|
|
190
|
+
elif 'trading' in server_type or 'finance' in server_type:
|
|
191
|
+
examples.extend([
|
|
192
|
+
"Get the current market data",
|
|
193
|
+
"Show me trading opportunities"
|
|
194
|
+
])
|
|
195
|
+
else:
|
|
196
|
+
# Generic examples
|
|
197
|
+
examples.extend([
|
|
198
|
+
f"Execute a {mcp_server.name} operation",
|
|
199
|
+
f"Process this request using {mcp_server.name}"
|
|
200
|
+
])
|
|
201
|
+
|
|
202
|
+
return examples[:5] # Limit to 5 examples
|
|
203
|
+
|
|
204
|
+
def update_agent_files(
|
|
205
|
+
self,
|
|
206
|
+
agent_path: Path,
|
|
207
|
+
updates: Dict[str, Any]
|
|
208
|
+
) -> None:
|
|
209
|
+
"""Update existing agent files with new configuration.
|
|
210
|
+
|
|
211
|
+
Args:
|
|
212
|
+
agent_path: Path to the agent directory
|
|
213
|
+
updates: Dictionary of updates to apply
|
|
214
|
+
"""
|
|
215
|
+
# Update agent_config.json if it exists
|
|
216
|
+
config_path = agent_path / "agent_config.json"
|
|
217
|
+
if config_path.exists():
|
|
218
|
+
with open(config_path, 'r') as f:
|
|
219
|
+
config = json.load(f)
|
|
220
|
+
|
|
221
|
+
# Merge updates
|
|
222
|
+
config.update(updates)
|
|
223
|
+
|
|
224
|
+
with open(config_path, 'w') as f:
|
|
225
|
+
json.dump(config, f, indent=2)
|
|
226
|
+
|
|
227
|
+
logger.info(f"Updated agent configuration at {config_path}")
|
|
228
|
+
|
|
229
|
+
def cleanup_agent(self, agent_path: Path) -> None:
|
|
230
|
+
"""Clean up generated agent files.
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
agent_path: Path to the agent directory
|
|
234
|
+
"""
|
|
235
|
+
if agent_path.exists():
|
|
236
|
+
shutil.rmtree(agent_path)
|
|
237
|
+
logger.info(f"Cleaned up agent at {agent_path}")
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"""Template generator for A2A utility agents.
|
|
2
|
+
|
|
3
|
+
This module provides functionality to generate utility agent code from templates.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import json
|
|
8
|
+
import shutil
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Dict, Any, List, Optional
|
|
11
|
+
from jinja2 import Environment, FileSystemLoader, select_autoescape
|
|
12
|
+
import logging
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class IATPServerTemplateGenerator:
|
|
18
|
+
"""Generate utility agent code from Jinja2 templates."""
|
|
19
|
+
|
|
20
|
+
def __init__(self, templates_dir: Optional[Path] = None):
|
|
21
|
+
"""Initialize the template generator.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
templates_dir: Directory containing the Jinja2 templates
|
|
25
|
+
"""
|
|
26
|
+
if templates_dir is None:
|
|
27
|
+
templates_dir = Path(__file__).parent / "templates"
|
|
28
|
+
|
|
29
|
+
self.templates_dir = templates_dir
|
|
30
|
+
self.env = Environment(
|
|
31
|
+
loader=FileSystemLoader(self.templates_dir),
|
|
32
|
+
autoescape=select_autoescape(['html', 'xml']),
|
|
33
|
+
trim_blocks=True,
|
|
34
|
+
lstrip_blocks=True
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
def generate_agent(
|
|
38
|
+
self,
|
|
39
|
+
output_dir: Path,
|
|
40
|
+
agent_name: str,
|
|
41
|
+
agent_id: str,
|
|
42
|
+
mcp_server_name: str,
|
|
43
|
+
mcp_server_url: str,
|
|
44
|
+
mcp_server_description: str,
|
|
45
|
+
mcp_server_capabilities: List[str],
|
|
46
|
+
agent_description: Optional[str] = None,
|
|
47
|
+
agent_version: str = "0.1.0",
|
|
48
|
+
mcp_server_type: str = "streamable-http",
|
|
49
|
+
mcp_server_metadata: Optional[Dict[str, Any]] = None,
|
|
50
|
+
expose_individual_tools: bool = False,
|
|
51
|
+
auth_required: bool = False,
|
|
52
|
+
auth_schemes: Optional[List[str]] = None,
|
|
53
|
+
skill_examples: Optional[List[str]] = None,
|
|
54
|
+
additional_dependencies: Optional[List[str]] = None,
|
|
55
|
+
environment_variables: Optional[List[Dict[str, str]]] = None,
|
|
56
|
+
use_simple_server: bool = True,
|
|
57
|
+
additional_ignores: Optional[List[str]] = None,
|
|
58
|
+
**kwargs
|
|
59
|
+
) -> Path:
|
|
60
|
+
"""Generate a complete utility agent from templates.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
output_dir: Directory to write the generated agent
|
|
64
|
+
agent_name: Human-readable name of the agent
|
|
65
|
+
agent_id: Unique identifier for the agent
|
|
66
|
+
mcp_server_name: Name of the MCP server
|
|
67
|
+
mcp_server_url: URL or path to the MCP server
|
|
68
|
+
mcp_server_description: Description of the MCP server
|
|
69
|
+
mcp_server_capabilities: List of capabilities provided by the MCP server
|
|
70
|
+
agent_description: Description of the agent (defaults to auto-generated)
|
|
71
|
+
agent_version: Version of the agent
|
|
72
|
+
mcp_server_type: Type of MCP server (stdio, http, etc.)
|
|
73
|
+
mcp_server_metadata: Additional metadata for the MCP server
|
|
74
|
+
expose_individual_tools: Whether to expose individual MCP tools as A2A skills
|
|
75
|
+
auth_required: Whether authentication is required
|
|
76
|
+
auth_schemes: List of authentication schemes if auth is required
|
|
77
|
+
skill_examples: Example prompts for the main skill
|
|
78
|
+
additional_dependencies: Extra Python dependencies
|
|
79
|
+
environment_variables: Environment variables for Dockerfile
|
|
80
|
+
use_simple_server: Whether to use the simplified server.py template
|
|
81
|
+
additional_ignores: Additional patterns to add to .gitignore
|
|
82
|
+
**kwargs: Additional template variables
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
Path to the generated agency directory
|
|
86
|
+
"""
|
|
87
|
+
# Create output directory
|
|
88
|
+
output_dir = Path(output_dir)
|
|
89
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
90
|
+
|
|
91
|
+
# Generate default values
|
|
92
|
+
if agent_description is None:
|
|
93
|
+
agent_description = f"Utility agent that exposes {mcp_server_name} capabilities via A2A protocol"
|
|
94
|
+
|
|
95
|
+
if skill_examples is None:
|
|
96
|
+
skill_examples = [
|
|
97
|
+
f"Help me use {mcp_server_name}",
|
|
98
|
+
f"Process this request with {mcp_server_name}",
|
|
99
|
+
"Execute this operation"
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
if mcp_server_metadata is None:
|
|
103
|
+
mcp_server_metadata = {}
|
|
104
|
+
|
|
105
|
+
if additional_dependencies is None:
|
|
106
|
+
additional_dependencies = []
|
|
107
|
+
|
|
108
|
+
if environment_variables is None:
|
|
109
|
+
environment_variables = []
|
|
110
|
+
|
|
111
|
+
if additional_ignores is None:
|
|
112
|
+
additional_ignores = []
|
|
113
|
+
|
|
114
|
+
# Convert agent name to valid Python identifiers
|
|
115
|
+
class_name = ''.join(word.capitalize() for word in agent_name.replace('-', ' ').split())
|
|
116
|
+
package_name = agent_name.lower().replace(' ', '_').replace('-', '_')
|
|
117
|
+
module_name = package_name
|
|
118
|
+
docker_image = f"traia/{agent_id}:latest"
|
|
119
|
+
|
|
120
|
+
# Prepare template context
|
|
121
|
+
context = {
|
|
122
|
+
"agent_name": agent_name,
|
|
123
|
+
"agent_id": agent_id,
|
|
124
|
+
"agent_description": agent_description,
|
|
125
|
+
"agent_version": agent_version,
|
|
126
|
+
"mcp_server_name": mcp_server_name,
|
|
127
|
+
"mcp_server_url": mcp_server_url,
|
|
128
|
+
"mcp_server_description": mcp_server_description,
|
|
129
|
+
"mcp_server_type": mcp_server_type,
|
|
130
|
+
"mcp_server_capabilities": mcp_server_capabilities,
|
|
131
|
+
"mcp_server_metadata": mcp_server_metadata,
|
|
132
|
+
"expose_individual_tools": expose_individual_tools,
|
|
133
|
+
"auth_required": auth_required,
|
|
134
|
+
"auth_schemes": auth_schemes or [],
|
|
135
|
+
"skill_examples": skill_examples,
|
|
136
|
+
"class_name": class_name,
|
|
137
|
+
"package_name": package_name,
|
|
138
|
+
"module_name": module_name,
|
|
139
|
+
"docker_image": docker_image,
|
|
140
|
+
"additional_dependencies": additional_dependencies,
|
|
141
|
+
"environment_variables": environment_variables,
|
|
142
|
+
"additional_ignores": additional_ignores,
|
|
143
|
+
"use_uv_lock": False, # Will be true after first uv sync
|
|
144
|
+
**kwargs
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
# Generate files
|
|
148
|
+
files_to_generate = []
|
|
149
|
+
|
|
150
|
+
# Common files for both approaches
|
|
151
|
+
common_files = [
|
|
152
|
+
("agent_config.json.j2", "agent_config.json"),
|
|
153
|
+
("pyproject.toml.j2", "pyproject.toml"),
|
|
154
|
+
("Dockerfile.j2", "Dockerfile"),
|
|
155
|
+
("README.md.j2", "README.md"),
|
|
156
|
+
("docker-compose.yml.j2", "docker-compose.yml"),
|
|
157
|
+
("env.example.j2", ".env.example"),
|
|
158
|
+
("gitignore.j2", ".gitignore"),
|
|
159
|
+
(".dockerignore.j2", ".dockerignore"),
|
|
160
|
+
("run_local_docker.sh.j2", "run_local_docker.sh"),
|
|
161
|
+
]
|
|
162
|
+
|
|
163
|
+
if use_simple_server:
|
|
164
|
+
# Simple single-file approach
|
|
165
|
+
files_to_generate = [
|
|
166
|
+
("server.py.j2", "server.py"),
|
|
167
|
+
] + common_files
|
|
168
|
+
else:
|
|
169
|
+
# Modular approach with separate files
|
|
170
|
+
# Create package directory
|
|
171
|
+
pkg_dir = output_dir / package_name
|
|
172
|
+
pkg_dir.mkdir(exist_ok=True)
|
|
173
|
+
|
|
174
|
+
files_to_generate = [
|
|
175
|
+
("agent.py.j2", f"{package_name}/agent.py"),
|
|
176
|
+
("agent_executor.py.j2", f"{package_name}/agent_executor.py"),
|
|
177
|
+
("__main__.py.j2", f"{package_name}/__main__.py"),
|
|
178
|
+
] + common_files
|
|
179
|
+
|
|
180
|
+
# Create __init__.py
|
|
181
|
+
(output_dir / package_name / "__init__.py").write_text("")
|
|
182
|
+
|
|
183
|
+
# Render and write templates
|
|
184
|
+
for template_name, output_name in files_to_generate:
|
|
185
|
+
try:
|
|
186
|
+
template = self.env.get_template(template_name)
|
|
187
|
+
content = template.render(context)
|
|
188
|
+
output_path = output_dir / output_name
|
|
189
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
190
|
+
output_path.write_text(content)
|
|
191
|
+
logger.info(f"Generated {output_path}")
|
|
192
|
+
|
|
193
|
+
# Make run_local_docker.sh executable
|
|
194
|
+
if output_name == "run_local_docker.sh":
|
|
195
|
+
output_path.chmod(0o755)
|
|
196
|
+
logger.info(f"Made {output_path} executable")
|
|
197
|
+
except Exception as e:
|
|
198
|
+
logger.error(f"Error generating {output_name} from {template_name}: {e}")
|
|
199
|
+
raise
|
|
200
|
+
|
|
201
|
+
# # Copy mcp_agent_template.py
|
|
202
|
+
# self._copy_mcp_agent_template(output_dir)
|
|
203
|
+
|
|
204
|
+
logger.info(f"Successfully generated utility agent at {output_dir}")
|
|
205
|
+
return output_dir
|
|
206
|
+
|
|
207
|
+
# def _copy_mcp_agent_template(self, output_dir: Path):
|
|
208
|
+
# """Copy the mcp_agent_template.py file to the generated agent.
|
|
209
|
+
|
|
210
|
+
# Args:
|
|
211
|
+
# output_dir: Output directory for the agent
|
|
212
|
+
# """
|
|
213
|
+
# # Find the mcp_agent_template.py file
|
|
214
|
+
# mcp_template_path = Path(__file__).parent.parent / "mcp" / "mcp_agent_template.py"
|
|
215
|
+
# agent_adapter_path = Path(__file__).parent.parent / "mcp" / "traia_mcp_adapter.py"
|
|
216
|
+
|
|
217
|
+
# if mcp_template_path.exists():
|
|
218
|
+
# # Copy to output directory
|
|
219
|
+
# dest_path_one = output_dir / "mcp_agent_template.py"
|
|
220
|
+
# dest_path_two = output_dir / "traia_mcp_adapter.py"
|
|
221
|
+
# shutil.copy2(mcp_template_path, dest_path_one)
|
|
222
|
+
# shutil.copy2(agent_adapter_path, dest_path_two)
|
|
223
|
+
# logger.info(f"Copied mcp_agent_template.py to {dest_path_one}")
|
|
224
|
+
# logger.info(f"Copied traia_mcp_adapter.py to {dest_path_two}")
|
|
225
|
+
# else:
|
|
226
|
+
# logger.warning(f"Could not find mcp_agent_template.py at {mcp_template_path}")
|
|
227
|
+
# logger.warning(f"Could not find traia_mcp_adapter.py at {agent_adapter_path}")
|
|
228
|
+
|
|
229
|
+
def list_templates(self) -> List[str]:
|
|
230
|
+
"""List available templates.
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
List of template file names
|
|
234
|
+
"""
|
|
235
|
+
return [f.name for f in self.templates_dir.glob("*.j2")]
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Environment files (should be mounted, not built into image)
|
|
2
|
+
.env
|
|
3
|
+
.env.*
|
|
4
|
+
!.env.example
|
|
5
|
+
|
|
6
|
+
# Python
|
|
7
|
+
__pycache__/
|
|
8
|
+
*.pyc
|
|
9
|
+
*.pyo
|
|
10
|
+
*.pyd
|
|
11
|
+
.Python
|
|
12
|
+
*.egg-info/
|
|
13
|
+
dist/
|
|
14
|
+
build/
|
|
15
|
+
|
|
16
|
+
# Git
|
|
17
|
+
.git/
|
|
18
|
+
.gitignore
|
|
19
|
+
|
|
20
|
+
# IDE
|
|
21
|
+
.vscode/
|
|
22
|
+
.idea/
|
|
23
|
+
*.swp
|
|
24
|
+
*.swo
|
|
25
|
+
|
|
26
|
+
# OS
|
|
27
|
+
.DS_Store
|
|
28
|
+
Thumbs.db
|
|
29
|
+
|
|
30
|
+
# UV/Poetry
|
|
31
|
+
.venv/
|
|
32
|
+
venv/
|
|
33
|
+
env/
|
|
34
|
+
|
|
35
|
+
# Logs
|
|
36
|
+
*.log
|
|
37
|
+
logs/
|
|
38
|
+
|
|
39
|
+
# Testing
|
|
40
|
+
.pytest_cache/
|
|
41
|
+
.coverage
|
|
42
|
+
htmlcov/
|
|
43
|
+
.tox/
|
|
44
|
+
|
|
45
|
+
# Documentation
|
|
46
|
+
docs/
|
|
47
|
+
*.md
|
|
48
|
+
!README.md
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
FROM python:3.12-slim
|
|
2
|
+
|
|
3
|
+
# Install system dependencies
|
|
4
|
+
RUN apt-get update && apt-get install -y \
|
|
5
|
+
curl \
|
|
6
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
7
|
+
|
|
8
|
+
# Install uv
|
|
9
|
+
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
|
|
10
|
+
RUN chmod +x /usr/local/bin/uv
|
|
11
|
+
|
|
12
|
+
# Create non-root user
|
|
13
|
+
RUN useradd -m -u 1000 appuser
|
|
14
|
+
|
|
15
|
+
# Create app directory and set ownership
|
|
16
|
+
RUN mkdir -p /app && chown -R appuser:appuser /app
|
|
17
|
+
|
|
18
|
+
# Set working directory
|
|
19
|
+
WORKDIR /app
|
|
20
|
+
|
|
21
|
+
# Switch to non-root user
|
|
22
|
+
USER appuser
|
|
23
|
+
|
|
24
|
+
# Copy project files
|
|
25
|
+
COPY --chown=appuser:appuser . .
|
|
26
|
+
|
|
27
|
+
# Install Python dependencies
|
|
28
|
+
RUN uv venv .venv && \
|
|
29
|
+
uv pip install -r pyproject.toml
|
|
30
|
+
|
|
31
|
+
# Set environment variables
|
|
32
|
+
ENV PATH="/app/.venv/bin:$PATH"
|
|
33
|
+
ENV PYTHONPATH=/app
|
|
34
|
+
ENV HOST=0.0.0.0
|
|
35
|
+
ENV PYTHONUNBUFFERED=1
|
|
36
|
+
ENV UV_SYSTEM_PYTHON=1
|
|
37
|
+
{% for env_var in environment_variables %}
|
|
38
|
+
ENV {{ env_var.name }}="{{ env_var.value }}"
|
|
39
|
+
{% endfor %}
|
|
40
|
+
|
|
41
|
+
# Health check
|
|
42
|
+
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
|
|
43
|
+
CMD curl -f http://localhost:${PORT:-8000}/.well-known/agent.json || exit 1
|
|
44
|
+
|
|
45
|
+
# Expose port (uses PORT environment variable with default)
|
|
46
|
+
EXPOSE ${PORT:-8000}
|
|
47
|
+
|
|
48
|
+
# Run the application
|
|
49
|
+
CMD ["uv", "run", "python", "-m", "{{ module_name }}"]
|