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.
- dtSpark/__init__.py +0 -0
- dtSpark/_description.txt +1 -0
- dtSpark/_full_name.txt +1 -0
- dtSpark/_licence.txt +21 -0
- dtSpark/_metadata.yaml +6 -0
- dtSpark/_name.txt +1 -0
- dtSpark/_version.txt +1 -0
- dtSpark/aws/__init__.py +7 -0
- dtSpark/aws/authentication.py +296 -0
- dtSpark/aws/bedrock.py +578 -0
- dtSpark/aws/costs.py +318 -0
- dtSpark/aws/pricing.py +580 -0
- dtSpark/cli_interface.py +2645 -0
- dtSpark/conversation_manager.py +3050 -0
- dtSpark/core/__init__.py +12 -0
- dtSpark/core/application.py +3355 -0
- dtSpark/core/context_compaction.py +735 -0
- dtSpark/daemon/__init__.py +104 -0
- dtSpark/daemon/__main__.py +10 -0
- dtSpark/daemon/action_monitor.py +213 -0
- dtSpark/daemon/daemon_app.py +730 -0
- dtSpark/daemon/daemon_manager.py +289 -0
- dtSpark/daemon/execution_coordinator.py +194 -0
- dtSpark/daemon/pid_file.py +169 -0
- dtSpark/database/__init__.py +482 -0
- dtSpark/database/autonomous_actions.py +1191 -0
- dtSpark/database/backends.py +329 -0
- dtSpark/database/connection.py +122 -0
- dtSpark/database/conversations.py +520 -0
- dtSpark/database/credential_prompt.py +218 -0
- dtSpark/database/files.py +205 -0
- dtSpark/database/mcp_ops.py +355 -0
- dtSpark/database/messages.py +161 -0
- dtSpark/database/schema.py +673 -0
- dtSpark/database/tool_permissions.py +186 -0
- dtSpark/database/usage.py +167 -0
- dtSpark/files/__init__.py +4 -0
- dtSpark/files/manager.py +322 -0
- dtSpark/launch.py +39 -0
- dtSpark/limits/__init__.py +10 -0
- dtSpark/limits/costs.py +296 -0
- dtSpark/limits/tokens.py +342 -0
- dtSpark/llm/__init__.py +17 -0
- dtSpark/llm/anthropic_direct.py +446 -0
- dtSpark/llm/base.py +146 -0
- dtSpark/llm/context_limits.py +438 -0
- dtSpark/llm/manager.py +177 -0
- dtSpark/llm/ollama.py +578 -0
- dtSpark/mcp_integration/__init__.py +5 -0
- dtSpark/mcp_integration/manager.py +653 -0
- dtSpark/mcp_integration/tool_selector.py +225 -0
- dtSpark/resources/config.yaml.template +631 -0
- dtSpark/safety/__init__.py +22 -0
- dtSpark/safety/llm_service.py +111 -0
- dtSpark/safety/patterns.py +229 -0
- dtSpark/safety/prompt_inspector.py +442 -0
- dtSpark/safety/violation_logger.py +346 -0
- dtSpark/scheduler/__init__.py +20 -0
- dtSpark/scheduler/creation_tools.py +599 -0
- dtSpark/scheduler/execution_queue.py +159 -0
- dtSpark/scheduler/executor.py +1152 -0
- dtSpark/scheduler/manager.py +395 -0
- dtSpark/tools/__init__.py +4 -0
- dtSpark/tools/builtin.py +833 -0
- dtSpark/web/__init__.py +20 -0
- dtSpark/web/auth.py +152 -0
- dtSpark/web/dependencies.py +37 -0
- dtSpark/web/endpoints/__init__.py +17 -0
- dtSpark/web/endpoints/autonomous_actions.py +1125 -0
- dtSpark/web/endpoints/chat.py +621 -0
- dtSpark/web/endpoints/conversations.py +353 -0
- dtSpark/web/endpoints/main_menu.py +547 -0
- dtSpark/web/endpoints/streaming.py +421 -0
- dtSpark/web/server.py +578 -0
- dtSpark/web/session.py +167 -0
- dtSpark/web/ssl_utils.py +195 -0
- dtSpark/web/static/css/dark-theme.css +427 -0
- dtSpark/web/static/js/actions.js +1101 -0
- dtSpark/web/static/js/chat.js +614 -0
- dtSpark/web/static/js/main.js +496 -0
- dtSpark/web/static/js/sse-client.js +242 -0
- dtSpark/web/templates/actions.html +408 -0
- dtSpark/web/templates/base.html +93 -0
- dtSpark/web/templates/chat.html +814 -0
- dtSpark/web/templates/conversations.html +350 -0
- dtSpark/web/templates/goodbye.html +81 -0
- dtSpark/web/templates/login.html +90 -0
- dtSpark/web/templates/main_menu.html +983 -0
- dtSpark/web/templates/new_conversation.html +191 -0
- dtSpark/web/web_interface.py +137 -0
- dtspark-1.0.4.dist-info/METADATA +187 -0
- dtspark-1.0.4.dist-info/RECORD +96 -0
- dtspark-1.0.4.dist-info/WHEEL +5 -0
- dtspark-1.0.4.dist-info/entry_points.txt +3 -0
- dtspark-1.0.4.dist-info/licenses/LICENSE +21 -0
- 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 %}
|