agent-orcha 0.0.1

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 (175) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +917 -0
  3. package/bin/cli.js +2 -0
  4. package/dist/lib/agents/agent-executor.d.ts +13 -0
  5. package/dist/lib/agents/agent-executor.d.ts.map +1 -0
  6. package/dist/lib/agents/agent-executor.js +158 -0
  7. package/dist/lib/agents/agent-executor.js.map +1 -0
  8. package/dist/lib/agents/agent-loader.d.ts +13 -0
  9. package/dist/lib/agents/agent-loader.d.ts.map +1 -0
  10. package/dist/lib/agents/agent-loader.js +39 -0
  11. package/dist/lib/agents/agent-loader.js.map +1 -0
  12. package/dist/lib/agents/index.d.ts +5 -0
  13. package/dist/lib/agents/index.d.ts.map +1 -0
  14. package/dist/lib/agents/index.js +4 -0
  15. package/dist/lib/agents/index.js.map +1 -0
  16. package/dist/lib/agents/types.d.ts +140 -0
  17. package/dist/lib/agents/types.d.ts.map +1 -0
  18. package/dist/lib/agents/types.js +28 -0
  19. package/dist/lib/agents/types.js.map +1 -0
  20. package/dist/lib/functions/function-loader.d.ts +30 -0
  21. package/dist/lib/functions/function-loader.d.ts.map +1 -0
  22. package/dist/lib/functions/function-loader.js +107 -0
  23. package/dist/lib/functions/function-loader.js.map +1 -0
  24. package/dist/lib/functions/index.d.ts +3 -0
  25. package/dist/lib/functions/index.d.ts.map +1 -0
  26. package/dist/lib/functions/index.js +2 -0
  27. package/dist/lib/functions/index.js.map +1 -0
  28. package/dist/lib/functions/simple-function-wrapper.d.ts +33 -0
  29. package/dist/lib/functions/simple-function-wrapper.d.ts.map +1 -0
  30. package/dist/lib/functions/simple-function-wrapper.js +66 -0
  31. package/dist/lib/functions/simple-function-wrapper.js.map +1 -0
  32. package/dist/lib/index.d.ts +16 -0
  33. package/dist/lib/index.d.ts.map +1 -0
  34. package/dist/lib/index.js +17 -0
  35. package/dist/lib/index.js.map +1 -0
  36. package/dist/lib/llm/index.d.ts +6 -0
  37. package/dist/lib/llm/index.d.ts.map +1 -0
  38. package/dist/lib/llm/index.js +4 -0
  39. package/dist/lib/llm/index.js.map +1 -0
  40. package/dist/lib/llm/llm-config.d.ts +138 -0
  41. package/dist/lib/llm/llm-config.d.ts.map +1 -0
  42. package/dist/lib/llm/llm-config.js +75 -0
  43. package/dist/lib/llm/llm-config.js.map +1 -0
  44. package/dist/lib/llm/llm-factory.d.ts +30 -0
  45. package/dist/lib/llm/llm-factory.d.ts.map +1 -0
  46. package/dist/lib/llm/llm-factory.js +103 -0
  47. package/dist/lib/llm/llm-factory.js.map +1 -0
  48. package/dist/lib/llm/provider-detector.d.ts +12 -0
  49. package/dist/lib/llm/provider-detector.d.ts.map +1 -0
  50. package/dist/lib/llm/provider-detector.js +46 -0
  51. package/dist/lib/llm/provider-detector.js.map +1 -0
  52. package/dist/lib/llm/types.d.ts +19 -0
  53. package/dist/lib/llm/types.d.ts.map +1 -0
  54. package/dist/lib/llm/types.js +18 -0
  55. package/dist/lib/llm/types.js.map +1 -0
  56. package/dist/lib/logger.d.ts +43 -0
  57. package/dist/lib/logger.d.ts.map +1 -0
  58. package/dist/lib/logger.js +113 -0
  59. package/dist/lib/logger.js.map +1 -0
  60. package/dist/lib/mcp/index.d.ts +4 -0
  61. package/dist/lib/mcp/index.d.ts.map +1 -0
  62. package/dist/lib/mcp/index.js +3 -0
  63. package/dist/lib/mcp/index.js.map +1 -0
  64. package/dist/lib/mcp/mcp-client.d.ts +17 -0
  65. package/dist/lib/mcp/mcp-client.d.ts.map +1 -0
  66. package/dist/lib/mcp/mcp-client.js +166 -0
  67. package/dist/lib/mcp/mcp-client.js.map +1 -0
  68. package/dist/lib/mcp/types.d.ts +146 -0
  69. package/dist/lib/mcp/types.d.ts.map +1 -0
  70. package/dist/lib/mcp/types.js +24 -0
  71. package/dist/lib/mcp/types.js.map +1 -0
  72. package/dist/lib/orchestrator.d.ts +64 -0
  73. package/dist/lib/orchestrator.d.ts.map +1 -0
  74. package/dist/lib/orchestrator.js +201 -0
  75. package/dist/lib/orchestrator.js.map +1 -0
  76. package/dist/lib/tools/built-in/index.d.ts +2 -0
  77. package/dist/lib/tools/built-in/index.d.ts.map +1 -0
  78. package/dist/lib/tools/built-in/index.js +2 -0
  79. package/dist/lib/tools/built-in/index.js.map +1 -0
  80. package/dist/lib/tools/built-in/vector-search.tool.d.ts +4 -0
  81. package/dist/lib/tools/built-in/vector-search.tool.d.ts.map +1 -0
  82. package/dist/lib/tools/built-in/vector-search.tool.js +26 -0
  83. package/dist/lib/tools/built-in/vector-search.tool.js.map +1 -0
  84. package/dist/lib/tools/index.d.ts +3 -0
  85. package/dist/lib/tools/index.d.ts.map +1 -0
  86. package/dist/lib/tools/index.js +3 -0
  87. package/dist/lib/tools/index.js.map +1 -0
  88. package/dist/lib/tools/tool-registry.d.ts +20 -0
  89. package/dist/lib/tools/tool-registry.d.ts.map +1 -0
  90. package/dist/lib/tools/tool-registry.js +76 -0
  91. package/dist/lib/tools/tool-registry.js.map +1 -0
  92. package/dist/lib/vectors/index.d.ts +5 -0
  93. package/dist/lib/vectors/index.d.ts.map +1 -0
  94. package/dist/lib/vectors/index.js +4 -0
  95. package/dist/lib/vectors/index.js.map +1 -0
  96. package/dist/lib/vectors/types.d.ts +212 -0
  97. package/dist/lib/vectors/types.d.ts.map +1 -0
  98. package/dist/lib/vectors/types.js +39 -0
  99. package/dist/lib/vectors/types.js.map +1 -0
  100. package/dist/lib/vectors/vector-store-factory.d.ts +14 -0
  101. package/dist/lib/vectors/vector-store-factory.d.ts.map +1 -0
  102. package/dist/lib/vectors/vector-store-factory.js +350 -0
  103. package/dist/lib/vectors/vector-store-factory.js.map +1 -0
  104. package/dist/lib/vectors/vector-store-manager.d.ts +18 -0
  105. package/dist/lib/vectors/vector-store-manager.d.ts.map +1 -0
  106. package/dist/lib/vectors/vector-store-manager.js +79 -0
  107. package/dist/lib/vectors/vector-store-manager.js.map +1 -0
  108. package/dist/lib/workflows/index.d.ts +5 -0
  109. package/dist/lib/workflows/index.d.ts.map +1 -0
  110. package/dist/lib/workflows/index.js +4 -0
  111. package/dist/lib/workflows/index.js.map +1 -0
  112. package/dist/lib/workflows/types.d.ts +630 -0
  113. package/dist/lib/workflows/types.d.ts.map +1 -0
  114. package/dist/lib/workflows/types.js +51 -0
  115. package/dist/lib/workflows/types.js.map +1 -0
  116. package/dist/lib/workflows/workflow-executor.d.ts +22 -0
  117. package/dist/lib/workflows/workflow-executor.d.ts.map +1 -0
  118. package/dist/lib/workflows/workflow-executor.js +276 -0
  119. package/dist/lib/workflows/workflow-executor.js.map +1 -0
  120. package/dist/lib/workflows/workflow-loader.d.ts +13 -0
  121. package/dist/lib/workflows/workflow-loader.d.ts.map +1 -0
  122. package/dist/lib/workflows/workflow-loader.js +39 -0
  123. package/dist/lib/workflows/workflow-loader.js.map +1 -0
  124. package/dist/public/index.html +762 -0
  125. package/dist/src/cli/commands/init.d.ts +2 -0
  126. package/dist/src/cli/commands/init.d.ts.map +1 -0
  127. package/dist/src/cli/commands/init.js +108 -0
  128. package/dist/src/cli/commands/init.js.map +1 -0
  129. package/dist/src/cli/commands/start.d.ts +3 -0
  130. package/dist/src/cli/commands/start.d.ts.map +1 -0
  131. package/dist/src/cli/commands/start.js +114 -0
  132. package/dist/src/cli/commands/start.js.map +1 -0
  133. package/dist/src/cli/index.d.ts +3 -0
  134. package/dist/src/cli/index.d.ts.map +1 -0
  135. package/dist/src/cli/index.js +59 -0
  136. package/dist/src/cli/index.js.map +1 -0
  137. package/dist/src/index.d.ts +2 -0
  138. package/dist/src/index.d.ts.map +1 -0
  139. package/dist/src/index.js +62 -0
  140. package/dist/src/index.js.map +1 -0
  141. package/dist/src/routes/agents.route.d.ts +3 -0
  142. package/dist/src/routes/agents.route.d.ts.map +1 -0
  143. package/dist/src/routes/agents.route.js +58 -0
  144. package/dist/src/routes/agents.route.js.map +1 -0
  145. package/dist/src/routes/llm.route.d.ts +3 -0
  146. package/dist/src/routes/llm.route.d.ts.map +1 -0
  147. package/dist/src/routes/llm.route.js +97 -0
  148. package/dist/src/routes/llm.route.js.map +1 -0
  149. package/dist/src/routes/vectors.route.d.ts +3 -0
  150. package/dist/src/routes/vectors.route.d.ts.map +1 -0
  151. package/dist/src/routes/vectors.route.js +74 -0
  152. package/dist/src/routes/vectors.route.js.map +1 -0
  153. package/dist/src/routes/workflows.route.d.ts +3 -0
  154. package/dist/src/routes/workflows.route.d.ts.map +1 -0
  155. package/dist/src/routes/workflows.route.js +58 -0
  156. package/dist/src/routes/workflows.route.js.map +1 -0
  157. package/dist/src/server.d.ts +9 -0
  158. package/dist/src/server.d.ts.map +1 -0
  159. package/dist/src/server.js +34 -0
  160. package/dist/src/server.js.map +1 -0
  161. package/dist/templates/.env.example +6 -0
  162. package/dist/templates/README.md +234 -0
  163. package/dist/templates/agents/example.agent.yaml +32 -0
  164. package/dist/templates/agents/knowledge.agent.yaml +36 -0
  165. package/dist/templates/agents/math.agent.yaml +38 -0
  166. package/dist/templates/agents/time.agent.yaml +42 -0
  167. package/dist/templates/functions/README.md +195 -0
  168. package/dist/templates/functions/fibonacci.function.js +55 -0
  169. package/dist/templates/llm.json +44 -0
  170. package/dist/templates/mcp.json +18 -0
  171. package/dist/templates/vectors/example-chroma.vector.yaml +43 -0
  172. package/dist/templates/vectors/example.vector.yaml +28 -0
  173. package/dist/templates/vectors/sample-data/example-document.txt +15 -0
  174. package/dist/templates/workflows/example.workflow.yaml +79 -0
  175. package/package.json +77 -0
@@ -0,0 +1,762 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en" class="dark">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Agent Orcha</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script>
9
+ tailwind.config = {
10
+ darkMode: 'class',
11
+ theme: {
12
+ extend: {
13
+ colors: {
14
+ dark: {
15
+ bg: '#0f172a',
16
+ surface: '#1e293b',
17
+ border: '#334155',
18
+ hover: '#475569',
19
+ }
20
+ }
21
+ }
22
+ }
23
+ }
24
+ </script>
25
+ </head>
26
+ <body class="bg-dark-bg text-gray-100 min-h-screen">
27
+ <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
28
+ <div class="mb-8">
29
+ <h1 class="text-4xl font-bold bg-gradient-to-r from-blue-400 to-purple-500 bg-clip-text text-transparent">
30
+ Agent Orcha
31
+ </h1>
32
+ <p class="text-gray-400 mt-2">Orchestrating AI Agents with Power</p>
33
+ </div>
34
+
35
+ <!-- Tabs -->
36
+ <div class="border-b border-dark-border mb-6">
37
+ <nav class="flex space-x-8">
38
+ <button class="tab-btn border-b-2 border-blue-500 text-blue-400 pb-3 px-1 font-medium" data-tab="agents">
39
+ Agents
40
+ </button>
41
+ <button class="tab-btn border-b-2 border-transparent text-gray-400 hover:text-gray-300 pb-3 px-1 font-medium" data-tab="workflows">
42
+ Workflows
43
+ </button>
44
+ <button class="tab-btn border-b-2 border-transparent text-gray-400 hover:text-gray-300 pb-3 px-1 font-medium" data-tab="llm">
45
+ LLMs
46
+ </button>
47
+ </nav>
48
+ </div>
49
+
50
+ <!-- Agents Tab -->
51
+ <div id="agentsTab" class="tab-content">
52
+ <div class="space-y-6">
53
+ <div>
54
+ <label class="block text-sm font-medium text-gray-300 mb-2">Select Agent</label>
55
+ <select id="agentSelect" class="w-full bg-dark-surface border border-dark-border rounded-lg px-4 py-2.5 text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent">
56
+ <option value="">-- Select Agent --</option>
57
+ </select>
58
+ </div>
59
+
60
+ <div>
61
+ <label class="block text-sm font-medium text-gray-300 mb-2">Input</label>
62
+ <textarea id="agentInput" rows="6" class="w-full bg-dark-surface border border-dark-border rounded-lg px-4 py-3 text-gray-100 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent resize-none" placeholder="Enter your message or input here..."></textarea>
63
+ </div>
64
+
65
+ <button id="runAgent" disabled class="bg-blue-600 hover:bg-blue-700 disabled:bg-gray-700 disabled:cursor-not-allowed text-white font-medium px-6 py-2.5 rounded-lg transition-colors">
66
+ Run Agent
67
+ </button>
68
+
69
+ <div>
70
+ <label class="block text-sm font-medium text-gray-300 mb-2">Output</label>
71
+ <div id="agentOutput" class="bg-dark-surface border border-dark-border rounded-lg p-4 min-h-[200px] font-mono text-sm text-gray-300 whitespace-pre-wrap overflow-x-auto">
72
+ Select an agent to get started.
73
+ </div>
74
+ </div>
75
+ </div>
76
+ </div>
77
+
78
+ <!-- Workflows Tab -->
79
+ <div id="workflowsTab" class="tab-content hidden">
80
+ <div class="space-y-6">
81
+ <div>
82
+ <label class="block text-sm font-medium text-gray-300 mb-2">Select Workflow</label>
83
+ <select id="workflowSelect" class="w-full bg-dark-surface border border-dark-border rounded-lg px-4 py-2.5 text-gray-100 focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent">
84
+ <option value="">-- Select Workflow --</option>
85
+ </select>
86
+ </div>
87
+
88
+ <!-- Flow Diagram -->
89
+ <div id="flowDiagram" class="bg-dark-surface border border-dark-border rounded-lg p-6 overflow-x-auto">
90
+ <div class="text-gray-500 italic text-center py-8">Select a workflow to see its flow</div>
91
+ </div>
92
+
93
+ <!-- Workflow Inputs -->
94
+ <div id="workflowInputs" class="space-y-3"></div>
95
+
96
+ <!-- Workflow Status -->
97
+ <div id="workflowStatus" class="bg-dark-surface border border-dark-border rounded-lg p-5 hidden">
98
+ <div class="flex justify-between items-center mb-3">
99
+ <div id="statusMessage" class="font-medium text-gray-200">Starting workflow...</div>
100
+ <div id="statusTime" class="text-sm text-gray-400">0s</div>
101
+ </div>
102
+ <div class="w-full bg-dark-bg rounded-full h-2 mb-4">
103
+ <div id="progressFill" class="bg-blue-600 h-2 rounded-full transition-all duration-300" style="width: 0%"></div>
104
+ </div>
105
+ <div id="statusLog" class="bg-dark-bg rounded-lg p-3 max-h-48 overflow-y-auto text-xs font-mono space-y-1"></div>
106
+ </div>
107
+
108
+ <button id="runWorkflow" disabled class="bg-purple-600 hover:bg-purple-700 disabled:bg-gray-700 disabled:cursor-not-allowed text-white font-medium px-6 py-2.5 rounded-lg transition-colors">
109
+ Run Workflow
110
+ </button>
111
+
112
+ <div>
113
+ <label class="block text-sm font-medium text-gray-300 mb-2">Output</label>
114
+ <div id="workflowOutput" class="bg-dark-surface border border-dark-border rounded-lg p-4 min-h-[200px] font-mono text-sm text-gray-300 whitespace-pre-wrap overflow-x-auto">
115
+ Select a workflow to get started.
116
+ </div>
117
+ </div>
118
+ </div>
119
+ </div>
120
+
121
+ <!-- LLM Tab -->
122
+ <div id="llmTab" class="tab-content hidden">
123
+ <div class="space-y-6">
124
+ <div>
125
+ <label class="block text-sm font-medium text-gray-300 mb-2">Select LLM</label>
126
+ <select id="llmSelect" class="w-full bg-dark-surface border border-dark-border rounded-lg px-4 py-2.5 text-gray-100 focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-transparent">
127
+ <option value="">-- Select LLM --</option>
128
+ </select>
129
+ </div>
130
+
131
+ <!-- LLM Info -->
132
+ <div id="llmInfo" class="bg-dark-surface/50 border border-dark-border rounded-lg p-4 hidden">
133
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4 text-sm">
134
+ <div>
135
+ <span class="text-gray-400">Model:</span>
136
+ <span id="llmModel" class="ml-2 text-gray-200 font-medium"></span>
137
+ </div>
138
+ <div>
139
+ <span class="text-gray-400">Base URL:</span>
140
+ <span id="llmBaseUrl" class="ml-2 text-gray-200 font-medium"></span>
141
+ </div>
142
+ </div>
143
+ </div>
144
+
145
+ <div>
146
+ <label class="block text-sm font-medium text-gray-300 mb-2">Message</label>
147
+ <textarea id="llmInput" rows="6" class="w-full bg-dark-surface border border-dark-border rounded-lg px-4 py-3 text-gray-100 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-transparent resize-none" placeholder="Enter your message to the LLM..."></textarea>
148
+ </div>
149
+
150
+ <div class="flex space-x-3">
151
+ <button id="runLlm" disabled class="bg-green-600 hover:bg-green-700 disabled:bg-gray-700 disabled:cursor-not-allowed text-white font-medium px-6 py-2.5 rounded-lg transition-colors">
152
+ Send Message
153
+ </button>
154
+ <button id="streamLlm" disabled class="bg-emerald-600 hover:bg-emerald-700 disabled:bg-gray-700 disabled:cursor-not-allowed text-white font-medium px-6 py-2.5 rounded-lg transition-colors">
155
+ Stream Response
156
+ </button>
157
+ </div>
158
+
159
+ <div>
160
+ <label class="block text-sm font-medium text-gray-300 mb-2">Output</label>
161
+ <div id="llmOutput" class="bg-dark-surface border border-dark-border rounded-lg p-4 min-h-[200px] font-mono text-sm text-gray-300 whitespace-pre-wrap overflow-x-auto">
162
+ </div>
163
+ </div>
164
+ </div>
165
+ </div>
166
+ </div>
167
+
168
+ <script>
169
+ const agentSelect = document.getElementById('agentSelect');
170
+ const workflowSelect = document.getElementById('workflowSelect');
171
+ const llmSelect = document.getElementById('llmSelect');
172
+ const agentInputEl = document.getElementById('agentInput');
173
+ const agentOutputEl = document.getElementById('agentOutput');
174
+ const workflowOutputEl = document.getElementById('workflowOutput');
175
+ const llmInputEl = document.getElementById('llmInput');
176
+ const llmOutputEl = document.getElementById('llmOutput');
177
+ const llmInfoEl = document.getElementById('llmInfo');
178
+ const llmModelEl = document.getElementById('llmModel');
179
+ const llmBaseUrlEl = document.getElementById('llmBaseUrl');
180
+ const runAgentBtn = document.getElementById('runAgent');
181
+ const runWorkflowBtn = document.getElementById('runWorkflow');
182
+ const runLlmBtn = document.getElementById('runLlm');
183
+ const streamLlmBtn = document.getElementById('streamLlm');
184
+ const workflowInputsEl = document.getElementById('workflowInputs');
185
+ const flowDiagramEl = document.getElementById('flowDiagram');
186
+ const workflowStatusEl = document.getElementById('workflowStatus');
187
+ const statusMessageEl = document.getElementById('statusMessage');
188
+ const statusTimeEl = document.getElementById('statusTime');
189
+ const progressFillEl = document.getElementById('progressFill');
190
+ const statusLogEl = document.getElementById('statusLog');
191
+ const tabBtns = document.querySelectorAll('.tab-btn');
192
+ const tabContents = document.querySelectorAll('.tab-content');
193
+
194
+ let agents = [];
195
+ let workflows = [];
196
+ let llmConfigs = [];
197
+ let currentWorkflowSchema = null;
198
+ let currentWorkflowSteps = [];
199
+ let stepElements = new Map();
200
+ let statusStartTime = null;
201
+ let statusInterval = null;
202
+
203
+ // Tab switching
204
+ tabBtns.forEach(btn => {
205
+ btn.addEventListener('click', () => {
206
+ const tabName = btn.dataset.tab;
207
+ tabBtns.forEach(b => {
208
+ b.classList.remove('border-blue-500', 'border-purple-500', 'border-green-500', 'text-blue-400', 'text-purple-400', 'text-green-400');
209
+ b.classList.add('border-transparent', 'text-gray-400');
210
+ });
211
+ tabContents.forEach(tc => tc.classList.add('hidden'));
212
+
213
+ const colors = { agents: 'blue', workflows: 'purple', llm: 'green' };
214
+ const color = colors[tabName] || 'blue';
215
+ btn.classList.remove('border-transparent', 'text-gray-400');
216
+ btn.classList.add(`border-${color}-500`, `text-${color}-400`);
217
+
218
+ document.getElementById(`${tabName}Tab`).classList.remove('hidden');
219
+ });
220
+ });
221
+
222
+ async function loadAgents() {
223
+ try {
224
+ const res = await fetch('/api/agents');
225
+ agents = await res.json();
226
+ agents.forEach(agent => {
227
+ const opt = document.createElement('option');
228
+ opt.value = agent.name;
229
+ opt.textContent = `${agent.name} - ${agent.description}`;
230
+ agentSelect.appendChild(opt);
231
+ });
232
+ } catch (e) {
233
+ console.error('Failed to load agents:', e);
234
+ }
235
+ }
236
+
237
+ async function loadWorkflows() {
238
+ try {
239
+ const res = await fetch('/api/workflows');
240
+ workflows = await res.json();
241
+ workflows.forEach(wf => {
242
+ const opt = document.createElement('option');
243
+ opt.value = wf.name;
244
+ opt.textContent = `${wf.name} - ${wf.description}`;
245
+ workflowSelect.appendChild(opt);
246
+ });
247
+ } catch (e) {
248
+ console.error('Failed to load workflows:', e);
249
+ }
250
+ }
251
+
252
+ async function loadLLMs() {
253
+ try {
254
+ const res = await fetch('/api/llm');
255
+ llmConfigs = await res.json();
256
+ llmConfigs.forEach(llm => {
257
+ const opt = document.createElement('option');
258
+ opt.value = llm.name;
259
+ opt.textContent = `${llm.name} (${llm.model})`;
260
+ llmSelect.appendChild(opt);
261
+ });
262
+ } catch (e) {
263
+ console.error('Failed to load LLMs:', e);
264
+ }
265
+ }
266
+
267
+ agentSelect.addEventListener('change', () => {
268
+ runAgentBtn.disabled = !agentSelect.value;
269
+ });
270
+
271
+ workflowSelect.addEventListener('change', async () => {
272
+ runWorkflowBtn.disabled = !workflowSelect.value;
273
+ resetWorkflowStatus();
274
+ if (workflowSelect.value) {
275
+ await loadWorkflowDetails(workflowSelect.value);
276
+ } else {
277
+ workflowInputsEl.innerHTML = '';
278
+ currentWorkflowSchema = null;
279
+ currentWorkflowSteps = [];
280
+ flowDiagramEl.innerHTML = '<div class="text-gray-500 italic text-center py-8">Select a workflow to see its flow</div>';
281
+ }
282
+ });
283
+
284
+ llmSelect.addEventListener('change', () => {
285
+ const hasValue = !!llmSelect.value;
286
+ runLlmBtn.disabled = !hasValue;
287
+ streamLlmBtn.disabled = !hasValue;
288
+
289
+ if (hasValue) {
290
+ const llm = llmConfigs.find(l => l.name === llmSelect.value);
291
+ if (llm) {
292
+ llmInfoEl.classList.remove('hidden');
293
+ llmModelEl.textContent = llm.model;
294
+ llmBaseUrlEl.textContent = llm.baseUrl || '(OpenAI default)';
295
+ }
296
+ } else {
297
+ llmInfoEl.classList.add('hidden');
298
+ }
299
+ });
300
+
301
+ async function loadWorkflowDetails(name) {
302
+ try {
303
+ const res = await fetch(`/api/workflows/${name}`);
304
+ const workflow = await res.json();
305
+ currentWorkflowSchema = workflow.input?.schema || {};
306
+ currentWorkflowSteps = workflow.steps || [];
307
+ renderWorkflowInputs(currentWorkflowSchema);
308
+ renderFlowDiagram(currentWorkflowSteps);
309
+ } catch (e) {
310
+ console.error('Failed to load workflow details:', e);
311
+ }
312
+ }
313
+
314
+ function parseToolRef(toolRef) {
315
+ if (typeof toolRef === 'string') {
316
+ const [source, name] = toolRef.split(':');
317
+ return { source, name };
318
+ }
319
+ return { source: toolRef.source, name: toolRef.name };
320
+ }
321
+
322
+ function getAgentTools(agentName) {
323
+ const agent = agents.find(a => a.name === agentName);
324
+ if (!agent || !agent.tools) return { mcp: [], vector: [] };
325
+
326
+ const mcp = [];
327
+ const vector = [];
328
+
329
+ agent.tools.forEach(tool => {
330
+ const { source, name } = parseToolRef(tool);
331
+ if (source === 'mcp') mcp.push(name);
332
+ else if (source === 'vector') vector.push(name);
333
+ });
334
+
335
+ return { mcp, vector };
336
+ }
337
+
338
+ function renderFlowDiagram(steps) {
339
+ flowDiagramEl.innerHTML = '';
340
+ stepElements.clear();
341
+
342
+ if (!steps || steps.length === 0) {
343
+ flowDiagramEl.innerHTML = '<div class="text-gray-500 italic text-center py-8">No steps defined</div>';
344
+ return;
345
+ }
346
+
347
+ const container = document.createElement('div');
348
+ container.className = 'flex items-center gap-3 flex-nowrap';
349
+
350
+ // Input node
351
+ const inputNode = document.createElement('div');
352
+ inputNode.className = 'flex flex-col items-center px-5 py-3 bg-green-500/10 border border-green-500/30 rounded-lg min-w-[100px] text-center';
353
+ inputNode.innerHTML = '<span class="font-semibold text-green-400 text-sm">Input</span>';
354
+ container.appendChild(inputNode);
355
+
356
+ steps.forEach((step) => {
357
+ const arrow = document.createElement('span');
358
+ arrow.className = 'text-gray-600 text-xl flex-shrink-0';
359
+ arrow.textContent = '→';
360
+ container.appendChild(arrow);
361
+
362
+ if (step.parallel && step.parallel.length > 0) {
363
+ const parallelContainer = document.createElement('div');
364
+ parallelContainer.className = 'flex flex-col gap-2 p-3 bg-dark-bg/50 border border-dashed border-dark-border rounded-lg';
365
+ step.parallel.forEach(pStep => {
366
+ const stepEl = createStepElement(pStep);
367
+ stepElements.set(pStep.id, stepEl);
368
+ parallelContainer.appendChild(stepEl);
369
+ });
370
+ container.appendChild(parallelContainer);
371
+ } else {
372
+ const stepEl = createStepElement(step);
373
+ stepElements.set(step.id, stepEl);
374
+ container.appendChild(stepEl);
375
+ }
376
+ });
377
+
378
+ const finalArrow = document.createElement('span');
379
+ finalArrow.className = 'text-gray-600 text-xl flex-shrink-0';
380
+ finalArrow.textContent = '→';
381
+ container.appendChild(finalArrow);
382
+
383
+ // Output node
384
+ const outputNode = document.createElement('div');
385
+ outputNode.className = 'flex flex-col items-center px-5 py-3 bg-green-500/10 border border-green-500/30 rounded-lg min-w-[100px] text-center';
386
+ outputNode.innerHTML = '<span class="font-semibold text-green-400 text-sm">Output</span>';
387
+ container.appendChild(outputNode);
388
+
389
+ flowDiagramEl.appendChild(container);
390
+ }
391
+
392
+ function createStepElement(step) {
393
+ const stepEl = document.createElement('div');
394
+ stepEl.className = 'flex flex-col items-center px-4 py-3 bg-blue-500/10 border border-blue-500/30 rounded-lg min-w-[120px] text-center transition-all';
395
+
396
+ const tools = getAgentTools(step.agent);
397
+ let toolBadges = '';
398
+
399
+ if (tools.mcp.length > 0) {
400
+ tools.mcp.forEach(name => {
401
+ toolBadges += `<span class="px-2 py-0.5 text-[10px] font-semibold uppercase bg-blue-500/20 text-blue-300 border border-blue-500/30 rounded">MCP:${name}</span>`;
402
+ });
403
+ }
404
+ if (tools.vector.length > 0) {
405
+ tools.vector.forEach(name => {
406
+ toolBadges += `<span class="px-2 py-0.5 text-[10px] font-semibold uppercase bg-purple-500/20 text-purple-300 border border-purple-500/30 rounded">Vec:${name}</span>`;
407
+ });
408
+ }
409
+
410
+ stepEl.innerHTML = `
411
+ <span class="font-semibold text-blue-300 text-sm">${step.id || 'step'}</span>
412
+ <span class="text-xs text-gray-400 mt-0.5">${step.agent || ''}</span>
413
+ ${toolBadges ? `<div class="flex flex-wrap gap-1 mt-2 justify-center">${toolBadges}</div>` : ''}
414
+ `;
415
+ return stepEl;
416
+ }
417
+
418
+ function renderWorkflowInputs(schema) {
419
+ workflowInputsEl.innerHTML = '';
420
+ const entries = Object.entries(schema);
421
+ if (entries.length === 0) return;
422
+
423
+ const label = document.createElement('label');
424
+ label.className = 'block text-sm font-medium text-gray-300 mb-2';
425
+ label.textContent = 'Workflow Inputs';
426
+ workflowInputsEl.appendChild(label);
427
+
428
+ const inputsContainer = document.createElement('div');
429
+ inputsContainer.className = 'space-y-2';
430
+
431
+ for (const [key, field] of entries) {
432
+ const input = document.createElement('input');
433
+ input.type = 'text';
434
+ input.id = `wf-${key}`;
435
+ input.placeholder = `${key}${field.required ? ' *' : ''}${field.default ? ` (default: ${field.default})` : ''}`;
436
+ input.className = 'w-full bg-dark-surface border border-dark-border rounded-lg px-4 py-2.5 text-gray-100 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent';
437
+ if (field.default) input.value = field.default;
438
+ inputsContainer.appendChild(input);
439
+ }
440
+
441
+ workflowInputsEl.appendChild(inputsContainer);
442
+ }
443
+
444
+ runAgentBtn.addEventListener('click', async () => {
445
+ const agentName = agentSelect.value;
446
+ const message = agentInputEl.value.trim();
447
+ if (!agentName) return;
448
+
449
+ const agent = agents.find(a => a.name === agentName);
450
+ const inputVars = agent?.inputVariables || ['content'];
451
+ const inputObj = {};
452
+
453
+ if (inputVars.length === 1) {
454
+ inputObj[inputVars[0]] = message;
455
+ } else {
456
+ inputObj[inputVars[0] || 'content'] = message;
457
+ inputVars.slice(1).forEach(v => inputObj[v] = '');
458
+ }
459
+
460
+ agentOutputEl.className = 'bg-dark-surface border border-dark-border rounded-lg p-4 min-h-[200px] font-mono text-sm text-gray-400 whitespace-pre-wrap overflow-x-auto';
461
+ agentOutputEl.textContent = 'Running agent...';
462
+ runAgentBtn.disabled = true;
463
+
464
+ try {
465
+ const res = await fetch(`/api/agents/${agentName}/invoke`, {
466
+ method: 'POST',
467
+ headers: { 'Content-Type': 'application/json' },
468
+ body: JSON.stringify({ input: inputObj })
469
+ });
470
+ const result = await res.json();
471
+ agentOutputEl.className = 'bg-dark-surface border border-dark-border rounded-lg p-4 min-h-[200px] font-mono text-sm text-gray-300 whitespace-pre-wrap overflow-x-auto';
472
+ agentOutputEl.textContent = typeof result.output === 'string'
473
+ ? result.output
474
+ : JSON.stringify(result, null, 2);
475
+ } catch (e) {
476
+ agentOutputEl.className = 'bg-dark-surface border border-red-900/30 rounded-lg p-4 min-h-[200px] font-mono text-sm text-red-400 whitespace-pre-wrap overflow-x-auto';
477
+ agentOutputEl.textContent = 'Error: ' + e.message;
478
+ } finally {
479
+ runAgentBtn.disabled = false;
480
+ }
481
+ });
482
+
483
+ function updateStepStatus(stepId, status) {
484
+ const stepEl = stepElements.get(stepId);
485
+ if (stepEl) {
486
+ stepEl.className = 'flex flex-col items-center px-4 py-3 rounded-lg min-w-[120px] text-center transition-all';
487
+ if (status === 'running') {
488
+ stepEl.className += ' bg-yellow-500/20 border border-yellow-500/40 animate-pulse';
489
+ } else if (status === 'complete') {
490
+ stepEl.className += ' bg-green-500/20 border border-green-500/40';
491
+ } else if (status === 'error') {
492
+ stepEl.className += ' bg-red-500/20 border border-red-500/40';
493
+ } else {
494
+ stepEl.className += ' bg-blue-500/10 border border-blue-500/30';
495
+ }
496
+ }
497
+ }
498
+
499
+ function addStatusLog(message, type = '') {
500
+ const entry = document.createElement('div');
501
+ entry.className = 'text-gray-400';
502
+ if (type === 'step-start') entry.className = 'text-blue-400';
503
+ if (type === 'step-complete') entry.className = 'text-green-400';
504
+ if (type === 'step-error') entry.className = 'text-red-400';
505
+ entry.textContent = `[${new Date().toLocaleTimeString()}] ${message}`;
506
+ statusLogEl.appendChild(entry);
507
+ statusLogEl.scrollTop = statusLogEl.scrollHeight;
508
+ }
509
+
510
+ function updateProgress(current, total, elapsed) {
511
+ const percentage = total > 0 ? Math.round((current / total) * 100) : 0;
512
+ progressFillEl.style.width = `${percentage}%`;
513
+
514
+ const seconds = Math.floor(elapsed / 1000);
515
+ const minutes = Math.floor(seconds / 60);
516
+ const displayTime = minutes > 0
517
+ ? `${minutes}m ${seconds % 60}s`
518
+ : `${seconds}s`;
519
+ statusTimeEl.textContent = displayTime;
520
+ }
521
+
522
+ function resetWorkflowStatus() {
523
+ workflowStatusEl.classList.add('hidden');
524
+ statusLogEl.innerHTML = '';
525
+ progressFillEl.style.width = '0%';
526
+ statusTimeEl.textContent = '0s';
527
+ stepElements.forEach((el) => {
528
+ el.className = 'flex flex-col items-center px-4 py-3 bg-blue-500/10 border border-blue-500/30 rounded-lg min-w-[120px] text-center transition-all';
529
+ });
530
+ if (statusInterval) {
531
+ clearInterval(statusInterval);
532
+ statusInterval = null;
533
+ }
534
+ }
535
+
536
+ runWorkflowBtn.addEventListener('click', async () => {
537
+ const workflowName = workflowSelect.value;
538
+ if (!workflowName) return;
539
+
540
+ const inputObj = {};
541
+ if (currentWorkflowSchema) {
542
+ for (const key of Object.keys(currentWorkflowSchema)) {
543
+ const el = document.getElementById(`wf-${key}`);
544
+ if (el && el.value) inputObj[key] = el.value;
545
+ }
546
+ }
547
+
548
+ resetWorkflowStatus();
549
+ workflowStatusEl.classList.remove('hidden');
550
+ workflowOutputEl.className = 'bg-dark-surface border border-dark-border rounded-lg p-4 min-h-[200px] font-mono text-sm text-gray-400 whitespace-pre-wrap overflow-x-auto';
551
+ workflowOutputEl.textContent = 'Starting workflow...';
552
+ runWorkflowBtn.disabled = true;
553
+ statusStartTime = Date.now();
554
+
555
+ statusInterval = setInterval(() => {
556
+ if (statusStartTime) {
557
+ const elapsed = Date.now() - statusStartTime;
558
+ const seconds = Math.floor(elapsed / 1000);
559
+ const minutes = Math.floor(seconds / 60);
560
+ const displayTime = minutes > 0
561
+ ? `${minutes}m ${seconds % 60}s`
562
+ : `${seconds}s`;
563
+ statusTimeEl.textContent = displayTime;
564
+ }
565
+ }, 1000);
566
+
567
+ try {
568
+ const response = await fetch(`/api/workflows/${workflowName}/stream`, {
569
+ method: 'POST',
570
+ headers: { 'Content-Type': 'application/json' },
571
+ body: JSON.stringify({ input: inputObj })
572
+ });
573
+
574
+ if (!response.ok) {
575
+ throw new Error(`HTTP error! status: ${response.status}`);
576
+ }
577
+
578
+ const reader = response.body.getReader();
579
+ const decoder = new TextDecoder();
580
+ let buffer = '';
581
+
582
+ while (true) {
583
+ const { done, value } = await reader.read();
584
+ if (done) break;
585
+
586
+ buffer += decoder.decode(value, { stream: true });
587
+ const lines = buffer.split('\n');
588
+ buffer = lines.pop() || '';
589
+
590
+ for (const line of lines) {
591
+ if (line.startsWith('data: ')) {
592
+ const data = line.slice(6);
593
+ if (data === '[DONE]') continue;
594
+
595
+ try {
596
+ const update = JSON.parse(data);
597
+
598
+ if (update.type === 'status') {
599
+ const status = update.data;
600
+
601
+ statusMessageEl.textContent = status.message;
602
+
603
+ if (status.progress) {
604
+ updateProgress(status.progress.current, status.progress.total, status.elapsed || 0);
605
+ }
606
+
607
+ if (status.type === 'step_start' && status.stepId) {
608
+ updateStepStatus(status.stepId, 'running');
609
+ addStatusLog(status.message, 'step-start');
610
+ } else if (status.type === 'step_complete' && status.stepId) {
611
+ updateStepStatus(status.stepId, 'complete');
612
+ addStatusLog(status.message, 'step-complete');
613
+ } else if (status.type === 'step_error' && status.stepId) {
614
+ updateStepStatus(status.stepId, 'error');
615
+ addStatusLog(status.message, 'step-error');
616
+ } else if (status.type === 'workflow_start') {
617
+ addStatusLog(status.message);
618
+ } else if (status.type === 'workflow_complete' || status.type === 'workflow_error') {
619
+ addStatusLog(status.message);
620
+ if (statusInterval) {
621
+ clearInterval(statusInterval);
622
+ statusInterval = null;
623
+ }
624
+ }
625
+ } else if (update.type === 'result') {
626
+ const result = update.data;
627
+ workflowOutputEl.className = 'bg-dark-surface border border-dark-border rounded-lg p-4 min-h-[200px] font-mono text-sm text-gray-300 whitespace-pre-wrap overflow-x-auto';
628
+
629
+ let output = '';
630
+ if (result.output) {
631
+ for (const [key, value] of Object.entries(result.output)) {
632
+ output += `=== ${key} ===\n${value}\n\n`;
633
+ }
634
+ }
635
+ output += `Duration: ${result.metadata?.duration}ms`;
636
+ workflowOutputEl.textContent = output || JSON.stringify(result, null, 2);
637
+
638
+ if (statusInterval) {
639
+ clearInterval(statusInterval);
640
+ statusInterval = null;
641
+ }
642
+ }
643
+ } catch (e) {
644
+ console.error('Error parsing SSE data:', e, data);
645
+ }
646
+ }
647
+ }
648
+ }
649
+ } catch (e) {
650
+ workflowOutputEl.className = 'bg-dark-surface border border-red-900/30 rounded-lg p-4 min-h-[200px] font-mono text-sm text-red-400 whitespace-pre-wrap overflow-x-auto';
651
+ workflowOutputEl.textContent = 'Error: ' + e.message;
652
+ addStatusLog(`Error: ${e.message}`, 'step-error');
653
+ if (statusInterval) {
654
+ clearInterval(statusInterval);
655
+ statusInterval = null;
656
+ }
657
+ } finally {
658
+ runWorkflowBtn.disabled = false;
659
+ }
660
+ });
661
+
662
+ runLlmBtn.addEventListener('click', async () => {
663
+ const llmName = llmSelect.value;
664
+ const message = llmInputEl.value.trim();
665
+ if (!llmName || !message) return;
666
+
667
+ llmOutputEl.className = 'bg-dark-surface border border-dark-border rounded-lg p-4 min-h-[200px] font-mono text-sm text-gray-400 whitespace-pre-wrap overflow-x-auto';
668
+ llmOutputEl.textContent = 'Sending message...';
669
+ runLlmBtn.disabled = true;
670
+ streamLlmBtn.disabled = true;
671
+
672
+ try {
673
+ const res = await fetch(`/api/llm/${llmName}/chat`, {
674
+ method: 'POST',
675
+ headers: { 'Content-Type': 'application/json' },
676
+ body: JSON.stringify({ message })
677
+ });
678
+ const result = await res.json();
679
+
680
+ if (result.error) {
681
+ throw new Error(result.error);
682
+ }
683
+
684
+ llmOutputEl.className = 'bg-dark-surface border border-dark-border rounded-lg p-4 min-h-[200px] font-mono text-sm text-gray-300 whitespace-pre-wrap overflow-x-auto';
685
+ llmOutputEl.textContent = result.output;
686
+ } catch (e) {
687
+ llmOutputEl.className = 'bg-dark-surface border border-red-900/30 rounded-lg p-4 min-h-[200px] font-mono text-sm text-red-400 whitespace-pre-wrap overflow-x-auto';
688
+ llmOutputEl.textContent = 'Error: ' + e.message;
689
+ } finally {
690
+ runLlmBtn.disabled = false;
691
+ streamLlmBtn.disabled = false;
692
+ }
693
+ });
694
+
695
+ streamLlmBtn.addEventListener('click', async () => {
696
+ const llmName = llmSelect.value;
697
+ const message = llmInputEl.value.trim();
698
+ if (!llmName || !message) return;
699
+
700
+ llmOutputEl.className = 'bg-dark-surface border border-dark-border rounded-lg p-4 min-h-[200px] font-mono text-sm text-gray-300 whitespace-pre-wrap overflow-x-auto';
701
+ llmOutputEl.textContent = '';
702
+ runLlmBtn.disabled = true;
703
+ streamLlmBtn.disabled = true;
704
+
705
+ try {
706
+ const response = await fetch(`/api/llm/${llmName}/stream`, {
707
+ method: 'POST',
708
+ headers: { 'Content-Type': 'application/json' },
709
+ body: JSON.stringify({ message })
710
+ });
711
+
712
+ if (!response.ok) {
713
+ throw new Error(`HTTP error! status: ${response.status}`);
714
+ }
715
+
716
+ const reader = response.body.getReader();
717
+ const decoder = new TextDecoder();
718
+ let buffer = '';
719
+
720
+ while (true) {
721
+ const { done, value } = await reader.read();
722
+ if (done) break;
723
+
724
+ buffer += decoder.decode(value, { stream: true });
725
+ const lines = buffer.split('\n');
726
+ buffer = lines.pop() || '';
727
+
728
+ for (const line of lines) {
729
+ if (line.startsWith('data: ')) {
730
+ const data = line.slice(6);
731
+ if (data === '[DONE]') continue;
732
+
733
+ try {
734
+ const update = JSON.parse(data);
735
+ if (update.content) {
736
+ llmOutputEl.textContent += update.content;
737
+ } else if (update.error) {
738
+ throw new Error(update.error);
739
+ }
740
+ } catch (e) {
741
+ if (e.message !== 'Unexpected end of JSON input') {
742
+ console.error('Error parsing SSE data:', e, data);
743
+ }
744
+ }
745
+ }
746
+ }
747
+ }
748
+ } catch (e) {
749
+ llmOutputEl.className = 'bg-dark-surface border border-red-900/30 rounded-lg p-4 min-h-[200px] font-mono text-sm text-red-400 whitespace-pre-wrap overflow-x-auto';
750
+ llmOutputEl.textContent = 'Error: ' + e.message;
751
+ } finally {
752
+ runLlmBtn.disabled = false;
753
+ streamLlmBtn.disabled = false;
754
+ }
755
+ });
756
+
757
+ loadAgents();
758
+ loadWorkflows();
759
+ loadLLMs();
760
+ </script>
761
+ </body>
762
+ </html>