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,191 @@
|
|
|
1
|
+
{% extends "base.html" %}
|
|
2
|
+
|
|
3
|
+
{% block title %}New Conversation - {{ app_name }}{% endblock %}
|
|
4
|
+
|
|
5
|
+
{% block content %}
|
|
6
|
+
<div class="row">
|
|
7
|
+
<div class="col-12">
|
|
8
|
+
<h2 class="mb-4">
|
|
9
|
+
<i class="bi bi-plus-circle-fill"></i> New Conversation
|
|
10
|
+
</h2>
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<div class="row">
|
|
15
|
+
<div class="col-lg-8 offset-lg-2">
|
|
16
|
+
<div class="card">
|
|
17
|
+
<div class="card-body">
|
|
18
|
+
<form id="new-conversation-form">
|
|
19
|
+
<div class="mb-3">
|
|
20
|
+
<label for="conversation-name" class="form-label">Conversation Name</label>
|
|
21
|
+
<input
|
|
22
|
+
type="text"
|
|
23
|
+
class="form-control"
|
|
24
|
+
id="conversation-name"
|
|
25
|
+
name="name"
|
|
26
|
+
placeholder="Enter a name for this conversation"
|
|
27
|
+
required
|
|
28
|
+
/>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<div class="mb-3">
|
|
32
|
+
<label for="model-select" class="form-label">Model</label>
|
|
33
|
+
<select class="form-select" id="model-select" name="model_id" required>
|
|
34
|
+
<option value="">Loading models...</option>
|
|
35
|
+
</select>
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
<div class="mb-3">
|
|
39
|
+
<label for="instructions" class="form-label">Instructions (Optional)</label>
|
|
40
|
+
<textarea
|
|
41
|
+
class="form-control"
|
|
42
|
+
id="instructions"
|
|
43
|
+
name="instructions"
|
|
44
|
+
rows="4"
|
|
45
|
+
placeholder="Enter system instructions for this conversation (optional)"
|
|
46
|
+
></textarea>
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
<div class="mb-3">
|
|
50
|
+
<label for="files" class="form-label">Attach Files (Optional)</label>
|
|
51
|
+
<input
|
|
52
|
+
type="file"
|
|
53
|
+
class="form-control"
|
|
54
|
+
id="files"
|
|
55
|
+
name="files"
|
|
56
|
+
multiple
|
|
57
|
+
/>
|
|
58
|
+
<div class="form-text">
|
|
59
|
+
Supported formats: txt, md, json, yaml, yml, xml, csv, log, py, js, ts, java, cpp, c, h
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<div class="d-grid gap-2">
|
|
64
|
+
<button type="submit" class="btn btn-primary btn-lg">
|
|
65
|
+
<i class="bi bi-plus-circle-fill"></i> Create Conversation
|
|
66
|
+
</button>
|
|
67
|
+
<a href="/menu" class="btn btn-secondary">
|
|
68
|
+
<i class="bi bi-x-circle"></i> Cancel
|
|
69
|
+
</a>
|
|
70
|
+
</div>
|
|
71
|
+
</form>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<!-- Toast container for notifications -->
|
|
78
|
+
<div class="toast-container position-fixed bottom-0 end-0 p-3">
|
|
79
|
+
<div id="toast" class="toast" role="alert">
|
|
80
|
+
<div class="toast-header">
|
|
81
|
+
<strong class="me-auto">Notification</strong>
|
|
82
|
+
<button type="button" class="btn-close" data-bs-dismiss="toast"></button>
|
|
83
|
+
</div>
|
|
84
|
+
<div class="toast-body" id="toast-message"></div>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
{% endblock %}
|
|
88
|
+
|
|
89
|
+
{% block extra_scripts %}
|
|
90
|
+
<script>
|
|
91
|
+
// Load available models
|
|
92
|
+
async function loadModels() {
|
|
93
|
+
try {
|
|
94
|
+
const response = await fetch('/api/models');
|
|
95
|
+
const models = await response.json();
|
|
96
|
+
|
|
97
|
+
const select = document.getElementById('model-select');
|
|
98
|
+
select.innerHTML = '<option value="">Select a model...</option>';
|
|
99
|
+
|
|
100
|
+
models.forEach(model => {
|
|
101
|
+
const option = document.createElement('option');
|
|
102
|
+
option.value = model.id;
|
|
103
|
+
|
|
104
|
+
// Show service provider and optionally model maker
|
|
105
|
+
let displayText = model.name;
|
|
106
|
+
if (model.model_maker && model.provider !== model.model_maker) {
|
|
107
|
+
// Show both: "Model Name [Model Maker via Service Provider]"
|
|
108
|
+
displayText += ` [${model.model_maker} via ${model.provider}]`;
|
|
109
|
+
} else {
|
|
110
|
+
// Show service only: "Model Name [Service Provider]"
|
|
111
|
+
displayText += ` [${model.provider}]`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
option.textContent = displayText;
|
|
115
|
+
select.appendChild(option);
|
|
116
|
+
});
|
|
117
|
+
} catch (error) {
|
|
118
|
+
console.error('Error loading models:', error);
|
|
119
|
+
showToast('Failed to load models', 'error');
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Show toast notification
|
|
124
|
+
function showToast(message, type = 'info') {
|
|
125
|
+
const toast = document.getElementById('toast');
|
|
126
|
+
const toastBody = document.getElementById('toast-message');
|
|
127
|
+
const toastHeader = toast.querySelector('.toast-header');
|
|
128
|
+
|
|
129
|
+
toastBody.textContent = message;
|
|
130
|
+
|
|
131
|
+
// Update toast style based on type
|
|
132
|
+
toastHeader.className = 'toast-header';
|
|
133
|
+
if (type === 'success') {
|
|
134
|
+
toastHeader.classList.add('bg-success', 'text-white');
|
|
135
|
+
} else if (type === 'error') {
|
|
136
|
+
toastHeader.classList.add('bg-danger', 'text-white');
|
|
137
|
+
} else if (type === 'warning') {
|
|
138
|
+
toastHeader.classList.add('bg-warning');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const bsToast = new bootstrap.Toast(toast);
|
|
142
|
+
bsToast.show();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Handle form submission
|
|
146
|
+
document.getElementById('new-conversation-form').addEventListener('submit', async (e) => {
|
|
147
|
+
e.preventDefault();
|
|
148
|
+
|
|
149
|
+
const formData = new FormData(e.target);
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
const response = await fetch('/api/conversations', {
|
|
153
|
+
method: 'POST',
|
|
154
|
+
body: formData
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
if (!response.ok) {
|
|
158
|
+
const error = await response.json();
|
|
159
|
+
throw new Error(error.detail || 'Failed to create conversation');
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const data = await response.json();
|
|
163
|
+
|
|
164
|
+
// Check for file upload warnings
|
|
165
|
+
if (data.warnings && data.warnings.length > 0) {
|
|
166
|
+
// Show warnings but still proceed
|
|
167
|
+
data.warnings.forEach(warning => {
|
|
168
|
+
showToast(warning, 'warning');
|
|
169
|
+
});
|
|
170
|
+
showToast('Conversation created (some files failed to upload)', 'success');
|
|
171
|
+
} else {
|
|
172
|
+
showToast('Conversation created successfully!', 'success');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Redirect to chat page after a short delay
|
|
176
|
+
setTimeout(() => {
|
|
177
|
+
window.location.href = `/chat/${data.id}`;
|
|
178
|
+
}, data.warnings && data.warnings.length > 0 ? 2000 : 1000);
|
|
179
|
+
|
|
180
|
+
} catch (error) {
|
|
181
|
+
console.error('Error creating conversation:', error);
|
|
182
|
+
showToast(error.message || 'Failed to create conversation', 'error');
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// Load models on page load
|
|
187
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
188
|
+
loadModels();
|
|
189
|
+
});
|
|
190
|
+
</script>
|
|
191
|
+
{% endblock %}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Web interface adapter for ConversationManager.
|
|
3
|
+
|
|
4
|
+
This module provides a web-compatible interface for handling tool permissions
|
|
5
|
+
and other interactive prompts via HTTP/SSE.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
import queue
|
|
12
|
+
import uuid
|
|
13
|
+
from typing import Optional, Dict
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class WebInterface:
|
|
20
|
+
"""
|
|
21
|
+
Web interface adapter for handling interactive prompts.
|
|
22
|
+
|
|
23
|
+
This class acts as a bridge between ConversationManager and the web UI,
|
|
24
|
+
managing permission requests and responses via queues.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self):
|
|
28
|
+
"""Initialise the web interface."""
|
|
29
|
+
self._permission_requests = {} # request_id -> request_data
|
|
30
|
+
self._permission_responses = {} # request_id -> response
|
|
31
|
+
self._pending_request_id = None
|
|
32
|
+
|
|
33
|
+
def prompt_tool_permission(self, tool_name: str, tool_description: str = None) -> Optional[str]:
|
|
34
|
+
"""
|
|
35
|
+
Request tool permission from web user.
|
|
36
|
+
|
|
37
|
+
This method:
|
|
38
|
+
1. Creates a permission request with a unique ID
|
|
39
|
+
2. Stores it for the web UI to retrieve
|
|
40
|
+
3. Waits for a response (with timeout)
|
|
41
|
+
4. Returns the user's choice
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
tool_name: Name of the tool
|
|
45
|
+
tool_description: Optional description of the tool
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
'allowed' if user grants permission for all future uses
|
|
49
|
+
'denied' if user denies this and all future uses
|
|
50
|
+
'once' if user grants permission for this time only
|
|
51
|
+
None if user cancelled or timeout
|
|
52
|
+
"""
|
|
53
|
+
# Generate unique request ID
|
|
54
|
+
request_id = str(uuid.uuid4())
|
|
55
|
+
|
|
56
|
+
# Store the permission request
|
|
57
|
+
self._permission_requests[request_id] = {
|
|
58
|
+
'tool_name': tool_name,
|
|
59
|
+
'tool_description': tool_description,
|
|
60
|
+
'status': 'pending'
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
# Store the current pending request ID
|
|
64
|
+
self._pending_request_id = request_id
|
|
65
|
+
|
|
66
|
+
logger.info(f"Web permission request created: {request_id} for tool: {tool_name}")
|
|
67
|
+
|
|
68
|
+
# Wait for response (with timeout)
|
|
69
|
+
# In a real implementation, this would use asyncio or threading.Event
|
|
70
|
+
# For now, we implement a simple polling mechanism
|
|
71
|
+
import time
|
|
72
|
+
timeout_seconds = 300 # 5 minutes timeout
|
|
73
|
+
poll_interval = 0.5 # Poll every 0.5 seconds
|
|
74
|
+
elapsed = 0
|
|
75
|
+
|
|
76
|
+
while elapsed < timeout_seconds:
|
|
77
|
+
if request_id in self._permission_responses:
|
|
78
|
+
response = self._permission_responses[request_id]
|
|
79
|
+
|
|
80
|
+
# Clean up
|
|
81
|
+
del self._permission_requests[request_id]
|
|
82
|
+
del self._permission_responses[request_id]
|
|
83
|
+
if self._pending_request_id == request_id:
|
|
84
|
+
self._pending_request_id = None
|
|
85
|
+
|
|
86
|
+
logger.info(f"Web permission response received for {request_id}: {response}")
|
|
87
|
+
return response
|
|
88
|
+
|
|
89
|
+
time.sleep(poll_interval)
|
|
90
|
+
elapsed += poll_interval
|
|
91
|
+
|
|
92
|
+
# Timeout - clean up and return None
|
|
93
|
+
logger.warning(f"Web permission request {request_id} timed out after {timeout_seconds}s")
|
|
94
|
+
if request_id in self._permission_requests:
|
|
95
|
+
del self._permission_requests[request_id]
|
|
96
|
+
if self._pending_request_id == request_id:
|
|
97
|
+
self._pending_request_id = None
|
|
98
|
+
|
|
99
|
+
return None
|
|
100
|
+
|
|
101
|
+
def get_pending_permission_request(self) -> Optional[Dict]:
|
|
102
|
+
"""
|
|
103
|
+
Get the current pending permission request if any.
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
Dictionary with request_id, tool_name, tool_description, or None
|
|
107
|
+
"""
|
|
108
|
+
if self._pending_request_id and self._pending_request_id in self._permission_requests:
|
|
109
|
+
return {
|
|
110
|
+
'request_id': self._pending_request_id,
|
|
111
|
+
**self._permission_requests[self._pending_request_id]
|
|
112
|
+
}
|
|
113
|
+
return None
|
|
114
|
+
|
|
115
|
+
def submit_permission_response(self, request_id: str, response: str) -> bool:
|
|
116
|
+
"""
|
|
117
|
+
Submit a response to a pending permission request.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
request_id: The request ID
|
|
121
|
+
response: User's response ('once', 'allowed', 'denied', or None for cancel)
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
True if response was accepted, False if request not found
|
|
125
|
+
"""
|
|
126
|
+
if request_id not in self._permission_requests:
|
|
127
|
+
logger.warning(f"Permission response submitted for unknown request: {request_id}")
|
|
128
|
+
return False
|
|
129
|
+
|
|
130
|
+
# Store the response
|
|
131
|
+
self._permission_responses[request_id] = response
|
|
132
|
+
|
|
133
|
+
# Update request status
|
|
134
|
+
self._permission_requests[request_id]['status'] = 'responded'
|
|
135
|
+
|
|
136
|
+
logger.info(f"Permission response submitted for {request_id}: {response}")
|
|
137
|
+
return True
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dtSpark
|
|
3
|
+
Version: 1.0.4
|
|
4
|
+
Summary: Secure Personal AI Research Kit - Multi-provider LLM CLI/Web interface with MCP tool integration
|
|
5
|
+
Home-page: https://github.com/digital-thought/dtSpark
|
|
6
|
+
Author: Matthew Westwood-Hill
|
|
7
|
+
Author-email: Matthew Westwood-Hill <matthew@digital-thought.org>
|
|
8
|
+
License: MIT
|
|
9
|
+
Project-URL: Homepage, https://github.com/digital-thought/dtSpark
|
|
10
|
+
Project-URL: Documentation, https://github.com/digital-thought/dtSpark#readme
|
|
11
|
+
Project-URL: Repository, https://github.com/digital-thought/dtSpark
|
|
12
|
+
Project-URL: Issues, https://github.com/digital-thought/dtSpark/issues
|
|
13
|
+
Keywords: llm,ai,chatbot,aws,bedrock,anthropic,claude,ollama,mcp,model-context-protocol,cli,web
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
|
+
Classifier: Environment :: Console
|
|
16
|
+
Classifier: Environment :: Web Environment
|
|
17
|
+
Classifier: Intended Audience :: Developers
|
|
18
|
+
Classifier: Intended Audience :: Information Technology
|
|
19
|
+
Classifier: Intended Audience :: Science/Research
|
|
20
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
21
|
+
Classifier: Operating System :: OS Independent
|
|
22
|
+
Classifier: Programming Language :: Python :: 3
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
26
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
27
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
28
|
+
Classifier: Topic :: Communications :: Chat
|
|
29
|
+
Requires-Python: >=3.10
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
License-File: LICENSE
|
|
32
|
+
Requires-Dist: boto3>=1.28.0
|
|
33
|
+
Requires-Dist: botocore>=1.31.0
|
|
34
|
+
Requires-Dist: fastapi>=0.100.0
|
|
35
|
+
Requires-Dist: uvicorn>=0.22.0
|
|
36
|
+
Requires-Dist: jinja2>=3.1.0
|
|
37
|
+
Requires-Dist: python-multipart>=0.0.6
|
|
38
|
+
Requires-Dist: sse-starlette>=1.6.0
|
|
39
|
+
Requires-Dist: rich>=13.0.0
|
|
40
|
+
Requires-Dist: prompt_toolkit>=3.0.0
|
|
41
|
+
Requires-Dist: httpx>=0.24.0
|
|
42
|
+
Requires-Dist: aiohttp>=3.8.0
|
|
43
|
+
Requires-Dist: mcp>=0.9.0
|
|
44
|
+
Requires-Dist: pyyaml>=6.0
|
|
45
|
+
Requires-Dist: dtPyAppFramework>=4.0.3
|
|
46
|
+
Requires-Dist: tiktoken>=0.5.0
|
|
47
|
+
Requires-Dist: ollama>=0.2.0
|
|
48
|
+
Requires-Dist: cryptography>=41.0.0
|
|
49
|
+
Requires-Dist: anthropic>=0.18.0
|
|
50
|
+
Requires-Dist: APScheduler>=3.10.0
|
|
51
|
+
Requires-Dist: markdown>=3.4.0
|
|
52
|
+
Provides-Extra: dev
|
|
53
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
54
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
55
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
56
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
57
|
+
Requires-Dist: isort>=5.12.0; extra == "dev"
|
|
58
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
59
|
+
Requires-Dist: flake8>=6.0.0; extra == "dev"
|
|
60
|
+
Provides-Extra: mysql
|
|
61
|
+
Requires-Dist: mysql-connector-python>=8.0.0; extra == "mysql"
|
|
62
|
+
Provides-Extra: postgresql
|
|
63
|
+
Requires-Dist: psycopg2-binary>=2.9.0; extra == "postgresql"
|
|
64
|
+
Provides-Extra: mssql
|
|
65
|
+
Requires-Dist: pyodbc>=4.0.0; extra == "mssql"
|
|
66
|
+
Provides-Extra: all-databases
|
|
67
|
+
Requires-Dist: mysql-connector-python>=8.0.0; extra == "all-databases"
|
|
68
|
+
Requires-Dist: psycopg2-binary>=2.9.0; extra == "all-databases"
|
|
69
|
+
Requires-Dist: pyodbc>=4.0.0; extra == "all-databases"
|
|
70
|
+
Dynamic: author
|
|
71
|
+
Dynamic: home-page
|
|
72
|
+
Dynamic: license-file
|
|
73
|
+
Dynamic: requires-python
|
|
74
|
+
|
|
75
|
+
# Spark - Secure Personal AI Research Kit
|
|
76
|
+
|
|
77
|
+
[](https://opensource.org/licenses/MIT)
|
|
78
|
+
[](https://www.python.org/downloads/)
|
|
79
|
+
|
|
80
|
+
**Spark** is a powerful, multi-provider LLM interface for conversational AI with integrated tool support. It supports AWS Bedrock, Anthropic Direct API, and Ollama local models through both CLI and Web interfaces.
|
|
81
|
+
|
|
82
|
+
## Key Features
|
|
83
|
+
|
|
84
|
+
- **Multi-Provider Support** - AWS Bedrock, Anthropic Direct API, and Ollama local models
|
|
85
|
+
- **Dual Interface** - Rich CLI terminal UI and modern Web browser interface
|
|
86
|
+
- **MCP Tool Integration** - Connect external tools via Model Context Protocol
|
|
87
|
+
- **Intelligent Context Management** - Automatic conversation compaction with model-aware limits
|
|
88
|
+
- **Security Features** - Prompt inspection, tool permissions, and audit logging
|
|
89
|
+
- **Multiple Database Backends** - SQLite, MySQL, PostgreSQL, and Microsoft SQL Server
|
|
90
|
+
|
|
91
|
+
## Quick Start
|
|
92
|
+
|
|
93
|
+
### Installation
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
pip install dtSpark
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### First-Time Setup
|
|
100
|
+
|
|
101
|
+
Run the interactive setup wizard to configure Spark:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
spark --setup
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
This guides you through:
|
|
108
|
+
- LLM provider selection and configuration
|
|
109
|
+
- Database setup
|
|
110
|
+
- Interface preferences
|
|
111
|
+
- Security settings
|
|
112
|
+
|
|
113
|
+
### Running Spark
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
# Start with CLI interface
|
|
117
|
+
spark
|
|
118
|
+
|
|
119
|
+
# Or use the alternative command
|
|
120
|
+
dtSpark
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Documentation
|
|
124
|
+
|
|
125
|
+
Comprehensive documentation is available in the [docs](docs/) folder:
|
|
126
|
+
|
|
127
|
+
- [Installation Guide](docs/installation.md) - Detailed installation instructions
|
|
128
|
+
- [Configuration Reference](docs/configuration.md) - Complete config.yaml documentation
|
|
129
|
+
- [Features Guide](docs/features.md) - Detailed feature documentation
|
|
130
|
+
- [CLI Reference](docs/cli-reference.md) - Command-line options and chat commands
|
|
131
|
+
- [Web Interface](docs/web-interface.md) - Web UI guide
|
|
132
|
+
- [MCP Integration](docs/mcp-integration.md) - Tool integration documentation
|
|
133
|
+
- [Security](docs/security.md) - Security features and best practices
|
|
134
|
+
|
|
135
|
+
## Architecture Overview
|
|
136
|
+
|
|
137
|
+
```mermaid
|
|
138
|
+
graph LR
|
|
139
|
+
subgraph Interfaces
|
|
140
|
+
CLI[CLI]
|
|
141
|
+
WEB[Web]
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
subgraph Core
|
|
145
|
+
CM[Conversation<br/>Manager]
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
subgraph Providers
|
|
149
|
+
BEDROCK[AWS Bedrock]
|
|
150
|
+
ANTHROPIC[Anthropic]
|
|
151
|
+
OLLAMA[Ollama]
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
subgraph Tools
|
|
155
|
+
MCP[MCP Servers]
|
|
156
|
+
BUILTIN[Built-in Tools]
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
CLI --> CM
|
|
160
|
+
WEB --> CM
|
|
161
|
+
CM --> BEDROCK
|
|
162
|
+
CM --> ANTHROPIC
|
|
163
|
+
CM --> OLLAMA
|
|
164
|
+
CM --> MCP
|
|
165
|
+
CM --> BUILTIN
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Requirements
|
|
169
|
+
|
|
170
|
+
- Python 3.10 or higher
|
|
171
|
+
- AWS credentials (for Bedrock)
|
|
172
|
+
- Anthropic API key (for direct API)
|
|
173
|
+
- Ollama server (for local models)
|
|
174
|
+
|
|
175
|
+
## Licence
|
|
176
|
+
|
|
177
|
+
MIT Licence - see [LICENSE](LICENSE) for details.
|
|
178
|
+
|
|
179
|
+
## Author
|
|
180
|
+
|
|
181
|
+
Matthew Westwood-Hill
|
|
182
|
+
matthew@digital-thought.org
|
|
183
|
+
|
|
184
|
+
## Support
|
|
185
|
+
|
|
186
|
+
- **Documentation**: [docs/](docs/)
|
|
187
|
+
- **Issues**: [GitHub Issues](https://github.com/digital-thought/dtSpark/issues)
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
dtSpark/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
dtSpark/_description.txt,sha256=4OTFeEBVY3q0IbKbSdmi7lQ9bT5aeFHl9mUmQ7qluSg,209
|
|
3
|
+
dtSpark/_full_name.txt,sha256=wsMYXtT12WMrY9gT1JHiKdE4k7H59psECS6cSD07giQ,31
|
|
4
|
+
dtSpark/_licence.txt,sha256=Mvt5wkOkst8VGlk48vwN3CgHwMHLfmplKSPOUEbTfOw,1071
|
|
5
|
+
dtSpark/_metadata.yaml,sha256=h3PQd2QsY5yUBzS2b6EueTwkmd57svsbAKcwDVVEfIo,188
|
|
6
|
+
dtSpark/_name.txt,sha256=kDZC5_a3iMKIPOUvtLXl0C9N5DiOfgUCsecwTUnkJhs,7
|
|
7
|
+
dtSpark/_version.txt,sha256=0bd7iPS59nWpAOQUOsdoohIYYjz-FwPKQxYjiMMnG9Y,6
|
|
8
|
+
dtSpark/cli_interface.py,sha256=aB-rFAh6o8GOwwecx-rSHEj3W4bmQHp51OPEBtC2CEw,96911
|
|
9
|
+
dtSpark/conversation_manager.py,sha256=e1aTJpUjxV96G9xrssJl1ujPJMZZwtfufQ8ZDb1WyAI,132270
|
|
10
|
+
dtSpark/launch.py,sha256=iE5f4KQIlQEbNioCx0PyVGYWREHIGBFuup_np4FZvZ4,941
|
|
11
|
+
dtSpark/aws/__init__.py,sha256=tVuUKO69R3ZOkHQNQo81ukGTdU0OAyYVxkH0DgtwaTM,261
|
|
12
|
+
dtSpark/aws/authentication.py,sha256=3rEyFK2BjeN-A1ztjtm3bdfA_XGWkdJlsmdlvCsV2QE,11288
|
|
13
|
+
dtSpark/aws/bedrock.py,sha256=j1OknN76LehIMRmoqzx8G3DUSqk2IsO3Fiy5AxQp7Fw,24250
|
|
14
|
+
dtSpark/aws/costs.py,sha256=eyjigH_Yef7g5cKqhR2QUx_7y4E6-NsLd9-WL52I2vM,11931
|
|
15
|
+
dtSpark/aws/pricing.py,sha256=pk85e--C5Iz0Y-6iWv_VfENAnrcWQ_9XK0fJQSlk5Xk,23388
|
|
16
|
+
dtSpark/core/__init__.py,sha256=kORX-9C7L91WxMTMUHXr3Yc09rb6oJNzEH5_KDRdgqM,477
|
|
17
|
+
dtSpark/core/application.py,sha256=sPGXH3GNH7VszCaBR8WW8GvSzfQ-7ccAs2U2BT6e15o,154605
|
|
18
|
+
dtSpark/core/context_compaction.py,sha256=FWN352EW8n-eWv0MzbOiWIiKcooRxeIAb7611eN-kdY,31898
|
|
19
|
+
dtSpark/daemon/__init__.py,sha256=xdKEyMsNXgIv6nNerpDcwf94c80n-BFoJFaucWxVF64,3300
|
|
20
|
+
dtSpark/daemon/__main__.py,sha256=sRNG4RJ-Ejvd-f7OAU5slPOtxVpCoC2OfazdTJro69Q,170
|
|
21
|
+
dtSpark/daemon/action_monitor.py,sha256=grejmbWWCMyDVQZ8HXn71OaGEMTFD-QK_N9g-oCemso,7772
|
|
22
|
+
dtSpark/daemon/daemon_app.py,sha256=ZuCJYTeb5dfUOF53F9ydF5rw1orGx35Utez-wI2VMTw,28673
|
|
23
|
+
dtSpark/daemon/daemon_manager.py,sha256=a5OitkSTjSzUaOj4gYzTmZh5QmAN3dxMelOPwESj-W0,9170
|
|
24
|
+
dtSpark/daemon/execution_coordinator.py,sha256=ykZ2iKe6XaoD3JCOjhnEbiD8q12d7opg9ZafZFiW7lQ,5869
|
|
25
|
+
dtSpark/daemon/pid_file.py,sha256=gAfxJcGfdvsjpY40Y1RV8DFB6mSAA4VH8HI26edxJns,4596
|
|
26
|
+
dtSpark/database/__init__.py,sha256=NaNZ-06nPUaXrq1C4W_YHdUWvcfMUAD8qdJOhPJSYco,25950
|
|
27
|
+
dtSpark/database/autonomous_actions.py,sha256=4HCgL6TuIZptA21vE39eReqBeOgXQ0mPJ5HXymbnH50,36022
|
|
28
|
+
dtSpark/database/backends.py,sha256=HAskNCI5QsF5QeYVD596XHn4ITlfqJkJW2jEkJUpYl0,9347
|
|
29
|
+
dtSpark/database/connection.py,sha256=egEGhp7gb7beoEZCYUUflY2YMAC5gdDs_XTWuB0CwiM,3672
|
|
30
|
+
dtSpark/database/conversations.py,sha256=kldbkVe1UMz52FSrX8RyCYNeAPE7P6pfpIGWHDsijpU,18338
|
|
31
|
+
dtSpark/database/credential_prompt.py,sha256=0YEu7sjTn5PGVNs7tQ6gMwBZ_j_FjuhrzkCEmXugWmw,6531
|
|
32
|
+
dtSpark/database/files.py,sha256=WDOE_aShfzlDvEQcwx9bZKPFz1unrIoBDQqpjG8UBdA,7006
|
|
33
|
+
dtSpark/database/mcp_ops.py,sha256=jgc8Ig1iogvrbOV8cUcSlmFRuhp14x1K8sgicPl2Neg,12706
|
|
34
|
+
dtSpark/database/messages.py,sha256=h2iVIWFzashtNlbg-8ln37H12yeKTOofRVXgDR1yPtY,5296
|
|
35
|
+
dtSpark/database/schema.py,sha256=j7Av9Ag9Q60pz99d26wjqvaHvQ4OZh8zAd5HX9_6Rjw,23336
|
|
36
|
+
dtSpark/database/tool_permissions.py,sha256=6NjYmU82BAq2lFsyhgzkudVoOpWAw-R-l2AV1_Z0KC4,6227
|
|
37
|
+
dtSpark/database/usage.py,sha256=6DwKvQTxx8F-P7cNU7dPjqCinIp9cst6OiUgJkY008o,5368
|
|
38
|
+
dtSpark/files/__init__.py,sha256=BTSp2hvW4NI6GSka3eKoNCfvzcxUPPWJOiqbY5gXe4I,90
|
|
39
|
+
dtSpark/files/manager.py,sha256=QsCux3dJBnVqpXvWW8EE-uGHZTe_d6orOvJ2uGSod98,10909
|
|
40
|
+
dtSpark/limits/__init__.py,sha256=q0E5-J6qneu7ZIWjGLeMXbouDhE1oGVkAUIkWe2M5Bg,250
|
|
41
|
+
dtSpark/limits/costs.py,sha256=YpPTQ3uqCkrOr4xpVOkIEml_vdLbzIAak_rV__2-wxg,10213
|
|
42
|
+
dtSpark/limits/tokens.py,sha256=ExbD-91hrJ0gHKoq-L6xYWU8Zxn5Wgqme25ATmgJ8MM,13590
|
|
43
|
+
dtSpark/llm/__init__.py,sha256=Govebgc3ukJFyu7eyIwTeUcsBwpUTeWkF4xIstW9kQk,608
|
|
44
|
+
dtSpark/llm/anthropic_direct.py,sha256=gq66qyA5Mup0ySSRMczP2B6cbsxQMN82uULeQ-aEc68,18450
|
|
45
|
+
dtSpark/llm/base.py,sha256=M8DaC_uZSa7UNt7Cx_EUBZcx6dbHCeEQBTSYGgBbphM,4217
|
|
46
|
+
dtSpark/llm/context_limits.py,sha256=YnyPpG7f6esS7KcS1hUrKLijYWL25crmQsWA_DeIhco,16627
|
|
47
|
+
dtSpark/llm/manager.py,sha256=Qg9glX5ZhepWaRnb8ILZiPoiwqzC8Y318JgwuJf9m58,5837
|
|
48
|
+
dtSpark/llm/ollama.py,sha256=r7ePWK1sfSveN6--Ln-Brtrxkli61uJWSrPU0Qy0MZg,20633
|
|
49
|
+
dtSpark/mcp_integration/__init__.py,sha256=pFw-yTSSJ1yx_ksTe94eq8pBrwDD-5Z-UqSM3VO4deQ,157
|
|
50
|
+
dtSpark/mcp_integration/manager.py,sha256=b-FfmAas344Ordj6AIdwxCgl0dqPx0WiLIu4Vt4URBA,25149
|
|
51
|
+
dtSpark/mcp_integration/tool_selector.py,sha256=VYtKXdYEFK_aC22wOWKI0hm9S67DsZtHScI1HpZVnjE,10003
|
|
52
|
+
dtSpark/resources/config.yaml.template,sha256=A3goAx9qD9_lqbfB6UqKoIVSDYjo_EZFVP-snqQnwHA,23966
|
|
53
|
+
dtSpark/safety/__init__.py,sha256=fdcZ4qNbYhH7Un9iKLwNe2GDr_doUmpSgtv-oNS8qPE,460
|
|
54
|
+
dtSpark/safety/llm_service.py,sha256=N2J9_Od-fGGvk9BkddD6CFd81PrJ03sMjSz6egBDYr4,3820
|
|
55
|
+
dtSpark/safety/patterns.py,sha256=W9xe-HoeQOl4UfGz7xxFm9VldKeoUg79FVd7ZUXQswE,8040
|
|
56
|
+
dtSpark/safety/prompt_inspector.py,sha256=6FAR7UtcsEYtbfOEdwHsJ__cjqdAsKjNtKPXp85c0sw,16297
|
|
57
|
+
dtSpark/safety/violation_logger.py,sha256=HCWKOWuhIe4_BIx9Ccb_mcdW-14fWRMGH6YqFc-3o8c,11769
|
|
58
|
+
dtSpark/scheduler/__init__.py,sha256=DkhER25jUoqFU1c4xTDZyoJhMJ-wFS6yZqvsp3cr0zo,494
|
|
59
|
+
dtSpark/scheduler/creation_tools.py,sha256=vShUXGI9VaQni3en3OIXuGx2HNT0uEL0MTkk46nh0sQ,21906
|
|
60
|
+
dtSpark/scheduler/execution_queue.py,sha256=7_yXnGxO-arZTl0qPbyE-kDhZDVXQKT2z9zIKj9s1SA,4954
|
|
61
|
+
dtSpark/scheduler/executor.py,sha256=n42MGNyZOoQohpYh1QFenZzFsv560S0h-o_-7FajkJc,43709
|
|
62
|
+
dtSpark/scheduler/manager.py,sha256=vwtmQadqf3cv1QzdZJzFqC0l3bBzIplGZ79BUjfsh4o,12846
|
|
63
|
+
dtSpark/tools/__init__.py,sha256=TPK-c8CmXheEkoiFzL9PMP8ve0hTpw9iIV2xlGLTsMc,147
|
|
64
|
+
dtSpark/tools/builtin.py,sha256=Bgyi99VJvcXtJS6M69s_Sn-TcdM7PSvkjLuYTt6EH9g,29484
|
|
65
|
+
dtSpark/web/__init__.py,sha256=5OrzA2yIR9NBf9mlTPnrQ0afMJTBuEgnzxq4QmIYe54,428
|
|
66
|
+
dtSpark/web/auth.py,sha256=uSIHwJOiklkjZSpLcznwOL1pVQktKeWifZygt068M9Y,4384
|
|
67
|
+
dtSpark/web/dependencies.py,sha256=ku54-Vb8wYvGVQ8Kluzxj8zm2Re3wDgU5LzFJ0NVIsI,874
|
|
68
|
+
dtSpark/web/server.py,sha256=sDOTEmLt3A0xhj-0rmM3jL8Spz3bf5_zfPNHqIDzH7A,20913
|
|
69
|
+
dtSpark/web/session.py,sha256=yoz4yKGqUiYtxc5qoKtyBxNH0JrITSUyzgfu5iTo_Lw,4972
|
|
70
|
+
dtSpark/web/ssl_utils.py,sha256=keDhLzEbc9REUGSdMls0vxBV3yPunR490u4j6EvB8dg,6624
|
|
71
|
+
dtSpark/web/web_interface.py,sha256=Jl50HxTatj6yzKFBvOxp51jlhNe3CqOqnBjT1uL8HEw,4713
|
|
72
|
+
dtSpark/web/endpoints/__init__.py,sha256=uOTiOKx6IDZ2kc0-2GS2hqZD2qX6KtwAhMMZQbS7pWc,356
|
|
73
|
+
dtSpark/web/endpoints/autonomous_actions.py,sha256=tHJtFvJJtpr34LujQu-udZvT4vY5TpJB3Sn0WPJ8giM,36588
|
|
74
|
+
dtSpark/web/endpoints/chat.py,sha256=w4Vi3X2e7orID0Ny5xF7vyTHe9mk8gaH7o67GB50psc,20641
|
|
75
|
+
dtSpark/web/endpoints/conversations.py,sha256=uFNpd_9DF1jqTEKFd9qRWK-uffXyTwz4Px9JIur_128,11728
|
|
76
|
+
dtSpark/web/endpoints/main_menu.py,sha256=8VUUBN7DopM0_Jy4Uaqkg-HHXxGVxz9uRcQ1_zXzM-Q,17752
|
|
77
|
+
dtSpark/web/endpoints/streaming.py,sha256=vhpLefhc1PUDlmZ3Lx4IXizZfHy45zPgv5POhb-TdFU,14581
|
|
78
|
+
dtSpark/web/static/css/dark-theme.css,sha256=K2q2C1s5kjqi_O1xAF86HTVFsI6sEfwPAPhVEfmT02M,8117
|
|
79
|
+
dtSpark/web/static/js/actions.js,sha256=NC9nLFfKEwffE88yBoaaGosmi4q9RoSVOPnziTD08GQ,37421
|
|
80
|
+
dtSpark/web/static/js/chat.js,sha256=s-TeeFgpRAsz0npeQHKPhQCgQawe03LBwLQYa7bjM84,21863
|
|
81
|
+
dtSpark/web/static/js/main.js,sha256=A5m_VDyEzMcfCL9nnId2oCtFecvWRlpedzzPbt34QGY,16235
|
|
82
|
+
dtSpark/web/static/js/sse-client.js,sha256=-YyRXNzSoQWrNLDvT8xGXtbgll_1JiP8p398OtQU7H8,8438
|
|
83
|
+
dtSpark/web/templates/actions.html,sha256=KHJ552XjwPCCOrz0WUkehRzNwuBkS7qVpsAoCx1lGZI,18852
|
|
84
|
+
dtSpark/web/templates/base.html,sha256=ne7kY8mnT5GpGDcxokQhNsn6H4tVksBE9BsXcJAsdmI,3765
|
|
85
|
+
dtSpark/web/templates/chat.html,sha256=OQiqmOV-Q71MxXt7M7yLcCGH0Jyhen4p3hLqU6829aw,32655
|
|
86
|
+
dtSpark/web/templates/conversations.html,sha256=URJX3W5-MjUjuUAXixziY1yFG-T6Ho5_u9Up7FcH70E,13004
|
|
87
|
+
dtSpark/web/templates/goodbye.html,sha256=4VnUgq6gHCMeJuty51qq8SZuTtpEMMUOsgTBut37Mc8,2825
|
|
88
|
+
dtSpark/web/templates/login.html,sha256=OkLf_uzMYhl_o9ER1RQZc8ch6-bCaiOokEhKbeEl8E8,3274
|
|
89
|
+
dtSpark/web/templates/main_menu.html,sha256=AXVFdEwAUWLFF_hXB7GrutwLTtfCqmuR1Yl5i39y3Co,37871
|
|
90
|
+
dtSpark/web/templates/new_conversation.html,sha256=hoiQ4Ew3lpFWHTsYza0JiBqqWJs7St7jTusKDnxRR8w,6844
|
|
91
|
+
dtspark-1.0.4.dist-info/licenses/LICENSE,sha256=jIuWlmbirwQabTwxfzE7SXT1LpCG-y_GRQ3VnoRTKgo,1101
|
|
92
|
+
dtspark-1.0.4.dist-info/METADATA,sha256=xrfDylfewoaeddXM9zaXzV6Yt5kDYLJeW7yga2mDhG0,5999
|
|
93
|
+
dtspark-1.0.4.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
|
|
94
|
+
dtspark-1.0.4.dist-info/entry_points.txt,sha256=IpIwa_a6XY8Z2w7DtgYAhpFHHEbha-zhLkyttWd3zpw,76
|
|
95
|
+
dtspark-1.0.4.dist-info/top_level.txt,sha256=x-6lMA6vNuxyDNJGNOKI4dyy7L0kOb9V98I5z46bJVY,8
|
|
96
|
+
dtspark-1.0.4.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-2025 Matthew Westwood-Hill / Digital-Thought
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
dtSpark
|