dtSpark 1.0.4__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 (96) hide show
  1. dtSpark/__init__.py +0 -0
  2. dtSpark/_description.txt +1 -0
  3. dtSpark/_full_name.txt +1 -0
  4. dtSpark/_licence.txt +21 -0
  5. dtSpark/_metadata.yaml +6 -0
  6. dtSpark/_name.txt +1 -0
  7. dtSpark/_version.txt +1 -0
  8. dtSpark/aws/__init__.py +7 -0
  9. dtSpark/aws/authentication.py +296 -0
  10. dtSpark/aws/bedrock.py +578 -0
  11. dtSpark/aws/costs.py +318 -0
  12. dtSpark/aws/pricing.py +580 -0
  13. dtSpark/cli_interface.py +2645 -0
  14. dtSpark/conversation_manager.py +3050 -0
  15. dtSpark/core/__init__.py +12 -0
  16. dtSpark/core/application.py +3355 -0
  17. dtSpark/core/context_compaction.py +735 -0
  18. dtSpark/daemon/__init__.py +104 -0
  19. dtSpark/daemon/__main__.py +10 -0
  20. dtSpark/daemon/action_monitor.py +213 -0
  21. dtSpark/daemon/daemon_app.py +730 -0
  22. dtSpark/daemon/daemon_manager.py +289 -0
  23. dtSpark/daemon/execution_coordinator.py +194 -0
  24. dtSpark/daemon/pid_file.py +169 -0
  25. dtSpark/database/__init__.py +482 -0
  26. dtSpark/database/autonomous_actions.py +1191 -0
  27. dtSpark/database/backends.py +329 -0
  28. dtSpark/database/connection.py +122 -0
  29. dtSpark/database/conversations.py +520 -0
  30. dtSpark/database/credential_prompt.py +218 -0
  31. dtSpark/database/files.py +205 -0
  32. dtSpark/database/mcp_ops.py +355 -0
  33. dtSpark/database/messages.py +161 -0
  34. dtSpark/database/schema.py +673 -0
  35. dtSpark/database/tool_permissions.py +186 -0
  36. dtSpark/database/usage.py +167 -0
  37. dtSpark/files/__init__.py +4 -0
  38. dtSpark/files/manager.py +322 -0
  39. dtSpark/launch.py +39 -0
  40. dtSpark/limits/__init__.py +10 -0
  41. dtSpark/limits/costs.py +296 -0
  42. dtSpark/limits/tokens.py +342 -0
  43. dtSpark/llm/__init__.py +17 -0
  44. dtSpark/llm/anthropic_direct.py +446 -0
  45. dtSpark/llm/base.py +146 -0
  46. dtSpark/llm/context_limits.py +438 -0
  47. dtSpark/llm/manager.py +177 -0
  48. dtSpark/llm/ollama.py +578 -0
  49. dtSpark/mcp_integration/__init__.py +5 -0
  50. dtSpark/mcp_integration/manager.py +653 -0
  51. dtSpark/mcp_integration/tool_selector.py +225 -0
  52. dtSpark/resources/config.yaml.template +631 -0
  53. dtSpark/safety/__init__.py +22 -0
  54. dtSpark/safety/llm_service.py +111 -0
  55. dtSpark/safety/patterns.py +229 -0
  56. dtSpark/safety/prompt_inspector.py +442 -0
  57. dtSpark/safety/violation_logger.py +346 -0
  58. dtSpark/scheduler/__init__.py +20 -0
  59. dtSpark/scheduler/creation_tools.py +599 -0
  60. dtSpark/scheduler/execution_queue.py +159 -0
  61. dtSpark/scheduler/executor.py +1152 -0
  62. dtSpark/scheduler/manager.py +395 -0
  63. dtSpark/tools/__init__.py +4 -0
  64. dtSpark/tools/builtin.py +833 -0
  65. dtSpark/web/__init__.py +20 -0
  66. dtSpark/web/auth.py +152 -0
  67. dtSpark/web/dependencies.py +37 -0
  68. dtSpark/web/endpoints/__init__.py +17 -0
  69. dtSpark/web/endpoints/autonomous_actions.py +1125 -0
  70. dtSpark/web/endpoints/chat.py +621 -0
  71. dtSpark/web/endpoints/conversations.py +353 -0
  72. dtSpark/web/endpoints/main_menu.py +547 -0
  73. dtSpark/web/endpoints/streaming.py +421 -0
  74. dtSpark/web/server.py +578 -0
  75. dtSpark/web/session.py +167 -0
  76. dtSpark/web/ssl_utils.py +195 -0
  77. dtSpark/web/static/css/dark-theme.css +427 -0
  78. dtSpark/web/static/js/actions.js +1101 -0
  79. dtSpark/web/static/js/chat.js +614 -0
  80. dtSpark/web/static/js/main.js +496 -0
  81. dtSpark/web/static/js/sse-client.js +242 -0
  82. dtSpark/web/templates/actions.html +408 -0
  83. dtSpark/web/templates/base.html +93 -0
  84. dtSpark/web/templates/chat.html +814 -0
  85. dtSpark/web/templates/conversations.html +350 -0
  86. dtSpark/web/templates/goodbye.html +81 -0
  87. dtSpark/web/templates/login.html +90 -0
  88. dtSpark/web/templates/main_menu.html +983 -0
  89. dtSpark/web/templates/new_conversation.html +191 -0
  90. dtSpark/web/web_interface.py +137 -0
  91. dtspark-1.0.4.dist-info/METADATA +187 -0
  92. dtspark-1.0.4.dist-info/RECORD +96 -0
  93. dtspark-1.0.4.dist-info/WHEEL +5 -0
  94. dtspark-1.0.4.dist-info/entry_points.txt +3 -0
  95. dtspark-1.0.4.dist-info/licenses/LICENSE +21 -0
  96. dtspark-1.0.4.dist-info/top_level.txt +1 -0
@@ -0,0 +1,242 @@
1
+ /**
2
+ * Server-Sent Events (SSE) client for Spark web interface
3
+ *
4
+ * Handles real-time streaming of chat responses via SSE
5
+ */
6
+
7
+ /**
8
+ * Send a message and handle streaming response via SSE
9
+ * @param {number} conversationId - The conversation ID
10
+ * @param {string} message - The message to send
11
+ */
12
+ async function sendMessageWithSSE(conversationId, message) {
13
+ // Show typing indicator
14
+ let typingIndicator = showTypingIndicator();
15
+
16
+ // Track streaming message element
17
+ let streamingMessageElement = null;
18
+ let accumulatedContent = '';
19
+
20
+ try {
21
+ // Create EventSource for SSE
22
+ const encodedMessage = encodeURIComponent(message);
23
+ const eventSource = new EventSource(
24
+ `/api/stream/chat?message=${encodedMessage}&conversation_id=${conversationId}`
25
+ );
26
+
27
+ // Handle different event types
28
+ eventSource.addEventListener('status', (event) => {
29
+ const data = JSON.parse(event.data);
30
+ console.log('Status:', data);
31
+
32
+ // Update typing indicator with status
33
+ if (typingIndicator && typingIndicator.parentNode) {
34
+ const statusText = data.message || '';
35
+ typingIndicator.querySelector('.visually-hidden').nextSibling.textContent = statusText;
36
+ }
37
+ });
38
+
39
+ eventSource.addEventListener('response', (event) => {
40
+ const data = JSON.parse(event.data);
41
+ console.log('Received response event:', data);
42
+
43
+ // Hide typing indicator on first response
44
+ if (typingIndicator && typingIndicator.parentNode) {
45
+ console.log('Hiding typing indicator');
46
+ hideTypingIndicator();
47
+ typingIndicator = null; // Clear reference after hiding
48
+ }
49
+
50
+ if (data.type === 'text') {
51
+ if (data.final) {
52
+ // Final response - this is the main assistant response after tool execution
53
+ console.log('Final response received, content length:', data.content ? data.content.length : 0);
54
+ accumulatedContent += data.content;
55
+ console.log('Accumulated content:', accumulatedContent);
56
+
57
+ // Ensure we have content before displaying
58
+ if (accumulatedContent.trim().length > 0) {
59
+ streamingMessageElement = updateStreamingMessage(
60
+ accumulatedContent,
61
+ streamingMessageElement
62
+ );
63
+ console.log('Updated streaming message element:', streamingMessageElement);
64
+ } else {
65
+ console.warn('No content to display');
66
+ }
67
+ eventSource.close();
68
+ } else {
69
+ // Non-final response - text that appears with tool calls
70
+ // Display immediately as a separate message
71
+ console.log('Non-final response, appending:', data.content);
72
+ appendMessage('assistant', data.content);
73
+ }
74
+ }
75
+ });
76
+
77
+ eventSource.addEventListener('tool_start', (event) => {
78
+ const data = JSON.parse(event.data);
79
+ appendToolCall(data.tool_name, data.input);
80
+ });
81
+
82
+ eventSource.addEventListener('tool_complete', (event) => {
83
+ const data = JSON.parse(event.data);
84
+ // Display tool result
85
+ appendToolResult(data.tool_use_id, { content: data.content });
86
+ });
87
+
88
+ eventSource.addEventListener('tool_error', (event) => {
89
+ const data = JSON.parse(event.data);
90
+ appendToolResult(data.tool_name, { error: data.error });
91
+ });
92
+
93
+ eventSource.addEventListener('permission_request', (event) => {
94
+ const data = JSON.parse(event.data);
95
+ console.log('Permission request received:', data);
96
+
97
+ // Show permission modal/dialog
98
+ showToolPermissionDialog(data.request_id, data.tool_name, data.tool_description);
99
+ });
100
+
101
+ eventSource.addEventListener('progress', (event) => {
102
+ const data = JSON.parse(event.data);
103
+ // Update progress (if we want to show a progress bar)
104
+ console.log('Progress:', data);
105
+ });
106
+
107
+ eventSource.addEventListener('complete', (event) => {
108
+ // Stream complete
109
+ console.log('Stream complete');
110
+ eventSource.close();
111
+
112
+ // Hide typing indicator if still visible
113
+ if (typingIndicator && typingIndicator.parentNode) {
114
+ console.log('Hiding typing indicator on complete');
115
+ hideTypingIndicator();
116
+ typingIndicator = null;
117
+ }
118
+ });
119
+
120
+ eventSource.addEventListener('error', (event) => {
121
+ console.error('SSE error:', event);
122
+ const data = JSON.parse(event.data);
123
+
124
+ // Hide typing indicator
125
+ if (typingIndicator && typingIndicator.parentNode) {
126
+ console.log('Hiding typing indicator on error');
127
+ hideTypingIndicator();
128
+ typingIndicator = null;
129
+ }
130
+
131
+ // Show error message
132
+ appendMessage('system', `Error: ${data.message}`);
133
+
134
+ eventSource.close();
135
+ });
136
+
137
+ eventSource.onerror = (error) => {
138
+ console.error('EventSource failed:', error);
139
+
140
+ // Hide typing indicator
141
+ if (typingIndicator && typingIndicator.parentNode) {
142
+ console.log('Hiding typing indicator on connection error');
143
+ hideTypingIndicator();
144
+ typingIndicator = null;
145
+ }
146
+
147
+ // If we haven't received any content, show error
148
+ if (!streamingMessageElement) {
149
+ appendMessage('system', 'Connection error. Please try again.');
150
+ }
151
+
152
+ eventSource.close();
153
+ };
154
+
155
+ } catch (error) {
156
+ console.error('Error sending message:', error);
157
+
158
+ // Hide typing indicator
159
+ if (typingIndicator) {
160
+ hideTypingIndicator();
161
+ }
162
+
163
+ showToast('Failed to send message', 'error');
164
+ }
165
+ }
166
+
167
+ /**
168
+ * Alternative: Send message using traditional HTTP POST (fallback)
169
+ * @param {number} conversationId - The conversation ID
170
+ * @param {string} message - The message to send
171
+ */
172
+ async function sendMessageHTTP(conversationId, message) {
173
+ try {
174
+ // Show typing indicator
175
+ const typingIndicator = showTypingIndicator();
176
+
177
+ // Create form data
178
+ const formData = new FormData();
179
+ formData.append('message', message);
180
+
181
+ // Send request
182
+ const response = await fetch(`/api/chat/${conversationId}/message`, {
183
+ method: 'POST',
184
+ body: formData
185
+ });
186
+
187
+ if (!response.ok) {
188
+ throw new Error('Failed to send message');
189
+ }
190
+
191
+ const data = await response.json();
192
+
193
+ // Hide typing indicator
194
+ hideTypingIndicator();
195
+
196
+ // Append response
197
+ if (data.status === 'success' && data.response) {
198
+ appendMessage('assistant', data.response);
199
+ } else {
200
+ appendMessage('system', 'No response from assistant');
201
+ }
202
+
203
+ } catch (error) {
204
+ console.error('Error sending message:', error);
205
+ hideTypingIndicator();
206
+ showToast('Failed to send message', 'error');
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Stream progress for a long-running task
212
+ * @param {string} taskName - The task name
213
+ * @param {number} totalSteps - Total number of steps
214
+ * @param {Function} onProgress - Callback for progress updates
215
+ */
216
+ async function streamProgress(taskName, totalSteps, onProgress) {
217
+ try {
218
+ const eventSource = new EventSource(
219
+ `/api/stream/progress?task_name=${encodeURIComponent(taskName)}&total_steps=${totalSteps}`
220
+ );
221
+
222
+ eventSource.addEventListener('progress', (event) => {
223
+ const data = JSON.parse(event.data);
224
+
225
+ if (onProgress) {
226
+ onProgress(data);
227
+ }
228
+
229
+ if (data.step >= data.total) {
230
+ eventSource.close();
231
+ }
232
+ });
233
+
234
+ eventSource.onerror = (error) => {
235
+ console.error('Progress stream error:', error);
236
+ eventSource.close();
237
+ };
238
+
239
+ } catch (error) {
240
+ console.error('Error streaming progress:', error);
241
+ }
242
+ }
@@ -0,0 +1,408 @@
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}Autonomous Actions - {{ app_name }}{% endblock %}
4
+
5
+ {% block content %}
6
+ <div class="container-fluid">
7
+ <div class="row mb-4">
8
+ <div class="col">
9
+ <h2><i class="bi bi-robot"></i> Autonomous Actions</h2>
10
+ <p class="text-muted">Schedule and manage AI-driven automated tasks</p>
11
+ </div>
12
+ <div class="col-auto">
13
+ <button class="btn btn-outline-primary me-2" onclick="showAICreateModal()">
14
+ <i class="bi bi-robot"></i> Create with AI
15
+ </button>
16
+ <button class="btn btn-primary" onclick="showCreateModal()">
17
+ <i class="bi bi-plus-circle"></i> Create Action
18
+ </button>
19
+ </div>
20
+ </div>
21
+
22
+ <!-- Failed Actions Warning -->
23
+ <div id="failed-warning" class="alert alert-warning d-none" role="alert">
24
+ <i class="bi bi-exclamation-triangle-fill"></i>
25
+ <span id="failed-count-text"></span>
26
+ </div>
27
+
28
+ <!-- Actions Table -->
29
+ <div class="card">
30
+ <div class="card-header d-flex justify-content-between align-items-center">
31
+ <span><i class="bi bi-list-task"></i> Actions</span>
32
+ <div class="form-check form-switch">
33
+ <input class="form-check-input" type="checkbox" id="showDisabled" checked onchange="loadActions()">
34
+ <label class="form-check-label" for="showDisabled">Show disabled</label>
35
+ </div>
36
+ </div>
37
+ <div class="card-body p-0">
38
+ <div class="table-responsive">
39
+ <table class="table table-hover mb-0" id="actions-table">
40
+ <thead>
41
+ <tr>
42
+ <th>ID</th>
43
+ <th>Name</th>
44
+ <th>Model</th>
45
+ <th>Schedule</th>
46
+ <th>Context</th>
47
+ <th>Status</th>
48
+ <th>Last Run</th>
49
+ <th>Failures</th>
50
+ <th>Actions</th>
51
+ </tr>
52
+ </thead>
53
+ <tbody id="actions-tbody">
54
+ <tr>
55
+ <td colspan="9" class="text-center text-muted py-4">
56
+ <div class="spinner-border spinner-border-sm" role="status"></div>
57
+ Loading actions...
58
+ </td>
59
+ </tr>
60
+ </tbody>
61
+ </table>
62
+ </div>
63
+ </div>
64
+ </div>
65
+
66
+ <!-- Recent Runs -->
67
+ <div class="card mt-4">
68
+ <div class="card-header">
69
+ <i class="bi bi-clock-history"></i> Recent Runs
70
+ </div>
71
+ <div class="card-body p-0">
72
+ <div class="table-responsive">
73
+ <table class="table table-hover mb-0" id="runs-table">
74
+ <thead>
75
+ <tr>
76
+ <th>Run ID</th>
77
+ <th>Action</th>
78
+ <th>Started</th>
79
+ <th>Status</th>
80
+ <th>Duration</th>
81
+ <th>Tokens</th>
82
+ <th>Actions</th>
83
+ </tr>
84
+ </thead>
85
+ <tbody id="runs-tbody">
86
+ <tr>
87
+ <td colspan="7" class="text-center text-muted py-4">
88
+ <div class="spinner-border spinner-border-sm" role="status"></div>
89
+ Loading recent runs...
90
+ </td>
91
+ </tr>
92
+ </tbody>
93
+ </table>
94
+ </div>
95
+ </div>
96
+ </div>
97
+ </div>
98
+
99
+ <!-- Create/Edit Action Modal -->
100
+ <div class="modal fade" id="actionModal" tabindex="-1" data-bs-backdrop="static">
101
+ <div class="modal-dialog modal-lg">
102
+ <div class="modal-content">
103
+ <div class="modal-header">
104
+ <h5 class="modal-title" id="actionModalTitle">Create Action</h5>
105
+ <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
106
+ </div>
107
+ <div class="modal-body">
108
+ <form id="actionForm">
109
+ <input type="hidden" id="actionId">
110
+
111
+ <div class="row mb-3">
112
+ <div class="col-md-6">
113
+ <label for="actionName" class="form-label">Name *</label>
114
+ <input type="text" class="form-control" id="actionName" required maxlength="100">
115
+ </div>
116
+ <div class="col-md-6">
117
+ <label for="actionModel" class="form-label">Model *</label>
118
+ <select class="form-select" id="actionModel" required>
119
+ <option value="">Select a model...</option>
120
+ </select>
121
+ </div>
122
+ </div>
123
+
124
+ <div class="mb-3">
125
+ <label for="actionDescription" class="form-label">Description *</label>
126
+ <input type="text" class="form-control" id="actionDescription" required maxlength="500">
127
+ </div>
128
+
129
+ <div class="mb-3">
130
+ <label for="actionPrompt" class="form-label">Action Prompt *</label>
131
+ <textarea class="form-control" id="actionPrompt" rows="5" required
132
+ placeholder="Describe the task for the AI to perform..."></textarea>
133
+ </div>
134
+
135
+ <div class="row mb-3">
136
+ <div class="col-md-4">
137
+ <label for="scheduleType" class="form-label">Schedule Type *</label>
138
+ <select class="form-select" id="scheduleType" required onchange="updateScheduleConfig()">
139
+ <option value="one_off">One-off</option>
140
+ <option value="recurring">Recurring</option>
141
+ </select>
142
+ </div>
143
+ <div class="col-md-4">
144
+ <label for="contextMode" class="form-label">Context Mode</label>
145
+ <select class="form-select" id="contextMode">
146
+ <option value="fresh">Fresh (new context each run)</option>
147
+ <option value="cumulative">Cumulative (retains context)</option>
148
+ </select>
149
+ </div>
150
+ <div class="col-md-4">
151
+ <label for="maxFailures" class="form-label">Max Failures</label>
152
+ <input type="number" class="form-control" id="maxFailures" value="3" min="1" max="100">
153
+ </div>
154
+ </div>
155
+
156
+ <!-- One-off schedule config -->
157
+ <div id="oneOffConfig" class="mb-3">
158
+ <label for="runDate" class="form-label">Run Date/Time *</label>
159
+ <input type="datetime-local" class="form-control" id="runDate">
160
+ </div>
161
+
162
+ <!-- Recurring schedule config -->
163
+ <div id="recurringConfig" class="mb-3 d-none">
164
+ <label for="cronExpression" class="form-label">Cron Expression *</label>
165
+ <input type="text" class="form-control" id="cronExpression" placeholder="0 9 * * *">
166
+ <div class="form-text">
167
+ Examples: <code>0 9 * * *</code> (daily at 9am),
168
+ <code>0 9 * * MON</code> (Mondays at 9am),
169
+ <code>0 */6 * * *</code> (every 6 hours)
170
+ </div>
171
+ </div>
172
+
173
+ <!-- Tool Permissions -->
174
+ <div class="mb-3">
175
+ <label class="form-label">Tool Permissions</label>
176
+ <div class="card">
177
+ <div class="card-body" style="max-height: 200px; overflow-y: auto;">
178
+ <div id="toolPermissions">
179
+ <p class="text-muted mb-0">Loading available tools...</p>
180
+ </div>
181
+ </div>
182
+ </div>
183
+ <div class="form-text">Select which tools this action can use</div>
184
+ </div>
185
+ </form>
186
+ </div>
187
+ <div class="modal-footer">
188
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
189
+ <button type="button" class="btn btn-primary" onclick="saveAction()">
190
+ <i class="bi bi-save"></i> Save Action
191
+ </button>
192
+ </div>
193
+ </div>
194
+ </div>
195
+ </div>
196
+
197
+ <!-- View Action Details Modal -->
198
+ <div class="modal fade" id="viewActionModal" tabindex="-1">
199
+ <div class="modal-dialog modal-lg">
200
+ <div class="modal-content">
201
+ <div class="modal-header">
202
+ <h5 class="modal-title">Action Details</h5>
203
+ <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
204
+ </div>
205
+ <div class="modal-body" id="viewActionBody">
206
+ <!-- Populated by JavaScript -->
207
+ </div>
208
+ <div class="modal-footer">
209
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
210
+ </div>
211
+ </div>
212
+ </div>
213
+ </div>
214
+
215
+ <!-- Action Runs Modal -->
216
+ <div class="modal fade" id="actionRunsModal" tabindex="-1">
217
+ <div class="modal-dialog modal-xl">
218
+ <div class="modal-content">
219
+ <div class="modal-header">
220
+ <h5 class="modal-title" id="actionRunsTitle">Action Runs</h5>
221
+ <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
222
+ </div>
223
+ <div class="modal-body">
224
+ <div class="table-responsive">
225
+ <table class="table table-hover">
226
+ <thead>
227
+ <tr>
228
+ <th>Run ID</th>
229
+ <th>Started</th>
230
+ <th>Completed</th>
231
+ <th>Status</th>
232
+ <th>Tokens</th>
233
+ <th>Actions</th>
234
+ </tr>
235
+ </thead>
236
+ <tbody id="actionRunsTbody">
237
+ </tbody>
238
+ </table>
239
+ </div>
240
+ </div>
241
+ <div class="modal-footer">
242
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
243
+ </div>
244
+ </div>
245
+ </div>
246
+ </div>
247
+
248
+ <!-- Run Details Modal -->
249
+ <div class="modal fade" id="runDetailsModal" tabindex="-1">
250
+ <div class="modal-dialog modal-xl">
251
+ <div class="modal-content">
252
+ <div class="modal-header">
253
+ <h5 class="modal-title">Run Details</h5>
254
+ <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
255
+ </div>
256
+ <div class="modal-body" id="runDetailsBody">
257
+ <!-- Populated by JavaScript -->
258
+ </div>
259
+ <div class="modal-footer">
260
+ <div class="me-auto">
261
+ <button type="button" class="btn btn-outline-secondary" onclick="exportRun('text')">
262
+ <i class="bi bi-file-text"></i> Export Text
263
+ </button>
264
+ <button type="button" class="btn btn-outline-secondary" onclick="exportRun('html')">
265
+ <i class="bi bi-filetype-html"></i> Export HTML
266
+ </button>
267
+ <button type="button" class="btn btn-outline-secondary" onclick="exportRun('markdown')">
268
+ <i class="bi bi-markdown"></i> Export Markdown
269
+ </button>
270
+ </div>
271
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
272
+ </div>
273
+ </div>
274
+ </div>
275
+ </div>
276
+
277
+ <!-- Confirm Delete Modal -->
278
+ <div class="modal fade" id="confirmDeleteModal" tabindex="-1">
279
+ <div class="modal-dialog">
280
+ <div class="modal-content">
281
+ <div class="modal-header bg-danger text-white">
282
+ <h5 class="modal-title"><i class="bi bi-exclamation-triangle"></i> Confirm Delete</h5>
283
+ <button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
284
+ </div>
285
+ <div class="modal-body">
286
+ <p>Are you sure you want to delete this action?</p>
287
+ <p class="text-danger"><strong id="deleteActionName"></strong></p>
288
+ <p class="text-muted small">This will also delete all run history for this action.</p>
289
+ </div>
290
+ <div class="modal-footer">
291
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
292
+ <button type="button" class="btn btn-danger" id="confirmDeleteBtn">
293
+ <i class="bi bi-trash"></i> Delete
294
+ </button>
295
+ </div>
296
+ </div>
297
+ </div>
298
+ </div>
299
+
300
+ <!-- AI-Assisted Action Creation Modal -->
301
+ <div class="modal fade" id="aiCreateModal" tabindex="-1" data-bs-backdrop="static">
302
+ <div class="modal-dialog modal-lg modal-dialog-scrollable">
303
+ <div class="modal-content">
304
+ <div class="modal-header">
305
+ <h5 class="modal-title">
306
+ <i class="bi bi-robot"></i> Create Action with AI
307
+ </h5>
308
+ <button type="button" class="btn-close" onclick="cancelAICreation()"></button>
309
+ </div>
310
+ <div class="modal-body">
311
+ <!-- Initial Setup Form -->
312
+ <div id="aiCreateSetup">
313
+ <p class="text-muted mb-4">
314
+ Use AI to help you create an autonomous action through conversation.
315
+ The AI will ask questions to understand what you need and configure the action for you.
316
+ </p>
317
+
318
+ <div class="mb-3">
319
+ <label for="aiActionName" class="form-label">Action Name *</label>
320
+ <input type="text" class="form-control" id="aiActionName" required maxlength="100"
321
+ placeholder="e.g., Daily Report Generator">
322
+ </div>
323
+
324
+ <div class="mb-3">
325
+ <label for="aiActionDescription" class="form-label">Description *</label>
326
+ <input type="text" class="form-control" id="aiActionDescription" required maxlength="500"
327
+ placeholder="Brief description of what this action should do">
328
+ </div>
329
+
330
+ <div class="mb-3">
331
+ <label for="aiActionModel" class="form-label">Model *</label>
332
+ <select class="form-select" id="aiActionModel" required>
333
+ <option value="">Select a model...</option>
334
+ </select>
335
+ <div class="form-text">This model will be used for both creation and execution of the action.</div>
336
+ </div>
337
+
338
+ <div class="d-grid">
339
+ <button type="button" class="btn btn-primary btn-lg" onclick="startAICreation()">
340
+ <i class="bi bi-chat-dots"></i> Start AI-Assisted Creation
341
+ </button>
342
+ </div>
343
+ </div>
344
+
345
+ <!-- Chat Interface (hidden initially) -->
346
+ <div id="aiCreateChat" class="d-none">
347
+ <div class="d-flex justify-content-between align-items-center mb-3">
348
+ <div>
349
+ <strong id="aiChatActionName"></strong>
350
+ <br><small class="text-muted" id="aiChatModelName"></small>
351
+ </div>
352
+ <span class="badge bg-primary" id="aiChatStatus">In Progress</span>
353
+ </div>
354
+
355
+ <!-- Chat Messages -->
356
+ <div id="aiChatMessages" class="border rounded p-3 mb-3" style="height: 350px; overflow-y: auto; background-color: var(--bs-gray-100);">
357
+ <!-- Messages will be added here dynamically -->
358
+ </div>
359
+
360
+ <!-- Chat Input -->
361
+ <div id="aiChatInputArea">
362
+ <div class="input-group">
363
+ <input type="text" class="form-control" id="aiChatInput"
364
+ placeholder="Type your response..."
365
+ onkeypress="handleAIChatKeypress(event)">
366
+ <button class="btn btn-primary" type="button" onclick="sendAICreationMessage()">
367
+ <i class="bi bi-send"></i> Send
368
+ </button>
369
+ </div>
370
+ </div>
371
+
372
+ <!-- Completion Message (hidden initially) -->
373
+ <div id="aiChatComplete" class="d-none">
374
+ <div class="alert alert-success mb-0">
375
+ <i class="bi bi-check-circle-fill"></i>
376
+ Action created successfully!
377
+ <a href="#" id="viewCreatedAction" onclick="viewCreatedAction(); return false;">View action details</a>
378
+ </div>
379
+ </div>
380
+ </div>
381
+
382
+ <!-- Loading Indicator -->
383
+ <div id="aiCreateLoading" class="d-none text-center py-4">
384
+ <div class="spinner-border text-primary" role="status"></div>
385
+ <p class="mt-2 text-muted">AI is thinking...</p>
386
+ </div>
387
+ </div>
388
+ <div class="modal-footer" id="aiCreateFooter">
389
+ <button type="button" class="btn btn-secondary" onclick="cancelAICreation()">Cancel</button>
390
+ </div>
391
+ </div>
392
+ </div>
393
+ </div>
394
+ {% endblock %}
395
+
396
+ {% block extra_scripts %}
397
+ <script src="/static/js/actions.js"></script>
398
+ <script>
399
+ // Initialise page
400
+ document.addEventListener('DOMContentLoaded', function() {
401
+ loadActions();
402
+ loadRecentRuns();
403
+ loadModels();
404
+ loadTools();
405
+ checkFailedActions();
406
+ });
407
+ </script>
408
+ {% endblock %}