@datalayer/agent-runtimes 0.0.9 → 0.0.11
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.
- package/README.md +2 -1
- package/lib/{examples/components → components}/AgentConfiguration.d.ts +54 -9
- package/lib/components/AgentConfiguration.js +585 -0
- package/lib/{examples/components → components}/FooterMetrics.d.ts +1 -2
- package/lib/{examples/components → components}/Header.d.ts +1 -6
- package/lib/{examples/components → components}/Header.js +5 -39
- package/lib/{examples/components → components}/HeaderControls.d.ts +1 -2
- package/lib/{examples/components → components}/HeaderControls.js +1 -1
- package/lib/{examples/components → components}/LexicalEditor.d.ts +2 -3
- package/lib/{examples/components → components}/LexicalEditor.js +2 -2
- package/lib/components/MainContent.d.ts +34 -0
- package/lib/{examples/components → components}/MainContent.js +18 -9
- package/lib/components/McpServerManager.d.ts +30 -0
- package/lib/components/McpServerManager.js +331 -0
- package/lib/{examples/components → components}/MockFileBrowser.d.ts +1 -2
- package/lib/{examples/components → components}/SessionTabs.d.ts +2 -3
- package/lib/{examples/components → components}/TimeTravel.d.ts +1 -2
- package/lib/components/chat/components/AgentDetails.d.ts +3 -1
- package/lib/components/chat/components/AgentDetails.js +472 -22
- package/lib/components/chat/components/Chat.d.ts +37 -3
- package/lib/components/chat/components/Chat.js +29 -10
- package/lib/components/chat/components/ChatFloating.d.ts +27 -2
- package/lib/components/chat/components/ChatFloating.js +17 -10
- package/lib/components/chat/components/ChatPopupStandalone.js +1 -1
- package/lib/components/chat/components/ChatSidebar.d.ts +1 -1
- package/lib/components/chat/components/ChatStandalone.d.ts +1 -1
- package/lib/components/chat/components/ChatStandalone.js +1 -1
- package/lib/components/chat/components/ContextDistribution.d.ts +70 -6
- package/lib/components/chat/components/ContextDistribution.js +13 -6
- package/lib/components/chat/components/ContextInspector.d.ts +81 -0
- package/lib/components/chat/components/ContextInspector.js +263 -0
- package/lib/components/chat/components/ContextPanel.d.ts +112 -0
- package/lib/components/chat/components/ContextPanel.js +368 -0
- package/lib/components/chat/components/base/ChatBase.d.ts +74 -19
- package/lib/components/chat/components/base/ChatBase.js +296 -37
- package/lib/components/chat/components/index.d.ts +3 -1
- package/lib/components/chat/components/index.js +2 -0
- package/lib/components/chat/extensions/ExtensionRegistry.d.ts +1 -1
- package/lib/components/chat/extensions/index.d.ts +1 -0
- package/lib/components/chat/index.d.ts +3 -3
- package/lib/components/chat/protocols/AGUIAdapter.js +24 -4
- package/lib/components/chat/protocols/VercelAIAdapter.js +35 -1
- package/lib/components/chat/store/chatStore.d.ts +2 -3
- package/lib/components/chat/store/conversationStore.d.ts +83 -0
- package/lib/components/chat/store/conversationStore.js +174 -0
- package/lib/components/chat/store/index.d.ts +2 -1
- package/lib/components/chat/store/index.js +1 -0
- package/lib/components/chat/types/inference.d.ts +17 -0
- package/lib/components/chat/types/protocol.d.ts +10 -0
- package/lib/components/index.d.ts +23 -0
- package/lib/components/index.js +11 -0
- package/lib/config/agents/code-ai/agents.d.ts +25 -0
- package/lib/config/agents/code-ai/agents.js +70 -0
- package/lib/config/agents/code-ai/index.d.ts +1 -0
- package/lib/config/agents/code-ai/index.js +5 -0
- package/lib/config/agents/codemode-paper/agents.d.ts +29 -0
- package/lib/config/agents/codemode-paper/agents.js +288 -0
- package/lib/config/agents/codemode-paper/index.d.ts +1 -0
- package/lib/config/agents/codemode-paper/index.js +5 -0
- package/lib/config/agents/datalayer-ai/agents.d.ts +29 -0
- package/lib/config/agents/datalayer-ai/agents.js +267 -0
- package/lib/config/agents/datalayer-ai/index.d.ts +1 -0
- package/lib/config/agents/datalayer-ai/index.js +5 -0
- package/lib/config/agents/index.d.ts +19 -0
- package/lib/config/agents/index.js +38 -0
- package/lib/config/envvars.d.ts +28 -0
- package/lib/config/envvars.js +115 -0
- package/lib/config/index.d.ts +5 -0
- package/lib/config/index.js +9 -0
- package/lib/config/mcpServers.d.ts +18 -0
- package/lib/config/mcpServers.js +153 -0
- package/lib/config/skills.d.ts +27 -0
- package/lib/config/skills.js +60 -0
- package/lib/{lib → config}/utils.d.ts +1 -1
- package/lib/{lib → config}/utils.js +2 -2
- package/lib/examples/AgentRuntimeLexical2Example.d.ts +1 -0
- package/lib/examples/AgentRuntimeLexical2Example.js +3 -2
- package/lib/examples/AgentRuntimeLexicalExample.d.ts +1 -0
- package/lib/examples/AgentRuntimeLexicalExample.js +5 -3
- package/lib/examples/AgentRuntimeLexicalSidebarExample.d.ts +1 -0
- package/lib/examples/AgentRuntimeLexicalSidebarExample.js +3 -3
- package/lib/examples/AgentRuntimeNotebookExample.js +1 -1
- package/lib/examples/AgentSpaceFormExample.d.ts +2 -2
- package/lib/examples/AgentSpaceFormExample.js +215 -35
- package/lib/examples/CopilotKitLexicalExample.d.ts +1 -0
- package/lib/examples/CopilotKitLexicalExample.js +3 -2
- package/lib/examples/index.d.ts +1 -0
- package/lib/examples/stores/notebooks/NotebookExample2.ipynb.json +43 -43
- package/lib/hooks/useAGUI.d.ts +1 -1
- package/lib/hooks/useAGUI.js +1 -1
- package/lib/identity/types.d.ts +1 -1
- package/lib/index.d.ts +2 -0
- package/lib/index.js +1 -0
- package/lib/runtime/index.d.ts +3 -0
- package/lib/runtime/runtimeStore.d.ts +3 -4
- package/lib/runtime/useAgentConnection.d.ts +2 -3
- package/lib/runtime/useAgentRuntime.d.ts +2 -3
- package/lib/stories/Cell.stories.js +1 -1
- package/lib/tools/adapters/agent-runtimes/notebookHooks.js +1 -0
- package/lib/tools/adapters/copilotkit/notebookHooks.js +1 -0
- package/lib/types.d.ts +158 -0
- package/package.json +5 -6
- package/scripts/apply-patches.sh +1 -1
- package/scripts/codegen/generate_agents.py +863 -0
- package/scripts/codegen/generate_envvars.py +302 -0
- package/scripts/codegen/generate_mcp_servers.py +436 -0
- package/scripts/codegen/generate_skills.py +334 -0
- package/scripts/download-ai-elements.py +35 -20
- package/scripts/sync-jupyter.sh +6 -0
- package/lib/components/ui/accordion.d.ts +0 -7
- package/lib/components/ui/accordion.js +0 -22
- package/lib/components/ui/alert-dialog.d.ts +0 -14
- package/lib/components/ui/alert-dialog.js +0 -43
- package/lib/components/ui/alert.d.ts +0 -9
- package/lib/components/ui/alert.js +0 -24
- package/lib/components/ui/aspect-ratio.d.ts +0 -3
- package/lib/components/ui/aspect-ratio.js +0 -11
- package/lib/components/ui/avatar.d.ts +0 -6
- package/lib/components/ui/avatar.js +0 -18
- package/lib/components/ui/badge.d.ts +0 -9
- package/lib/components/ui/badge.js +0 -22
- package/lib/components/ui/breadcrumb.d.ts +0 -11
- package/lib/components/ui/breadcrumb.js +0 -27
- package/lib/components/ui/button-group.d.ts +0 -11
- package/lib/components/ui/button-group.js +0 -31
- package/lib/components/ui/button.d.ts +0 -13
- package/lib/components/ui/button.js +0 -39
- package/lib/components/ui/calendar.d.ts +0 -8
- package/lib/components/ui/calendar.js +0 -80
- package/lib/components/ui/card.d.ts +0 -9
- package/lib/components/ui/card.js +0 -24
- package/lib/components/ui/carousel.d.ts +0 -19
- package/lib/components/ui/carousel.js +0 -95
- package/lib/components/ui/chart.d.ts +0 -53
- package/lib/components/ui/chart.js +0 -136
- package/lib/components/ui/checkbox.d.ts +0 -4
- package/lib/components/ui/checkbox.js +0 -13
- package/lib/components/ui/collapsible.d.ts +0 -5
- package/lib/components/ui/collapsible.js +0 -17
- package/lib/components/ui/command.d.ts +0 -18
- package/lib/components/ui/command.js +0 -38
- package/lib/components/ui/context-menu.d.ts +0 -25
- package/lib/components/ui/context-menu.js +0 -55
- package/lib/components/ui/dialog.d.ts +0 -15
- package/lib/components/ui/dialog.js +0 -40
- package/lib/components/ui/drawer.d.ts +0 -13
- package/lib/components/ui/drawer.js +0 -39
- package/lib/components/ui/dropdown-menu.d.ts +0 -25
- package/lib/components/ui/dropdown-menu.js +0 -55
- package/lib/components/ui/empty.d.ts +0 -11
- package/lib/components/ui/empty.js +0 -37
- package/lib/components/ui/field.d.ts +0 -24
- package/lib/components/ui/field.js +0 -80
- package/lib/components/ui/form.d.ts +0 -24
- package/lib/components/ui/form.js +0 -63
- package/lib/components/ui/hover-card.d.ts +0 -6
- package/lib/components/ui/hover-card.js +0 -18
- package/lib/components/ui/input-group.d.ts +0 -19
- package/lib/components/ui/input-group.js +0 -69
- package/lib/components/ui/input-otp.d.ts +0 -11
- package/lib/components/ui/input-otp.js +0 -25
- package/lib/components/ui/input.d.ts +0 -3
- package/lib/components/ui/input.js +0 -6
- package/lib/components/ui/item.d.ts +0 -23
- package/lib/components/ui/item.js +0 -66
- package/lib/components/ui/kbd.d.ts +0 -3
- package/lib/components/ui/kbd.js +0 -13
- package/lib/components/ui/label.d.ts +0 -4
- package/lib/components/ui/label.js +0 -12
- package/lib/components/ui/menubar.d.ts +0 -26
- package/lib/components/ui/menubar.js +0 -58
- package/lib/components/ui/navigation-menu.d.ts +0 -14
- package/lib/components/ui/navigation-menu.js +0 -31
- package/lib/components/ui/pagination.d.ts +0 -13
- package/lib/components/ui/pagination.js +0 -29
- package/lib/components/ui/popover.d.ts +0 -7
- package/lib/components/ui/popover.js +0 -21
- package/lib/components/ui/progress.d.ts +0 -4
- package/lib/components/ui/progress.js +0 -12
- package/lib/components/ui/radio-group.d.ts +0 -5
- package/lib/components/ui/radio-group.js +0 -16
- package/lib/components/ui/resizable.d.ts +0 -8
- package/lib/components/ui/resizable.js +0 -19
- package/lib/components/ui/scroll-area.d.ts +0 -5
- package/lib/components/ui/scroll-area.js +0 -17
- package/lib/components/ui/select.d.ts +0 -15
- package/lib/components/ui/select.js +0 -42
- package/lib/components/ui/separator.d.ts +0 -4
- package/lib/components/ui/separator.js +0 -12
- package/lib/components/ui/sheet.d.ts +0 -13
- package/lib/components/ui/sheet.js +0 -44
- package/lib/components/ui/sidebar.d.ts +0 -69
- package/lib/components/ui/sidebar.js +0 -216
- package/lib/components/ui/skeleton.d.ts +0 -2
- package/lib/components/ui/skeleton.js +0 -10
- package/lib/components/ui/slider.d.ts +0 -4
- package/lib/components/ui/slider.js +0 -18
- package/lib/components/ui/sonner.d.ts +0 -3
- package/lib/components/ui/sonner.js +0 -25
- package/lib/components/ui/spinner.d.ts +0 -2
- package/lib/components/ui/spinner.js +0 -11
- package/lib/components/ui/switch.d.ts +0 -4
- package/lib/components/ui/switch.js +0 -12
- package/lib/components/ui/table.d.ts +0 -10
- package/lib/components/ui/table.js +0 -32
- package/lib/components/ui/tabs.d.ts +0 -7
- package/lib/components/ui/tabs.js +0 -21
- package/lib/components/ui/textarea.d.ts +0 -3
- package/lib/components/ui/textarea.js +0 -6
- package/lib/components/ui/toast.d.ts +0 -15
- package/lib/components/ui/toast.js +0 -38
- package/lib/components/ui/toaster.d.ts +0 -1
- package/lib/components/ui/toaster.js +0 -14
- package/lib/components/ui/toggle-group.d.ts +0 -9
- package/lib/components/ui/toggle-group.js +0 -26
- package/lib/components/ui/toggle.d.ts +0 -9
- package/lib/components/ui/toggle.js +0 -30
- package/lib/components/ui/tooltip.d.ts +0 -7
- package/lib/components/ui/tooltip.js +0 -21
- package/lib/components/vercel-ai-elements/artifact.d.ts +0 -23
- package/lib/components/vercel-ai-elements/artifact.js +0 -24
- package/lib/components/vercel-ai-elements/code-block.d.ts +0 -17
- package/lib/components/vercel-ai-elements/code-block.js +0 -94
- package/lib/components/vercel-ai-elements/conversation.d.ts +0 -15
- package/lib/components/vercel-ai-elements/conversation.js +0 -21
- package/lib/components/vercel-ai-elements/loader.d.ts +0 -5
- package/lib/components/vercel-ai-elements/loader.js +0 -8
- package/lib/components/vercel-ai-elements/message.d.ts +0 -46
- package/lib/components/vercel-ai-elements/message.js +0 -109
- package/lib/components/vercel-ai-elements/model-selector.d.ts +0 -35
- package/lib/components/vercel-ai-elements/model-selector.js +0 -22
- package/lib/components/vercel-ai-elements/prompt-input.d.ts +0 -195
- package/lib/components/vercel-ai-elements/prompt-input.js +0 -589
- package/lib/components/vercel-ai-elements/reasoning.d.ts +0 -26
- package/lib/components/vercel-ai-elements/reasoning.js +0 -80
- package/lib/components/vercel-ai-elements/shimmer.d.ts +0 -9
- package/lib/components/vercel-ai-elements/shimmer.js +0 -22
- package/lib/components/vercel-ai-elements/sources.d.ts +0 -12
- package/lib/components/vercel-ai-elements/sources.js +0 -13
- package/lib/components/vercel-ai-elements/suggestion.d.ts +0 -10
- package/lib/components/vercel-ai-elements/suggestion.js +0 -16
- package/lib/components/vercel-ai-elements/tool.d.ts +0 -23
- package/lib/components/vercel-ai-elements/tool.js +0 -52
- package/lib/examples/components/AgentConfiguration.js +0 -382
- package/lib/examples/components/MainContent.d.ts +0 -19
- package/lib/examples/components/index.d.ts +0 -10
- package/lib/examples/components/index.js +0 -13
- package/lib/examples/vercel-ai-elements/VercelAiElementsShowcase.d.ts +0 -12
- package/lib/examples/vercel-ai-elements/VercelAiElementsShowcase.js +0 -69
- package/lib/examples/vercel-ai-elements/components/ArtifactShowcase.d.ts +0 -1
- package/lib/examples/vercel-ai-elements/components/ArtifactShowcase.js +0 -85
- package/lib/examples/vercel-ai-elements/components/CodeBlockShowcase.d.ts +0 -1
- package/lib/examples/vercel-ai-elements/components/CodeBlockShowcase.js +0 -62
- package/lib/examples/vercel-ai-elements/components/ConversationShowcase.d.ts +0 -1
- package/lib/examples/vercel-ai-elements/components/ConversationShowcase.js +0 -51
- package/lib/examples/vercel-ai-elements/components/LoaderShowcase.d.ts +0 -1
- package/lib/examples/vercel-ai-elements/components/LoaderShowcase.js +0 -9
- package/lib/examples/vercel-ai-elements/components/MessageShowcase.d.ts +0 -1
- package/lib/examples/vercel-ai-elements/components/MessageShowcase.js +0 -56
- package/lib/examples/vercel-ai-elements/components/ModelSelectorShowcase.d.ts +0 -1
- package/lib/examples/vercel-ai-elements/components/ModelSelectorShowcase.js +0 -50
- package/lib/examples/vercel-ai-elements/components/PromptInputShowcase.d.ts +0 -1
- package/lib/examples/vercel-ai-elements/components/PromptInputShowcase.js +0 -16
- package/lib/examples/vercel-ai-elements/components/ReasoningShowcase.d.ts +0 -1
- package/lib/examples/vercel-ai-elements/components/ReasoningShowcase.js +0 -72
- package/lib/examples/vercel-ai-elements/components/ShimmerShowcase.d.ts +0 -1
- package/lib/examples/vercel-ai-elements/components/ShimmerShowcase.js +0 -9
- package/lib/examples/vercel-ai-elements/components/SourcesShowcase.d.ts +0 -1
- package/lib/examples/vercel-ai-elements/components/SourcesShowcase.js +0 -43
- package/lib/examples/vercel-ai-elements/components/SuggestionShowcase.d.ts +0 -1
- package/lib/examples/vercel-ai-elements/components/SuggestionShowcase.js +0 -31
- package/lib/examples/vercel-ai-elements/components/ToolShowcase.d.ts +0 -1
- package/lib/examples/vercel-ai-elements/components/ToolShowcase.js +0 -54
- package/lib/examples/vercel-ai-elements/index.d.ts +0 -13
- package/lib/examples/vercel-ai-elements/index.js +0 -17
- package/lib/examples/vercel-ai-elements/main.d.ts +0 -1
- package/lib/examples/vercel-ai-elements/main.js +0 -9
- package/lib/examples/vercel-ai-elements/showcase.css +0 -128
- package/lib/hooks/useToast.d.ts +0 -44
- package/lib/hooks/useToast.js +0 -128
- package/patches/@datalayer+jupyter-lexical+1.0.8.patch +0 -11628
- package/patches/@datalayer+jupyter-react+2.0.2.patch +0 -5338
- package/style/showcase-vercel-ai.css +0 -137
- /package/lib/{examples/components → components}/FooterMetrics.js +0 -0
- /package/lib/{examples/components → components}/MockFileBrowser.js +0 -0
- /package/lib/{examples/components → components}/SessionTabs.js +0 -0
- /package/lib/{examples/components → components}/TimeTravel.js +0 -0
- /package/lib/{models → types}/AIAgent.d.ts +0 -0
- /package/lib/{models → types}/AIAgent.js +0 -0
- /package/lib/{models → types}/index.d.ts +0 -0
- /package/lib/{models → types}/index.js +0 -0
|
@@ -0,0 +1,863 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Copyright (c) 2025-2026 Datalayer, Inc.
|
|
3
|
+
# Distributed under the terms of the Modified BSD License.
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
Agent Specification Code Generator.
|
|
7
|
+
|
|
8
|
+
Generates Python and TypeScript code from YAML agent specifications.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
import subprocess
|
|
13
|
+
import sys
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Any, Dict, List
|
|
16
|
+
|
|
17
|
+
import yaml
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _fmt_list(items: list[str]) -> str:
|
|
21
|
+
"""Format a list of strings with double quotes for ruff compliance."""
|
|
22
|
+
if not items:
|
|
23
|
+
return "[]"
|
|
24
|
+
return "[" + ", ".join(f'"{item}"' for item in items) + "]"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def load_yaml_specs(specs_dir: Path) -> List[tuple[str, Dict[str, Any]]]:
|
|
28
|
+
"""
|
|
29
|
+
Load all YAML agent specifications from directory and subdirectories.
|
|
30
|
+
|
|
31
|
+
Returns list of tuples: (subfolder_name, spec_dict)
|
|
32
|
+
where subfolder_name is the immediate parent folder name, or "" for root level.
|
|
33
|
+
"""
|
|
34
|
+
specs = []
|
|
35
|
+
|
|
36
|
+
# First, load specs from root level
|
|
37
|
+
for yaml_file in sorted(specs_dir.glob("*.yaml")):
|
|
38
|
+
with open(yaml_file, "r") as f:
|
|
39
|
+
spec = yaml.safe_load(f)
|
|
40
|
+
if spec: # Skip empty files
|
|
41
|
+
specs.append(("", spec))
|
|
42
|
+
|
|
43
|
+
# Then, load specs from subdirectories (one level deep)
|
|
44
|
+
for subdir in sorted(specs_dir.iterdir()):
|
|
45
|
+
if subdir.is_dir() and not subdir.name.startswith("."):
|
|
46
|
+
for yaml_file in sorted(subdir.glob("*.yaml")):
|
|
47
|
+
with open(yaml_file, "r") as f:
|
|
48
|
+
spec = yaml.safe_load(f)
|
|
49
|
+
if spec: # Skip empty files
|
|
50
|
+
specs.append((subdir.name, spec))
|
|
51
|
+
|
|
52
|
+
return specs
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def generate_python_code(specs: List[tuple[str, Dict[str, Any]]]) -> str:
|
|
56
|
+
"""Generate Python code from agent specifications."""
|
|
57
|
+
# Header
|
|
58
|
+
code = '''# Copyright (c) 2025-2026 Datalayer, Inc.
|
|
59
|
+
# Distributed under the terms of the Modified BSD License.
|
|
60
|
+
|
|
61
|
+
"""
|
|
62
|
+
Agent Library.
|
|
63
|
+
|
|
64
|
+
Predefined agent specifications that can be instantiated as AgentSpaces.
|
|
65
|
+
THIS FILE IS AUTO-GENERATED. DO NOT EDIT MANUALLY.
|
|
66
|
+
Generated from YAML specifications in specs/agents/
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
from typing import Dict
|
|
70
|
+
|
|
71
|
+
from agent_runtimes.mcp.catalog_mcp_servers import MCP_SERVER_CATALOG
|
|
72
|
+
from agent_runtimes.types import AgentSpec
|
|
73
|
+
|
|
74
|
+
# ============================================================================
|
|
75
|
+
# Agent Specs
|
|
76
|
+
# ============================================================================
|
|
77
|
+
|
|
78
|
+
'''
|
|
79
|
+
|
|
80
|
+
# Organize specs by subfolder
|
|
81
|
+
from collections import defaultdict
|
|
82
|
+
|
|
83
|
+
specs_by_folder: Dict[str, List[Dict[str, Any]]] = defaultdict(list)
|
|
84
|
+
for folder, spec in specs:
|
|
85
|
+
specs_by_folder[folder].append(spec)
|
|
86
|
+
|
|
87
|
+
# Generate agent spec constants organized by folder
|
|
88
|
+
agent_ids = []
|
|
89
|
+
|
|
90
|
+
# Sort folders: empty string (root) first, then alphabetically
|
|
91
|
+
sorted_folders = sorted(
|
|
92
|
+
specs_by_folder.keys(), key=lambda x: "" if x == "" else f"z{x}"
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
for folder in sorted_folders:
|
|
96
|
+
folder_specs = specs_by_folder[folder]
|
|
97
|
+
|
|
98
|
+
# Add folder header if not root
|
|
99
|
+
if folder:
|
|
100
|
+
code += f"\n# {folder.replace('-', ' ').title()} Agents\n"
|
|
101
|
+
code += f"# {'=' * 76}\n\n"
|
|
102
|
+
|
|
103
|
+
for spec in folder_specs:
|
|
104
|
+
agent_id = spec["id"]
|
|
105
|
+
# Prefix agent ID with folder name for uniqueness
|
|
106
|
+
full_agent_id = f"{folder}/{agent_id}" if folder else agent_id
|
|
107
|
+
# Create constant name: e.g., "data-acquisition" -> "DATA_ACQUISITION_AGENT_SPEC"
|
|
108
|
+
# But if id already ends with "-agent", don't duplicate: "github-agent" -> "GITHUB_AGENT_SPEC"
|
|
109
|
+
# NO folder prefix for Python constants
|
|
110
|
+
base_name = agent_id.upper().replace("-", "_")
|
|
111
|
+
|
|
112
|
+
if agent_id.endswith("-agent"):
|
|
113
|
+
const_name = base_name + "_SPEC"
|
|
114
|
+
else:
|
|
115
|
+
const_name = base_name + "_AGENT_SPEC"
|
|
116
|
+
agent_ids.append((full_agent_id, const_name, folder))
|
|
117
|
+
|
|
118
|
+
# Get MCP servers
|
|
119
|
+
mcp_server_ids = spec.get("mcp_servers", [])
|
|
120
|
+
mcp_servers_str = ", ".join(
|
|
121
|
+
f'MCP_SERVER_CATALOG["{sid}"]' for sid in mcp_server_ids
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
# Format optional fields
|
|
125
|
+
icon = f'"{spec.get("icon")}"' if spec.get("icon") else "None"
|
|
126
|
+
emoji = f'"{spec.get("emoji")}"' if spec.get("emoji") else "None"
|
|
127
|
+
color = f'"{spec.get("color")}"' if spec.get("color") else "None"
|
|
128
|
+
suggestions = spec.get("suggestions", [])
|
|
129
|
+
suggestions_str = (
|
|
130
|
+
"[\n "
|
|
131
|
+
+ ",\n ".join(f'"{s}"' for s in suggestions)
|
|
132
|
+
+ ",\n ]"
|
|
133
|
+
if suggestions
|
|
134
|
+
else "[]"
|
|
135
|
+
)
|
|
136
|
+
# Escape multi-line strings properly
|
|
137
|
+
welcome = (
|
|
138
|
+
spec.get("welcome_message", "").replace('"', '\\"').replace("\n", " ")
|
|
139
|
+
)
|
|
140
|
+
welcome_notebook = spec.get("welcome_notebook")
|
|
141
|
+
welcome_document = spec.get("welcome_document")
|
|
142
|
+
system_prompt = spec.get("system_prompt", "")
|
|
143
|
+
system_prompt_codemode_addons = spec.get(
|
|
144
|
+
"system_prompt_codemode_addons", ""
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
# Escape triple quotes in system prompts for Python triple-quoted strings
|
|
148
|
+
if system_prompt:
|
|
149
|
+
system_prompt = system_prompt.replace('"""', r"\"\"\"")
|
|
150
|
+
if system_prompt_codemode_addons:
|
|
151
|
+
system_prompt_codemode_addons = system_prompt_codemode_addons.replace(
|
|
152
|
+
'"""', r"\"\"\""
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
# Clean description for Python (single line)
|
|
156
|
+
description = (
|
|
157
|
+
spec["description"].replace("\n", " ").replace(" ", " ").strip()
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
# Use triple quotes for multiline system prompts
|
|
161
|
+
system_prompt_str = f'"""{system_prompt}"""' if system_prompt else "None"
|
|
162
|
+
system_prompt_codemode_addons_str = (
|
|
163
|
+
f'"""{system_prompt_codemode_addons}"""'
|
|
164
|
+
if system_prompt_codemode_addons
|
|
165
|
+
else "None"
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
code += f'''{const_name} = AgentSpec(
|
|
169
|
+
id="{full_agent_id}",
|
|
170
|
+
name="{spec["name"]}",
|
|
171
|
+
description="{description}",
|
|
172
|
+
tags={_fmt_list(spec.get("tags", []))},
|
|
173
|
+
enabled={spec.get("enabled", True)},
|
|
174
|
+
mcp_servers=[{mcp_servers_str}],
|
|
175
|
+
skills={_fmt_list(spec.get("skills", []))},
|
|
176
|
+
environment_name="{spec.get("environment_name", "ai-agents-env")}",
|
|
177
|
+
icon={icon},
|
|
178
|
+
emoji={emoji},
|
|
179
|
+
color={color},
|
|
180
|
+
suggestions={suggestions_str},
|
|
181
|
+
welcome_message="{welcome}",
|
|
182
|
+
welcome_notebook={f'"{welcome_notebook}"' if welcome_notebook else "None"},
|
|
183
|
+
welcome_document={f'"{welcome_document}"' if welcome_document else "None"},
|
|
184
|
+
system_prompt={system_prompt_str},
|
|
185
|
+
system_prompt_codemode_addons={system_prompt_codemode_addons_str},
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
'''
|
|
189
|
+
|
|
190
|
+
# Generate registry organized by folder
|
|
191
|
+
code += """
|
|
192
|
+
# ============================================================================
|
|
193
|
+
# Agent Specs Registry
|
|
194
|
+
# ============================================================================
|
|
195
|
+
|
|
196
|
+
AGENT_SPECS: Dict[str, AgentSpec] = {
|
|
197
|
+
"""
|
|
198
|
+
|
|
199
|
+
# Sort by folder for organized registry
|
|
200
|
+
for folder in sorted_folders:
|
|
201
|
+
folder_agents = [(aid, cname) for aid, cname, f in agent_ids if f == folder]
|
|
202
|
+
if folder_agents and folder:
|
|
203
|
+
code += f" # {folder.replace('-', ' ').title()}\n"
|
|
204
|
+
for full_agent_id, const_name in folder_agents:
|
|
205
|
+
code += f' "{full_agent_id}": {const_name},\n'
|
|
206
|
+
if folder_agents and folder:
|
|
207
|
+
code += "\n"
|
|
208
|
+
|
|
209
|
+
code += """}
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def get_agent_spec(agent_id: str) -> AgentSpec | None:
|
|
213
|
+
\"\"\"
|
|
214
|
+
Get an agent specification by ID.
|
|
215
|
+
|
|
216
|
+
Args:
|
|
217
|
+
agent_id: The unique identifier of the agent.
|
|
218
|
+
|
|
219
|
+
Returns:
|
|
220
|
+
The AgentSpec configuration, or None if not found.
|
|
221
|
+
\"\"\"
|
|
222
|
+
return AGENT_SPECS.get(agent_id)
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def list_agent_specs() -> list[AgentSpec]:
|
|
226
|
+
\"\"\"
|
|
227
|
+
List all available agent specifications.
|
|
228
|
+
|
|
229
|
+
Returns:
|
|
230
|
+
List of all AgentSpec configurations.
|
|
231
|
+
\"\"\"
|
|
232
|
+
return list(AGENT_SPECS.values())
|
|
233
|
+
"""
|
|
234
|
+
|
|
235
|
+
return code
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def generate_typescript_code(
|
|
239
|
+
specs: List[tuple[str, Dict[str, Any]]], mcp_specs_dir: str, skills_specs_dir: str
|
|
240
|
+
) -> str:
|
|
241
|
+
"""Generate TypeScript code from agent specifications."""
|
|
242
|
+
# Load available MCP servers from specs
|
|
243
|
+
import glob
|
|
244
|
+
import os
|
|
245
|
+
|
|
246
|
+
mcp_server_files = glob.glob(os.path.join(mcp_specs_dir, "*.yaml"))
|
|
247
|
+
mcp_server_ids = [
|
|
248
|
+
os.path.basename(f).replace(".yaml", "") for f in mcp_server_files
|
|
249
|
+
]
|
|
250
|
+
mcp_server_ids.sort()
|
|
251
|
+
|
|
252
|
+
# Load available skills from specs
|
|
253
|
+
skill_files = glob.glob(os.path.join(skills_specs_dir, "*.yaml"))
|
|
254
|
+
skill_ids = [os.path.basename(f).replace(".yaml", "") for f in skill_files]
|
|
255
|
+
skill_ids.sort()
|
|
256
|
+
|
|
257
|
+
# Determine which MCP servers and skills are actually used in these specs
|
|
258
|
+
used_mcp_servers = set()
|
|
259
|
+
used_skills = set()
|
|
260
|
+
for _, spec in specs:
|
|
261
|
+
for server in spec.get("mcp_servers", []):
|
|
262
|
+
used_mcp_servers.add(server)
|
|
263
|
+
for skill in spec.get("skills", []):
|
|
264
|
+
used_skills.add(skill)
|
|
265
|
+
|
|
266
|
+
# Only import what's actually used
|
|
267
|
+
mcp_imports = []
|
|
268
|
+
mcp_map_entries = []
|
|
269
|
+
for server_id in mcp_server_ids:
|
|
270
|
+
if server_id in used_mcp_servers:
|
|
271
|
+
const_name = server_id.upper().replace("-", "_") + "_MCP_SERVER"
|
|
272
|
+
mcp_imports.append(const_name)
|
|
273
|
+
mcp_map_entries.append(f" '{server_id}': {const_name},")
|
|
274
|
+
|
|
275
|
+
# Generate skill import names and map entries
|
|
276
|
+
skill_imports = []
|
|
277
|
+
skill_map_entries = []
|
|
278
|
+
for sid in skill_ids:
|
|
279
|
+
if sid in used_skills:
|
|
280
|
+
const_name = sid.upper().replace("-", "_") + "_SKILL_SPEC"
|
|
281
|
+
skill_imports.append(const_name)
|
|
282
|
+
skill_map_entries.append(f" '{sid}': {const_name},")
|
|
283
|
+
|
|
284
|
+
# Determine if we need any helper code
|
|
285
|
+
has_mcp = len(mcp_imports) > 0
|
|
286
|
+
has_skills = len(skill_imports) > 0
|
|
287
|
+
|
|
288
|
+
# Header
|
|
289
|
+
code = """/*
|
|
290
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
291
|
+
* Distributed under the terms of the Modified BSD License.
|
|
292
|
+
*/
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Agent Library.
|
|
296
|
+
*
|
|
297
|
+
* Predefined agent specifications that can be instantiated as AgentSpaces.
|
|
298
|
+
* THIS FILE IS AUTO-GENERATED. DO NOT EDIT MANUALLY.
|
|
299
|
+
* Generated from YAML specifications in specs/agents/
|
|
300
|
+
*/
|
|
301
|
+
|
|
302
|
+
import type { AgentSpec } from '../../../types';
|
|
303
|
+
"""
|
|
304
|
+
|
|
305
|
+
# Only add MCP server imports if needed
|
|
306
|
+
if has_mcp:
|
|
307
|
+
code += "import {\n"
|
|
308
|
+
code += " " + ",\n ".join(mcp_imports) + ",\n"
|
|
309
|
+
code += "} from '../../mcpServers';\n"
|
|
310
|
+
|
|
311
|
+
# Only add skill imports if needed
|
|
312
|
+
if has_skills:
|
|
313
|
+
code += "import {\n"
|
|
314
|
+
code += " " + ",\n ".join(skill_imports) + ",\n"
|
|
315
|
+
code += "} from '../../skills';\n"
|
|
316
|
+
code += "import type { SkillSpec } from '../../skills';\n"
|
|
317
|
+
|
|
318
|
+
# Only add MCP server lookup if used
|
|
319
|
+
if has_mcp:
|
|
320
|
+
code += """
|
|
321
|
+
// ============================================================================
|
|
322
|
+
// MCP Server Lookup
|
|
323
|
+
// ============================================================================
|
|
324
|
+
|
|
325
|
+
const MCP_SERVER_MAP: Record<string, any> = {
|
|
326
|
+
"""
|
|
327
|
+
code += "\n".join(mcp_map_entries) + "\n"
|
|
328
|
+
code += "};\n"
|
|
329
|
+
|
|
330
|
+
# Only add skill lookup if used
|
|
331
|
+
if has_skills:
|
|
332
|
+
code += """
|
|
333
|
+
/**
|
|
334
|
+
* Map skill IDs to SkillSpec objects, converting to AgentSkillSpec shape.
|
|
335
|
+
*/
|
|
336
|
+
const SKILL_MAP: Record<string, any> = {
|
|
337
|
+
"""
|
|
338
|
+
code += "\n".join(skill_map_entries) + "\n"
|
|
339
|
+
code += "};\n"
|
|
340
|
+
code += """
|
|
341
|
+
function toAgentSkillSpec(skill: SkillSpec) {
|
|
342
|
+
return {
|
|
343
|
+
id: skill.id,
|
|
344
|
+
name: skill.name,
|
|
345
|
+
description: skill.description,
|
|
346
|
+
version: '1.0.0',
|
|
347
|
+
tags: skill.tags,
|
|
348
|
+
enabled: skill.enabled,
|
|
349
|
+
requiredEnvVars: skill.requiredEnvVars,
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
"""
|
|
353
|
+
|
|
354
|
+
code += """
|
|
355
|
+
// ============================================================================
|
|
356
|
+
// Agent Specs
|
|
357
|
+
// ============================================================================
|
|
358
|
+
|
|
359
|
+
"""
|
|
360
|
+
|
|
361
|
+
# Organize specs by subfolder for TypeScript
|
|
362
|
+
from collections import defaultdict
|
|
363
|
+
|
|
364
|
+
specs_by_folder: Dict[str, List[Dict[str, Any]]] = defaultdict(list)
|
|
365
|
+
for folder, spec in specs:
|
|
366
|
+
specs_by_folder[folder].append(spec)
|
|
367
|
+
|
|
368
|
+
# Sort folders: empty string (root) first, then alphabetically
|
|
369
|
+
sorted_folders = sorted(
|
|
370
|
+
specs_by_folder.keys(), key=lambda x: "" if x == "" else f"z{x}"
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
# Generate agent spec constants organized by folder
|
|
374
|
+
agent_ids = []
|
|
375
|
+
|
|
376
|
+
for folder in sorted_folders:
|
|
377
|
+
folder_specs = specs_by_folder[folder]
|
|
378
|
+
|
|
379
|
+
# Add folder header if not root
|
|
380
|
+
if folder:
|
|
381
|
+
code += f"// {folder.replace('-', ' ').title()} Agents\n"
|
|
382
|
+
code += f"// {'=' * 76}\n\n"
|
|
383
|
+
|
|
384
|
+
for spec in folder_specs:
|
|
385
|
+
agent_id = spec["id"]
|
|
386
|
+
# Prefix agent ID with folder name for uniqueness
|
|
387
|
+
full_agent_id = f"{folder}/{agent_id}" if folder else agent_id
|
|
388
|
+
# Create constant name: e.g., "data-acquisition" -> "DATA_ACQUISITION_AGENT_SPEC"
|
|
389
|
+
# But if id already ends with "-agent", don't duplicate: "github-agent" -> "GITHUB_AGENT_SPEC"
|
|
390
|
+
# NO folder prefix for TypeScript constants
|
|
391
|
+
base_name = agent_id.upper().replace("-", "_")
|
|
392
|
+
|
|
393
|
+
if agent_id.endswith("-agent"):
|
|
394
|
+
const_name = base_name + "_SPEC"
|
|
395
|
+
else:
|
|
396
|
+
const_name = base_name + "_AGENT_SPEC"
|
|
397
|
+
agent_ids.append((full_agent_id, const_name, folder))
|
|
398
|
+
|
|
399
|
+
# Get MCP servers
|
|
400
|
+
mcp_server_ids = spec.get("mcp_servers", [])
|
|
401
|
+
if has_mcp and mcp_server_ids:
|
|
402
|
+
mcp_servers_str = ", ".join(
|
|
403
|
+
f"MCP_SERVER_MAP['{sid}']" for sid in mcp_server_ids
|
|
404
|
+
)
|
|
405
|
+
else:
|
|
406
|
+
mcp_servers_str = ""
|
|
407
|
+
|
|
408
|
+
# Get skills - resolve to AgentSkillSpec via toAgentSkillSpec
|
|
409
|
+
skill_ids_list = spec.get("skills", [])
|
|
410
|
+
if has_skills and skill_ids_list:
|
|
411
|
+
skills_str = ", ".join(
|
|
412
|
+
f"toAgentSkillSpec(SKILL_MAP['{sid}'])" for sid in skill_ids_list
|
|
413
|
+
)
|
|
414
|
+
else:
|
|
415
|
+
skills_str = ""
|
|
416
|
+
|
|
417
|
+
# Format tags and suggestions as arrays
|
|
418
|
+
tags = spec.get("tags", [])
|
|
419
|
+
tags_str = "[" + ", ".join(f"'{t}'" for t in tags) + "]"
|
|
420
|
+
|
|
421
|
+
suggestions = spec.get("suggestions", [])
|
|
422
|
+
# Escape single quotes in suggestions for TypeScript
|
|
423
|
+
escaped_suggestions = [s.replace("'", "\\'") for s in suggestions]
|
|
424
|
+
suggestions_str = (
|
|
425
|
+
"[\n "
|
|
426
|
+
+ ",\n ".join(f"'{s}'" for s in escaped_suggestions)
|
|
427
|
+
+ ",\n ]"
|
|
428
|
+
if suggestions
|
|
429
|
+
else "[]"
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
# Format optional fields
|
|
433
|
+
icon = f"'{spec.get('icon')}'" if spec.get("icon") else "undefined"
|
|
434
|
+
emoji = f"'{spec.get('emoji')}'" if spec.get("emoji") else "undefined"
|
|
435
|
+
color = f"'{spec.get('color')}'" if spec.get("color") else "undefined"
|
|
436
|
+
system_prompt = spec.get("system_prompt")
|
|
437
|
+
system_prompt_codemode_addons = spec.get("system_prompt_codemode_addons")
|
|
438
|
+
|
|
439
|
+
# Escape backticks for TypeScript template literals
|
|
440
|
+
if system_prompt:
|
|
441
|
+
system_prompt = system_prompt.replace("`", "\\`")
|
|
442
|
+
if system_prompt_codemode_addons:
|
|
443
|
+
system_prompt_codemode_addons = system_prompt_codemode_addons.replace(
|
|
444
|
+
"`", "\\`"
|
|
445
|
+
)
|
|
446
|
+
|
|
447
|
+
# Clean description for TypeScript (multi-line template literal)
|
|
448
|
+
description = (
|
|
449
|
+
spec["description"].replace("\n", " ").replace(" ", " ").strip()
|
|
450
|
+
)
|
|
451
|
+
|
|
452
|
+
code += f"""export const {const_name}: AgentSpec = {{
|
|
453
|
+
id: '{full_agent_id}',
|
|
454
|
+
name: '{spec["name"]}',
|
|
455
|
+
description: `{description}`,
|
|
456
|
+
tags: {tags_str},
|
|
457
|
+
enabled: {str(spec.get("enabled", True)).lower()},
|
|
458
|
+
mcpServers: [{mcp_servers_str}],
|
|
459
|
+
skills: [{skills_str}],
|
|
460
|
+
environmentName: '{spec.get("environment_name", "ai-agents-env")}',
|
|
461
|
+
icon: {icon},
|
|
462
|
+
emoji: {emoji},
|
|
463
|
+
color: {color},
|
|
464
|
+
suggestions: {suggestions_str},
|
|
465
|
+
systemPrompt: {f"`{system_prompt}`" if system_prompt else "undefined"},
|
|
466
|
+
systemPromptCodemodeAddons: {f"`{system_prompt_codemode_addons}`" if system_prompt_codemode_addons else "undefined"},
|
|
467
|
+
}};
|
|
468
|
+
|
|
469
|
+
"""
|
|
470
|
+
|
|
471
|
+
# Generate registry organized by folder
|
|
472
|
+
code += """// ============================================================================
|
|
473
|
+
// Agent Specs Registry
|
|
474
|
+
// ============================================================================
|
|
475
|
+
|
|
476
|
+
export const AGENT_SPECS: Record<string, AgentSpec> = {
|
|
477
|
+
"""
|
|
478
|
+
|
|
479
|
+
# Sort by folder for organized registry
|
|
480
|
+
for folder in sorted_folders:
|
|
481
|
+
folder_agents = [(aid, cname) for aid, cname, f in agent_ids if f == folder]
|
|
482
|
+
if folder_agents and folder:
|
|
483
|
+
code += f" // {folder.replace('-', ' ').title()}\n"
|
|
484
|
+
for full_agent_id, const_name in folder_agents:
|
|
485
|
+
code += f" '{full_agent_id}': {const_name},\n"
|
|
486
|
+
if folder_agents and folder:
|
|
487
|
+
code += "\n"
|
|
488
|
+
|
|
489
|
+
code += """};
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Get an agent specification by ID.
|
|
493
|
+
*/
|
|
494
|
+
export function getAgentSpecs(agentId: string): AgentSpec | undefined {
|
|
495
|
+
return AGENT_SPECS[agentId];
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* List all available agent specifications.
|
|
500
|
+
*/
|
|
501
|
+
export function listAgentSpecs(): AgentSpec[] {
|
|
502
|
+
return Object.values(AGENT_SPECS);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Collect all required environment variables for an agent spec.
|
|
507
|
+
*
|
|
508
|
+
* Iterates over the spec's MCP servers and skills and returns the
|
|
509
|
+
* deduplicated union of their `requiredEnvVars` arrays.
|
|
510
|
+
*/
|
|
511
|
+
export function getAgentSpecRequiredEnvVars(spec: AgentSpec): string[] {
|
|
512
|
+
const vars = new Set<string>();
|
|
513
|
+
for (const server of spec.mcpServers) {
|
|
514
|
+
for (const v of server.requiredEnvVars ?? []) {
|
|
515
|
+
vars.add(v);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
for (const skill of spec.skills) {
|
|
519
|
+
for (const v of skill.requiredEnvVars ?? []) {
|
|
520
|
+
vars.add(v);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
return Array.from(vars);
|
|
524
|
+
}
|
|
525
|
+
"""
|
|
526
|
+
|
|
527
|
+
return code
|
|
528
|
+
|
|
529
|
+
|
|
530
|
+
def update_init_file(
|
|
531
|
+
specs: List[tuple[str, Dict[str, Any]]], init_file_path: Path
|
|
532
|
+
) -> None:
|
|
533
|
+
"""Update __init__.py with the new agent spec constants."""
|
|
534
|
+
# Collect all constant names
|
|
535
|
+
const_names = []
|
|
536
|
+
for folder, spec in specs:
|
|
537
|
+
agent_id = spec["id"]
|
|
538
|
+
if folder:
|
|
539
|
+
base_name = (
|
|
540
|
+
f"{folder}_{agent_id}".upper().replace("-", "_").replace("/", "_")
|
|
541
|
+
)
|
|
542
|
+
else:
|
|
543
|
+
base_name = agent_id.upper().replace("-", "_")
|
|
544
|
+
|
|
545
|
+
if agent_id.endswith("-agent"):
|
|
546
|
+
const_name = base_name + "_SPEC"
|
|
547
|
+
else:
|
|
548
|
+
const_name = base_name + "_AGENT_SPEC"
|
|
549
|
+
const_names.append(const_name)
|
|
550
|
+
|
|
551
|
+
# Sort for consistent ordering
|
|
552
|
+
const_names.sort()
|
|
553
|
+
|
|
554
|
+
# Read the current __init__.py
|
|
555
|
+
with open(init_file_path, "r") as f:
|
|
556
|
+
content = f.read()
|
|
557
|
+
|
|
558
|
+
# Find the agents import block and replace it
|
|
559
|
+
import re
|
|
560
|
+
|
|
561
|
+
# Pattern to match the entire from .agents import block
|
|
562
|
+
pattern = r"(from \.agents import \(\n)(.*?)(\n\))"
|
|
563
|
+
|
|
564
|
+
# Build new imports
|
|
565
|
+
new_imports = " AGENT_SPECS,\n"
|
|
566
|
+
for const_name in const_names:
|
|
567
|
+
new_imports += f" {const_name},\n"
|
|
568
|
+
new_imports += " get_agent_spec,\n"
|
|
569
|
+
new_imports += " list_agent_specs,"
|
|
570
|
+
|
|
571
|
+
# Replace the imports
|
|
572
|
+
new_content = re.sub(pattern, r"\1" + new_imports + r"\3", content, flags=re.DOTALL)
|
|
573
|
+
|
|
574
|
+
# Write back
|
|
575
|
+
with open(init_file_path, "w") as f:
|
|
576
|
+
f.write(new_content)
|
|
577
|
+
|
|
578
|
+
|
|
579
|
+
def generate_subfolder_structure(specs: List[tuple[str, Dict[str, Any]]], args):
|
|
580
|
+
"""Generate separate agent files per subfolder."""
|
|
581
|
+
from collections import defaultdict
|
|
582
|
+
|
|
583
|
+
# Organize specs by folder
|
|
584
|
+
specs_by_folder: Dict[str, List[Dict[str, Any]]] = defaultdict(list)
|
|
585
|
+
for folder, spec in specs:
|
|
586
|
+
specs_by_folder[folder].append(spec)
|
|
587
|
+
|
|
588
|
+
# Get MCP and skills specs directories
|
|
589
|
+
mcp_specs_dir = args.specs_dir.parent / "mcp-servers"
|
|
590
|
+
skills_specs_dir = args.specs_dir.parent / "skills"
|
|
591
|
+
|
|
592
|
+
# Determine base directories
|
|
593
|
+
python_base = args.python_output.parent / "agents"
|
|
594
|
+
typescript_base = args.typescript_output.parent / "agents"
|
|
595
|
+
|
|
596
|
+
print(f"Generating subfolder structure in {python_base} and {typescript_base}...")
|
|
597
|
+
|
|
598
|
+
# Generate files for each folder
|
|
599
|
+
all_python_imports = []
|
|
600
|
+
all_typescript_imports = []
|
|
601
|
+
|
|
602
|
+
for folder, folder_specs in sorted(specs_by_folder.items()):
|
|
603
|
+
if not folder: # Skip root level for now
|
|
604
|
+
continue
|
|
605
|
+
|
|
606
|
+
print(f" Generating agents for subfolder: {folder}")
|
|
607
|
+
|
|
608
|
+
# Convert folder name to valid Python module name (replace hyphens with underscores)
|
|
609
|
+
folder_python_name = folder.replace("-", "_")
|
|
610
|
+
|
|
611
|
+
# Create Python subfolder file
|
|
612
|
+
python_folder_dir = python_base / folder_python_name
|
|
613
|
+
python_folder_dir.mkdir(parents=True, exist_ok=True)
|
|
614
|
+
python_file = python_folder_dir / "agents.py"
|
|
615
|
+
|
|
616
|
+
# Generate Python code for this folder
|
|
617
|
+
python_code = generate_python_code([(folder, spec) for spec in folder_specs])
|
|
618
|
+
with open(python_file, "w") as f:
|
|
619
|
+
f.write(python_code)
|
|
620
|
+
|
|
621
|
+
# Create __init__.py for Python subfolder
|
|
622
|
+
python_init = python_folder_dir / "__init__.py"
|
|
623
|
+
with open(python_init, "w") as f:
|
|
624
|
+
f.write(f"""# Copyright (c) 2025-2026 Datalayer, Inc.
|
|
625
|
+
# Distributed under the terms of the Modified BSD License.
|
|
626
|
+
|
|
627
|
+
from .agents import *
|
|
628
|
+
|
|
629
|
+
__all__ = ["AGENT_SPECS", "get_agent_spec", "list_agent_specs"]
|
|
630
|
+
""")
|
|
631
|
+
|
|
632
|
+
# Collect imports for main index
|
|
633
|
+
all_python_imports.append(
|
|
634
|
+
f"from .{folder_python_name} import AGENT_SPECS as {folder_python_name.upper()}_AGENTS"
|
|
635
|
+
)
|
|
636
|
+
|
|
637
|
+
# Create TypeScript subfolder file
|
|
638
|
+
typescript_folder_dir = typescript_base / folder
|
|
639
|
+
typescript_folder_dir.mkdir(parents=True, exist_ok=True)
|
|
640
|
+
typescript_file = typescript_folder_dir / "agents.ts"
|
|
641
|
+
|
|
642
|
+
# Generate TypeScript code for this folder
|
|
643
|
+
typescript_code = generate_typescript_code(
|
|
644
|
+
[(folder, spec) for spec in folder_specs],
|
|
645
|
+
str(mcp_specs_dir),
|
|
646
|
+
str(skills_specs_dir),
|
|
647
|
+
)
|
|
648
|
+
with open(typescript_file, "w") as f:
|
|
649
|
+
f.write(typescript_code)
|
|
650
|
+
|
|
651
|
+
# Create index.ts for TypeScript subfolder
|
|
652
|
+
typescript_index = typescript_folder_dir / "index.ts"
|
|
653
|
+
with open(typescript_index, "w") as f:
|
|
654
|
+
f.write(f"""/*
|
|
655
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
656
|
+
* Distributed under the terms of the Modified BSD License.
|
|
657
|
+
*/
|
|
658
|
+
|
|
659
|
+
export * from './agents';
|
|
660
|
+
""")
|
|
661
|
+
|
|
662
|
+
# Collect imports for main index
|
|
663
|
+
all_typescript_imports.append(f"export * from './{folder}';")
|
|
664
|
+
|
|
665
|
+
# Create main Python index file
|
|
666
|
+
python_index = python_base / "__init__.py"
|
|
667
|
+
python_index_content = """# Copyright (c) 2025-2026 Datalayer, Inc.
|
|
668
|
+
# Distributed under the terms of the Modified BSD License.
|
|
669
|
+
|
|
670
|
+
\"\"\"
|
|
671
|
+
Agent Library - Subfolder Organization.
|
|
672
|
+
|
|
673
|
+
THIS FILE IS AUTO-GENERATED. DO NOT EDIT MANUALLY.
|
|
674
|
+
\"\"\"
|
|
675
|
+
|
|
676
|
+
from typing import Dict
|
|
677
|
+
from agent_runtimes.types import AgentSpec
|
|
678
|
+
|
|
679
|
+
"""
|
|
680
|
+
|
|
681
|
+
# Add imports
|
|
682
|
+
for imp in all_python_imports:
|
|
683
|
+
python_index_content += f"{imp}\n"
|
|
684
|
+
|
|
685
|
+
# Merge all agent specs
|
|
686
|
+
python_index_content += """
|
|
687
|
+
# Merge all agent specs from subfolders
|
|
688
|
+
AGENT_SPECS: Dict[str, AgentSpec] = {}
|
|
689
|
+
"""
|
|
690
|
+
|
|
691
|
+
for folder in sorted(specs_by_folder.keys()):
|
|
692
|
+
if folder:
|
|
693
|
+
folder_python_name = folder.replace("-", "_")
|
|
694
|
+
python_index_content += (
|
|
695
|
+
f"AGENT_SPECS.update({folder_python_name.upper()}_AGENTS)\n"
|
|
696
|
+
)
|
|
697
|
+
|
|
698
|
+
python_index_content += """
|
|
699
|
+
|
|
700
|
+
def get_agent_spec(agent_id: str) -> AgentSpec | None:
|
|
701
|
+
\"\"\"Get an agent specification by ID.\"\"\"
|
|
702
|
+
return AGENT_SPECS.get(agent_id)
|
|
703
|
+
|
|
704
|
+
|
|
705
|
+
def list_agent_specs() -> list[AgentSpec]:
|
|
706
|
+
\"\"\"List all available agent specifications.\"\"\"
|
|
707
|
+
return list(AGENT_SPECS.values())
|
|
708
|
+
|
|
709
|
+
__all__ = ["AGENT_SPECS", "get_agent_spec", "list_agent_specs"]
|
|
710
|
+
"""
|
|
711
|
+
|
|
712
|
+
with open(python_index, "w") as f:
|
|
713
|
+
f.write(python_index_content)
|
|
714
|
+
|
|
715
|
+
# Create main TypeScript index file
|
|
716
|
+
typescript_index = typescript_base / "index.ts"
|
|
717
|
+
typescript_index_content = """/*
|
|
718
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
719
|
+
* Distributed under the terms of the Modified BSD License.
|
|
720
|
+
*/
|
|
721
|
+
|
|
722
|
+
/**
|
|
723
|
+
* Agent Library - Subfolder Organization.
|
|
724
|
+
*
|
|
725
|
+
* THIS FILE IS AUTO-GENERATED. DO NOT EDIT MANUALLY.
|
|
726
|
+
*/
|
|
727
|
+
|
|
728
|
+
import type { AgentSpec } from '../../types';
|
|
729
|
+
|
|
730
|
+
"""
|
|
731
|
+
|
|
732
|
+
# Import AGENT_SPECS from each subfolder
|
|
733
|
+
for folder in sorted(specs_by_folder.keys()):
|
|
734
|
+
if folder:
|
|
735
|
+
folder_const = folder.replace("-", "_").upper()
|
|
736
|
+
typescript_index_content += f"import {{ AGENT_SPECS as {folder_const}_AGENTS }} from './{folder}';\n"
|
|
737
|
+
|
|
738
|
+
typescript_index_content += """
|
|
739
|
+
// Merge all agent specs from subfolders
|
|
740
|
+
export const AGENT_SPECS: Record<string, AgentSpec> = {
|
|
741
|
+
"""
|
|
742
|
+
|
|
743
|
+
for folder in sorted(specs_by_folder.keys()):
|
|
744
|
+
if folder:
|
|
745
|
+
folder_const = folder.replace("-", "_").upper()
|
|
746
|
+
typescript_index_content += f" ...{folder_const}_AGENTS,\n"
|
|
747
|
+
|
|
748
|
+
typescript_index_content += """};
|
|
749
|
+
|
|
750
|
+
/**
|
|
751
|
+
* Get an agent specification by ID.
|
|
752
|
+
*/
|
|
753
|
+
export function getAgentSpecs(agentId: string): AgentSpec | undefined {
|
|
754
|
+
return AGENT_SPECS[agentId];
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
/**
|
|
758
|
+
* List all available agent specifications.
|
|
759
|
+
*/
|
|
760
|
+
export function listAgentSpecs(): AgentSpec[] {
|
|
761
|
+
return Object.values(AGENT_SPECS);
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
/**
|
|
765
|
+
* Collect all required environment variables for an agent spec.
|
|
766
|
+
*/
|
|
767
|
+
export function getAgentSpecRequiredEnvVars(spec: AgentSpec): string[] {
|
|
768
|
+
const vars = new Set<string>();
|
|
769
|
+
for (const server of spec.mcpServers) {
|
|
770
|
+
for (const v of server.requiredEnvVars ?? []) {
|
|
771
|
+
vars.add(v);
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
for (const skill of spec.skills) {
|
|
775
|
+
for (const v of skill.requiredEnvVars ?? []) {
|
|
776
|
+
vars.add(v);
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
return Array.from(vars);
|
|
780
|
+
}
|
|
781
|
+
"""
|
|
782
|
+
|
|
783
|
+
with open(typescript_index, "w") as f:
|
|
784
|
+
f.write(typescript_index_content)
|
|
785
|
+
|
|
786
|
+
print(f"✓ Generated {len(specs_by_folder)} subfolder(s)")
|
|
787
|
+
|
|
788
|
+
|
|
789
|
+
def main():
|
|
790
|
+
"""Main entry point."""
|
|
791
|
+
parser = argparse.ArgumentParser(
|
|
792
|
+
description="Generate Python and TypeScript code from YAML agent specifications"
|
|
793
|
+
)
|
|
794
|
+
parser.add_argument(
|
|
795
|
+
"--specs-dir",
|
|
796
|
+
type=Path,
|
|
797
|
+
default=Path("specs/agents"),
|
|
798
|
+
help="Directory containing YAML agent specifications",
|
|
799
|
+
)
|
|
800
|
+
parser.add_argument(
|
|
801
|
+
"--python-output",
|
|
802
|
+
type=Path,
|
|
803
|
+
default=Path("agent_runtimes/config/agents.py"),
|
|
804
|
+
help="Output path for generated Python code (if using --subfolder-structure, this will be the parent directory)",
|
|
805
|
+
)
|
|
806
|
+
parser.add_argument(
|
|
807
|
+
"--typescript-output",
|
|
808
|
+
type=Path,
|
|
809
|
+
default=Path("src/config/agents.ts"),
|
|
810
|
+
help="Output path for generated TypeScript code (if using --subfolder-structure, this will be the parent directory)",
|
|
811
|
+
)
|
|
812
|
+
parser.add_argument(
|
|
813
|
+
"--subfolder-structure",
|
|
814
|
+
action="store_true",
|
|
815
|
+
help="Generate separate files per subfolder instead of one combined file",
|
|
816
|
+
)
|
|
817
|
+
|
|
818
|
+
args = parser.parse_args()
|
|
819
|
+
|
|
820
|
+
# Check specs directory exists
|
|
821
|
+
if not args.specs_dir.exists():
|
|
822
|
+
print(f"Error: Specs directory not found: {args.specs_dir}", file=sys.stderr)
|
|
823
|
+
sys.exit(1)
|
|
824
|
+
|
|
825
|
+
# Load YAML specifications
|
|
826
|
+
print(f"Loading agent specifications from {args.specs_dir}...")
|
|
827
|
+
specs = load_yaml_specs(args.specs_dir)
|
|
828
|
+
print(f"Loaded {len(specs)} agent specification(s)")
|
|
829
|
+
|
|
830
|
+
if args.subfolder_structure:
|
|
831
|
+
# Generate separate files per subfolder
|
|
832
|
+
generate_subfolder_structure(specs, args)
|
|
833
|
+
else:
|
|
834
|
+
# Generate Python code (single file)
|
|
835
|
+
print(f"Generating Python code to {args.python_output}...")
|
|
836
|
+
python_code = generate_python_code(specs)
|
|
837
|
+
args.python_output.parent.mkdir(parents=True, exist_ok=True)
|
|
838
|
+
with open(args.python_output, "w") as f:
|
|
839
|
+
f.write(python_code)
|
|
840
|
+
|
|
841
|
+
# Generate TypeScript code (single file)
|
|
842
|
+
print(f"Generating TypeScript code to {args.typescript_output}...")
|
|
843
|
+
# Get MCP and skills specs directories (siblings to agents directory)
|
|
844
|
+
mcp_specs_dir = args.specs_dir.parent / "mcp-servers"
|
|
845
|
+
skills_specs_dir = args.specs_dir.parent / "skills"
|
|
846
|
+
typescript_code = generate_typescript_code(
|
|
847
|
+
specs, str(mcp_specs_dir), str(skills_specs_dir)
|
|
848
|
+
)
|
|
849
|
+
args.typescript_output.parent.mkdir(parents=True, exist_ok=True)
|
|
850
|
+
with open(args.typescript_output, "w") as f:
|
|
851
|
+
f.write(typescript_code)
|
|
852
|
+
|
|
853
|
+
# Update __init__.py with new agent spec constants
|
|
854
|
+
init_file_path = args.python_output.parent / "__init__.py"
|
|
855
|
+
if init_file_path.exists():
|
|
856
|
+
print(f"Updating {init_file_path}...")
|
|
857
|
+
update_init_file(specs, init_file_path)
|
|
858
|
+
|
|
859
|
+
print("✅ Code generation complete!")
|
|
860
|
+
|
|
861
|
+
|
|
862
|
+
if __name__ == "__main__":
|
|
863
|
+
main()
|