a4e 0.1.5__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.
Files changed (70) hide show
  1. a4e/__init__.py +0 -0
  2. a4e/cli.py +47 -0
  3. a4e/cli_commands/__init__.py +5 -0
  4. a4e/cli_commands/add.py +376 -0
  5. a4e/cli_commands/deploy.py +149 -0
  6. a4e/cli_commands/dev.py +162 -0
  7. a4e/cli_commands/info.py +206 -0
  8. a4e/cli_commands/init.py +211 -0
  9. a4e/cli_commands/list.py +227 -0
  10. a4e/cli_commands/mcp.py +504 -0
  11. a4e/cli_commands/remove.py +197 -0
  12. a4e/cli_commands/update.py +285 -0
  13. a4e/cli_commands/validate.py +117 -0
  14. a4e/core.py +109 -0
  15. a4e/dev_runner.py +425 -0
  16. a4e/server.py +86 -0
  17. a4e/templates/agent.md.j2 +168 -0
  18. a4e/templates/agent.py.j2 +15 -0
  19. a4e/templates/agents.md.j2 +99 -0
  20. a4e/templates/metadata.json.j2 +20 -0
  21. a4e/templates/prompt.md.j2 +20 -0
  22. a4e/templates/prompts/agent.md.j2 +206 -0
  23. a4e/templates/skills/agents.md.j2 +110 -0
  24. a4e/templates/skills/skill.md.j2 +120 -0
  25. a4e/templates/support_module.py.j2 +84 -0
  26. a4e/templates/tool.py.j2 +60 -0
  27. a4e/templates/tools/agent.md.j2 +192 -0
  28. a4e/templates/view.tsx.j2 +21 -0
  29. a4e/templates/views/agent.md.j2 +219 -0
  30. a4e/tools/__init__.py +70 -0
  31. a4e/tools/agent_tools/__init__.py +12 -0
  32. a4e/tools/agent_tools/add_support_module.py +95 -0
  33. a4e/tools/agent_tools/add_tool.py +115 -0
  34. a4e/tools/agent_tools/list_tools.py +28 -0
  35. a4e/tools/agent_tools/remove_tool.py +69 -0
  36. a4e/tools/agent_tools/update_tool.py +123 -0
  37. a4e/tools/deploy/__init__.py +8 -0
  38. a4e/tools/deploy/deploy.py +59 -0
  39. a4e/tools/dev/__init__.py +10 -0
  40. a4e/tools/dev/check_environment.py +79 -0
  41. a4e/tools/dev/dev_start.py +30 -0
  42. a4e/tools/dev/dev_stop.py +26 -0
  43. a4e/tools/project/__init__.py +10 -0
  44. a4e/tools/project/get_agent_info.py +66 -0
  45. a4e/tools/project/get_instructions.py +216 -0
  46. a4e/tools/project/initialize_project.py +231 -0
  47. a4e/tools/schemas/__init__.py +8 -0
  48. a4e/tools/schemas/generate_schemas.py +278 -0
  49. a4e/tools/skills/__init__.py +12 -0
  50. a4e/tools/skills/add_skill.py +105 -0
  51. a4e/tools/skills/helpers.py +137 -0
  52. a4e/tools/skills/list_skills.py +54 -0
  53. a4e/tools/skills/remove_skill.py +74 -0
  54. a4e/tools/skills/update_skill.py +150 -0
  55. a4e/tools/validation/__init__.py +8 -0
  56. a4e/tools/validation/validate.py +389 -0
  57. a4e/tools/views/__init__.py +12 -0
  58. a4e/tools/views/add_view.py +40 -0
  59. a4e/tools/views/helpers.py +91 -0
  60. a4e/tools/views/list_views.py +27 -0
  61. a4e/tools/views/remove_view.py +73 -0
  62. a4e/tools/views/update_view.py +124 -0
  63. a4e/utils/dev_manager.py +253 -0
  64. a4e/utils/schema_generator.py +255 -0
  65. a4e-0.1.5.dist-info/METADATA +427 -0
  66. a4e-0.1.5.dist-info/RECORD +70 -0
  67. a4e-0.1.5.dist-info/WHEEL +5 -0
  68. a4e-0.1.5.dist-info/entry_points.txt +2 -0
  69. a4e-0.1.5.dist-info/licenses/LICENSE +21 -0
  70. a4e-0.1.5.dist-info/top_level.txt +1 -0
a4e/dev_runner.py ADDED
@@ -0,0 +1,425 @@
1
+ import sys
2
+ import os
3
+ import importlib.util
4
+ import inspect
5
+ import json
6
+ import argparse
7
+ from pathlib import Path
8
+ from types import ModuleType
9
+ from typing import Any, Optional
10
+
11
+
12
+ # Mock a4e SDK and autogen
13
+ def _mock_dependencies():
14
+ """Mock a4e.sdk and autogen_agentchat to allow agent.py to load"""
15
+ if "a4e" not in sys.modules:
16
+ a4e = ModuleType("a4e")
17
+ sdk = ModuleType("a4e.sdk")
18
+
19
+ class MockAgentFactory:
20
+ @staticmethod
21
+ async def create_agent(*args, **kwargs):
22
+ return "MockAgentInstance"
23
+
24
+ sdk.AgentFactory = MockAgentFactory
25
+
26
+ # Mock tool decorator
27
+ def tool(func):
28
+ func._is_tool = True
29
+ return func
30
+
31
+ sdk.tool = tool
32
+
33
+ a4e.sdk = sdk
34
+ sys.modules["a4e"] = a4e
35
+ sys.modules["a4e.sdk"] = sdk
36
+
37
+ if "autogen_agentchat" not in sys.modules:
38
+ autogen = ModuleType("autogen_agentchat")
39
+ agents = ModuleType("autogen_agentchat.agents")
40
+
41
+ class AssistantAgent:
42
+ pass
43
+
44
+ agents.AssistantAgent = AssistantAgent
45
+ autogen.agents = agents
46
+ sys.modules["autogen_agentchat"] = autogen
47
+ sys.modules["autogen_agentchat.agents"] = agents
48
+
49
+
50
+ def run_agent_server(agent_path: Path, port: int):
51
+ """Run the agent in a FastMCP server with REST API endpoints"""
52
+ from mcp.server.fastmcp import FastMCP
53
+ from starlette.applications import Starlette
54
+ from starlette.routing import Route, Mount
55
+ from starlette.responses import JSONResponse, Response
56
+ from starlette.middleware.cors import CORSMiddleware
57
+ import zipfile
58
+ import io
59
+
60
+ _mock_dependencies()
61
+
62
+ agent_name = agent_path.name
63
+ mcp = FastMCP(name=agent_name)
64
+
65
+ # Load metadata
66
+ metadata_path = agent_path / "metadata.json"
67
+ metadata = {}
68
+ if metadata_path.exists():
69
+ metadata = json.loads(metadata_path.read_text())
70
+
71
+ # Load tools schemas
72
+ tools_schemas_path = agent_path / "tools" / "schemas.json"
73
+ tools_schemas = {}
74
+ if tools_schemas_path.exists():
75
+ tools_schemas = json.loads(tools_schemas_path.read_text())
76
+
77
+ # Load views schemas
78
+ views_schemas_path = agent_path / "views" / "schemas.json"
79
+ views_schemas = {}
80
+ if views_schemas_path.exists():
81
+ views_schemas = json.loads(views_schemas_path.read_text())
82
+
83
+ # Load skills schemas
84
+ skills_schemas_path = agent_path / "skills" / "schemas.json"
85
+ skills_schemas = {}
86
+ if skills_schemas_path.exists():
87
+ skills_schemas = json.loads(skills_schemas_path.read_text())
88
+
89
+ # Load system prompt
90
+ prompt_path = agent_path / "prompts" / "agent.md"
91
+ system_prompt = ""
92
+ if prompt_path.exists():
93
+ system_prompt = prompt_path.read_text()
94
+
95
+ # Load agent tools into MCP
96
+ tools_dir = agent_path / "tools"
97
+ if tools_dir.exists():
98
+ sys.path.insert(0, str(agent_path))
99
+ for tool_file in tools_dir.glob("*.py"):
100
+ if tool_file.name == "__init__.py":
101
+ continue
102
+
103
+ try:
104
+ spec = importlib.util.spec_from_file_location(tool_file.stem, tool_file)
105
+ if spec and spec.loader:
106
+ module = importlib.util.module_from_spec(spec)
107
+ spec.loader.exec_module(module)
108
+
109
+ for name, obj in inspect.getmembers(module):
110
+ if inspect.isfunction(obj):
111
+ if (
112
+ getattr(obj, "_is_tool", False)
113
+ or name == tool_file.stem
114
+ ):
115
+ # Register with FastMCP
116
+ mcp.tool()(obj)
117
+ print(f"Registered tool: {name}")
118
+ except Exception as e:
119
+ print(f"Failed to load tool {tool_file}: {e}")
120
+
121
+ # Add system prompt resource
122
+ @mcp.resource("agent://system_prompt")
123
+ def get_system_prompt() -> str:
124
+ return system_prompt or "You are a helpful assistant."
125
+
126
+ # REST API Endpoints
127
+ async def agent_info(request):
128
+ return JSONResponse(metadata)
129
+
130
+ async def get_tools(request):
131
+ # Convert schema format to frontend expected format
132
+ # tools_schemas is a dict: {"tool_name": {"name": "...", "parameters": {...}}}
133
+ tools = []
134
+ for tool_name, schema in tools_schemas.items():
135
+ params = []
136
+ # Handle both "parameters" and "inputSchema" formats
137
+ param_schema = schema.get("parameters") or schema.get("inputSchema", {})
138
+ if "properties" in param_schema:
139
+ required = param_schema.get("required", [])
140
+ for param_name, param_info in param_schema["properties"].items():
141
+ params.append(
142
+ {
143
+ "name": param_name,
144
+ "type": param_info.get("type", "string"),
145
+ "description": param_info.get("description", ""),
146
+ "required": param_name in required,
147
+ }
148
+ )
149
+ tools.append(
150
+ {
151
+ "name": schema.get("name", tool_name),
152
+ "description": schema.get("description", ""),
153
+ "parameters": params,
154
+ }
155
+ )
156
+ return JSONResponse(tools)
157
+
158
+ async def get_views(request):
159
+ # Convert views schema to frontend expected format
160
+ views = []
161
+ for view_id, view_data in views_schemas.items():
162
+ props = []
163
+ if "params" in view_data:
164
+ for prop_name, prop_info in view_data["params"].items():
165
+ props.append(
166
+ {
167
+ "name": prop_name,
168
+ "type": prop_info.get("type", "string"),
169
+ "required": True, # Default to required
170
+ "description": prop_info.get("description", ""),
171
+ }
172
+ )
173
+ views.append(
174
+ {
175
+ "id": view_data.get("id", view_id),
176
+ "description": view_data.get("description", ""),
177
+ "props": props,
178
+ }
179
+ )
180
+ return JSONResponse(views)
181
+
182
+ async def get_skills(request):
183
+ # Convert skills schema to frontend expected format
184
+ skills = []
185
+ for skill_id, skill_data in skills_schemas.items():
186
+ skills.append(
187
+ {
188
+ "id": skill_data.get("id", skill_id),
189
+ "name": skill_data.get("name", skill_id),
190
+ "description": skill_data.get("description", ""),
191
+ "intent_triggers": skill_data.get("intent_triggers", []),
192
+ "requires_auth": skill_data.get("requires_auth", False),
193
+ "internal_tools": skill_data.get("internal_tools", []),
194
+ "output": skill_data.get("output", {}),
195
+ }
196
+ )
197
+ return JSONResponse(skills)
198
+
199
+ async def get_view_source(request):
200
+ """Get source code for a specific view"""
201
+ view_id = request.path_params["view_id"]
202
+ view_file = agent_path / "views" / view_id / "view.tsx"
203
+
204
+ if not view_file.exists():
205
+ return JSONResponse({"error": "View not found"}, status_code=404)
206
+
207
+ return Response(content=view_file.read_text(), media_type="text/plain")
208
+
209
+ async def download_source(request):
210
+ """Download the entire agent source as a zip file"""
211
+ buffer = io.BytesIO()
212
+ with zipfile.ZipFile(buffer, "w", zipfile.ZIP_DEFLATED) as zip_file:
213
+ for root, dirs, files in os.walk(agent_path):
214
+ for file in files:
215
+ # Skip __pycache__ and hidden files
216
+ if "__pycache__" in root or file.startswith("."):
217
+ continue
218
+
219
+ file_path = os.path.join(root, file)
220
+ archive_name = os.path.relpath(file_path, agent_path)
221
+ zip_file.write(file_path, archive_name)
222
+
223
+ buffer.seek(0)
224
+ return Response(
225
+ content=buffer.getvalue(),
226
+ media_type="application/zip",
227
+ headers={"Content-Disposition": f'attachment; filename="{agent_name}.zip"'},
228
+ )
229
+
230
+ async def get_prompt(request):
231
+ return JSONResponse({"prompt": system_prompt})
232
+
233
+ async def unified_stream(request):
234
+ from sse_starlette.sse import EventSourceResponse
235
+ import asyncio
236
+ import json
237
+
238
+ try:
239
+ payload = await request.json()
240
+ print(f"[DEV] Received payload: {json.dumps(payload, indent=2)}")
241
+ except Exception as e:
242
+ print(f"[DEV] Error parsing payload: {e}")
243
+ payload = {}
244
+
245
+ # Extract user message - handle both formats:
246
+ # Format 1 (playground): {"message": "Hello", "currentState": {...}, "metadata": {...}}
247
+ # Format 2 (legacy): {"messages": [{"content": "Hello"}]}
248
+ last_message = payload.get("message", "")
249
+ if not last_message:
250
+ messages = payload.get("messages", [])
251
+ if messages:
252
+ last_message = messages[-1].get("content", "")
253
+ if not last_message:
254
+ last_message = "Hello!"
255
+
256
+ print(f"[DEV] Processing message: {last_message}")
257
+
258
+ # Check for view switch commands
259
+ message_lower = last_message.lower()
260
+ view_to_show = None
261
+ view_props = {}
262
+
263
+ if "show profile" in message_lower or "profile view" in message_lower:
264
+ view_to_show = "profile"
265
+ view_props = {
266
+ "userName": "Test User",
267
+ "email": "test@example.com",
268
+ "role": "Developer"
269
+ }
270
+ elif "show results" in message_lower or "results view" in message_lower:
271
+ view_to_show = "results"
272
+ view_props = {
273
+ "title": "Analysis Complete",
274
+ "summary": "Here are your test results",
275
+ "items": [
276
+ {"title": "Test 1", "description": "First test passed", "value": "100%", "status": "success"},
277
+ {"title": "Test 2", "description": "Minor issues found", "value": "85%", "status": "warning"},
278
+ {"title": "Test 3", "description": "Needs attention", "value": "60%", "status": "error"}
279
+ ]
280
+ }
281
+ elif "show error" in message_lower or "error view" in message_lower:
282
+ view_to_show = "error"
283
+ view_props = {
284
+ "title": "Test Error",
285
+ "message": "This is a test error message to demonstrate the error view.",
286
+ "errorCode": "TEST_001",
287
+ "suggestion": "This is just a demo - no action needed!"
288
+ }
289
+ elif "show welcome" in message_lower or "welcome view" in message_lower:
290
+ view_to_show = "welcome"
291
+ view_props = {
292
+ "title": "Welcome Back!",
293
+ "subtitle": "Ready to help you",
294
+ "userName": "Developer"
295
+ }
296
+
297
+ async def event_generator():
298
+ # Simulate thinking delay
299
+ await asyncio.sleep(0.5)
300
+
301
+ # 1. Send status
302
+ yield {
303
+ "data": json.dumps(
304
+ {"type": "status", "content": "Processing request..."}
305
+ )
306
+ }
307
+ await asyncio.sleep(0.5)
308
+
309
+ # 2. If view switch requested, send view event
310
+ if view_to_show:
311
+ response_text = f"Switching to {view_to_show} view..."
312
+ words = response_text.split(" ")
313
+ for word in words:
314
+ yield {
315
+ "data": json.dumps(
316
+ {"type": "chat", "content": word + " ", "complete": False}
317
+ )
318
+ }
319
+ await asyncio.sleep(0.05)
320
+
321
+ yield {
322
+ "data": json.dumps({"type": "chat", "content": "", "complete": True})
323
+ }
324
+
325
+ await asyncio.sleep(0.3)
326
+
327
+ # Send view event
328
+ yield {
329
+ "data": json.dumps({
330
+ "type": "view",
331
+ "viewId": view_to_show,
332
+ "props": view_props
333
+ })
334
+ }
335
+ else:
336
+ # 3. Stream text response
337
+ response_text = f"I received your message: '{last_message}'. Try saying 'show profile', 'show results', 'show error', or 'show welcome' to switch views!"
338
+
339
+ words = response_text.split(" ")
340
+ for word in words:
341
+ yield {
342
+ "data": json.dumps(
343
+ {"type": "chat", "content": word + " ", "complete": False}
344
+ )
345
+ }
346
+ await asyncio.sleep(0.05)
347
+
348
+ # Complete chat
349
+ yield {
350
+ "data": json.dumps({"type": "chat", "content": "", "complete": True})
351
+ }
352
+
353
+ # 4. Done signal
354
+ yield {"data": json.dumps({"type": "done", "content": "Stream complete"})}
355
+
356
+ # SSE Done
357
+ yield {"data": "[DONE]"}
358
+
359
+ return EventSourceResponse(event_generator())
360
+
361
+ print(f"Starting agent server on port {port}...")
362
+ # FastMCP uses starlette/uvicorn internally for SSE
363
+ import uvicorn
364
+
365
+ # Get the SSE ASGI app from FastMCP
366
+ sse_app = mcp.sse_app()
367
+
368
+ # Create combined app with REST endpoints + MCP SSE
369
+ # Note: Mount must come last as it catches all unmatched routes
370
+ app = Starlette(
371
+ routes=[
372
+ Route("/agent-info", agent_info, methods=["GET"]),
373
+ Route("/tools", get_tools, methods=["GET"]),
374
+ Route("/views", get_views, methods=["GET"]),
375
+ Route("/views/{view_id}/source", get_view_source, methods=["GET"]),
376
+ Route("/skills", get_skills, methods=["GET"]),
377
+ Route("/system-prompt", get_prompt, methods=["GET"]),
378
+ Route("/download", download_source, methods=["GET"]),
379
+ Route(
380
+ "/api/agents/{agent_name}/unified-stream",
381
+ unified_stream,
382
+ methods=["POST"],
383
+ ),
384
+ # Alternative endpoints the playground might call
385
+ Route("/chat", unified_stream, methods=["POST"]),
386
+ Route("/stream", unified_stream, methods=["POST"]),
387
+ Route("/api/chat", unified_stream, methods=["POST"]),
388
+ Route("/api/stream", unified_stream, methods=["POST"]),
389
+ Mount("/mcp", sse_app), # Mount MCP SSE at /mcp to avoid route conflicts
390
+ ]
391
+ )
392
+
393
+ # Add CORS middleware with expose_headers for ngrok compatibility
394
+ app.add_middleware(
395
+ CORSMiddleware,
396
+ allow_origins=["*"],
397
+ allow_credentials=True,
398
+ allow_methods=["*"],
399
+ allow_headers=["*"],
400
+ expose_headers=["*"],
401
+ )
402
+
403
+ # Add request logging middleware
404
+ from starlette.middleware.base import BaseHTTPMiddleware
405
+
406
+ class LoggingMiddleware(BaseHTTPMiddleware):
407
+ async def dispatch(self, request, call_next):
408
+ print(f"[DEV] {request.method} {request.url.path}")
409
+ response = await call_next(request)
410
+ print(f"[DEV] Response status: {response.status_code}")
411
+ return response
412
+
413
+ app.add_middleware(LoggingMiddleware)
414
+
415
+ # Run uvicorn directly with our port
416
+ uvicorn.run(app, host="0.0.0.0", port=port)
417
+
418
+
419
+ if __name__ == "__main__":
420
+ parser = argparse.ArgumentParser()
421
+ parser.add_argument("--agent-path", required=True, help="Path to agent directory")
422
+ parser.add_argument("--port", type=int, default=5000, help="Port to run on")
423
+ args = parser.parse_args()
424
+
425
+ run_agent_server(Path(args.agent_path), args.port)
a4e/server.py ADDED
@@ -0,0 +1,86 @@
1
+ """
2
+ A4E MCP Server - Main entry point.
3
+
4
+ This server provides tools for creating and managing A4E agents.
5
+ All tools are organized in the tools/ directory by category.
6
+
7
+ IMPORTANT: This server communicates via stdio (stdin/stdout).
8
+ All logging MUST go to stderr to avoid breaking the MCP protocol.
9
+ """
10
+
11
+ from pathlib import Path
12
+ import argparse
13
+ import sys
14
+
15
+ from .core import mcp, set_project_dir
16
+
17
+
18
+ def _log_error(message: str) -> None:
19
+ """Log error to stderr (never stdout, which is reserved for MCP protocol)."""
20
+ print(f"[a4e] {message}", file=sys.stderr)
21
+
22
+ # Import all tools to register them with the MCP server
23
+ # Each tool uses the @mcp.tool() decorator from core.py
24
+ from .tools import (
25
+ # Project
26
+ initialize_project,
27
+ get_agent_info,
28
+ get_instructions,
29
+ # Agent tools
30
+ add_tool,
31
+ list_tools,
32
+ remove_tool,
33
+ update_tool,
34
+ # Views
35
+ add_view,
36
+ list_views,
37
+ remove_view,
38
+ update_view,
39
+ # Skills
40
+ add_skill,
41
+ list_skills,
42
+ remove_skill,
43
+ update_skill,
44
+ # Schemas
45
+ generate_schemas,
46
+ # Validation
47
+ validate,
48
+ # Development
49
+ dev_start,
50
+ dev_stop,
51
+ check_environment,
52
+ # Deployment
53
+ deploy,
54
+ )
55
+
56
+
57
+ def main():
58
+ """Entry point for the CLI"""
59
+ # Parse CLI arguments (standard MCP pattern)
60
+ parser = argparse.ArgumentParser(
61
+ description="A4E MCP Server for agent creation and management"
62
+ )
63
+ parser.add_argument(
64
+ "--project-dir",
65
+ type=str,
66
+ help="Root directory for agent projects (standard MCP pattern). "
67
+ "Agents will be created in {project-dir}/file-store/agent-store/",
68
+ )
69
+
70
+ args, unknown = parser.parse_known_args()
71
+
72
+ # Set global project directory
73
+ if args.project_dir:
74
+ project_dir = Path(args.project_dir).resolve()
75
+ # Validate that it exists
76
+ if not project_dir.exists():
77
+ _log_error(f"Project directory does not exist: {project_dir}")
78
+ sys.exit(1)
79
+ set_project_dir(project_dir)
80
+
81
+ # Run MCP server
82
+ mcp.run()
83
+
84
+
85
+ if __name__ == "__main__":
86
+ main()
@@ -0,0 +1,168 @@
1
+ # AGENTS.md — A4E Agent Project
2
+
3
+ > This file provides context and instructions for AI coding agents working on A4E agent projects.
4
+
5
+ ## Overview
6
+
7
+ A4E agents are AI assistants that combine natural language understanding with custom capabilities defined by **Tools** and **Views**. This template provides guidance for developing and maintaining A4E agents.
8
+
9
+ ## Project Structure
10
+
11
+ ```
12
+ {agent-name}/
13
+ ├── AGENTS.md # This file - context for AI coding agents
14
+ ├── agent.py # Agent factory and initialization
15
+ ├── metadata.json # Agent metadata for the marketplace
16
+ ├── prompts/
17
+ │ ├── AGENTS.md # Prompt development guide
18
+ │ ├── agent.md # Main agent personality and instructions
19
+ │ ├── reviewer.md # Review/validation prompts
20
+ │ └── view_renderer.md # View rendering instructions
21
+ ├── tools/
22
+ │ ├── AGENTS.md # Tool development guide
23
+ │ ├── schemas.json # Auto-generated tool schemas
24
+ │ └── *.py # Python tool files
25
+ ├── views/
26
+ │ ├── AGENTS.md # View development guide
27
+ │ ├── schemas.json # Auto-generated view schemas
28
+ │ └── */view.tsx # React view components
29
+ └── skills/ # Optional: reusable skill modules
30
+ └── AGENTS.md # Skill development guide
31
+ ```
32
+
33
+ ## Quick Start
34
+
35
+ | Task | How to Do It |
36
+ | ----------------------- | ------------------------------------------- |
37
+ | Add a tool | Create `tools/<name>.py` with `@tool` |
38
+ | Add a view | Create `views/<id>/view.tsx` |
39
+ | Edit personality | Modify `prompts/agent.md` |
40
+ | Update metadata | Edit `metadata.json` |
41
+ | Generate schemas | Run `generate_schemas` MCP tool |
42
+ | Start dev server | Run `dev_start` MCP tool |
43
+ | Deploy to Hub | Run `deploy` MCP tool |
44
+
45
+ ## Code Style
46
+
47
+ ### Python (Tools)
48
+
49
+ - Python 3.11+ required
50
+ - Type hints for ALL parameters and return types
51
+ - Use `Optional[T]` for optional params
52
+ - Snake_case for functions and variables
53
+ - Return `dict` with `status` and data
54
+
55
+ ### TypeScript (Views)
56
+
57
+ - Functional components with hooks
58
+ - Explicit props interfaces
59
+ - camelCase for variables
60
+ - PascalCase for components
61
+ - Tailwind CSS for styling
62
+
63
+ ### Markdown (Prompts)
64
+
65
+ - Clear section headings
66
+ - Bullet points for guidelines
67
+ - Examples for expected behavior
68
+ - Template variables where needed
69
+
70
+ ## Key Files
71
+
72
+ ### agent.py
73
+
74
+ Factory function that creates the agent instance:
75
+
76
+ ```python
77
+ from a4e.sdk import AgentFactory
78
+
79
+ async def create_agent(model_client, agent_id):
80
+ return await AgentFactory.create_agent(
81
+ agent_path=agent_id,
82
+ model_client=model_client
83
+ )
84
+ ```
85
+
86
+ ### metadata.json
87
+
88
+ Agent marketplace metadata:
89
+
90
+ ```json
91
+ {
92
+ "id": "agent-id",
93
+ "name": "Display Name",
94
+ "category": "Category",
95
+ "description": "...",
96
+ "version": "1.0.0"
97
+ }
98
+ ```
99
+
100
+ ### prompts/agent.md
101
+
102
+ Main personality and instructions:
103
+
104
+ ```markdown
105
+ # Agent Name
106
+
107
+ You are [Agent Name], a specialized AI assistant...
108
+
109
+ ## Your Mission
110
+ ## Capabilities
111
+ ## Guidelines
112
+ ## Constraints
113
+ ```
114
+
115
+ ## Schema Generation
116
+
117
+ Schemas are automatically generated from code:
118
+
119
+ - **Tools**: Parsed from Python `@tool` decorators and docstrings
120
+ - **Views**: Parsed from TypeScript props interfaces
121
+
122
+ **Never edit schema files manually** — run `generate_schemas` after changes.
123
+
124
+ ## Development Workflow
125
+
126
+ 1. **Create** - Add tools, views, or prompts
127
+ 2. **Generate** - Run `generate_schemas` to update schemas
128
+ 3. **Test** - Use `dev_start` to run locally
129
+ 4. **Iterate** - Refine based on testing
130
+ 5. **Deploy** - Use `deploy` to publish
131
+
132
+ ## Security
133
+
134
+ - Never hardcode API keys or secrets
135
+ - Use environment variables for sensitive data
136
+ - Validate all user inputs in tools
137
+ - Sanitize data before rendering in views
138
+ - Handle errors gracefully
139
+
140
+ ## Troubleshooting
141
+
142
+ ### Tool not appearing
143
+
144
+ 1. Check `@tool` decorator is present
145
+ 2. Verify type hints and docstring
146
+ 3. Run `generate_schemas`
147
+
148
+ ### View not rendering
149
+
150
+ 1. Check default export
151
+ 2. Verify props interface
152
+ 3. Check for TypeScript errors
153
+
154
+ ### Agent not responding correctly
155
+
156
+ 1. Review `prompts/agent.md`
157
+ 2. Check tool implementations
158
+ 3. Verify schema generation ran
159
+
160
+ ## Subdirectory Guides
161
+
162
+ Each subdirectory has its own `AGENTS.md` with specific guidance:
163
+
164
+ - `tools/AGENTS.md` - Tool development patterns
165
+ - `views/AGENTS.md` - View component patterns
166
+ - `prompts/AGENTS.md` - Prompt engineering tips
167
+ - `skills/AGENTS.md` - Skill module development
168
+
@@ -0,0 +1,15 @@
1
+ from a4e.sdk import AgentFactory
2
+ from autogen_agentchat.agents import AssistantAgent
3
+
4
+ async def create_agent(model_client, agent_id):
5
+ """
6
+ Create the agent instance
7
+
8
+ Args:
9
+ model_client: The LLM client
10
+ agent_id: The ID of the agent
11
+ """
12
+ return await AgentFactory.create_agent(
13
+ agent_path=agent_id,
14
+ model_client=model_client
15
+ )