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,329 @@
1
+ """
2
+ Database backend abstraction for multi-database support.
3
+
4
+ Supports:
5
+ - SQLite (default, local file-based)
6
+ - MySQL/MariaDB (remote database server)
7
+ - PostgreSQL (remote database server)
8
+ - Microsoft SQL Server (remote database server)
9
+
10
+
11
+ """
12
+
13
+ import logging
14
+ import sqlite3
15
+ import os
16
+ from abc import ABC, abstractmethod
17
+ from typing import Optional, Dict, Any, Union
18
+ from dataclasses import dataclass
19
+
20
+ from dtPyAppFramework.paths import ApplicationPaths
21
+
22
+ @dataclass
23
+ class DatabaseCredentials:
24
+ """Database connection credentials."""
25
+ host: Optional[str] = None
26
+ port: Optional[int] = None
27
+ database: Optional[str] = None
28
+ username: Optional[str] = None
29
+ password: Optional[str] = None
30
+ ssl: bool = False
31
+ driver: Optional[str] = None # For MSSQL
32
+ path: Optional[str] = None # For SQLite
33
+
34
+
35
+ class DatabaseBackend(ABC):
36
+ """Abstract base class for database backends."""
37
+
38
+ def __init__(self, credentials: DatabaseCredentials):
39
+ """
40
+ Initialise database backend.
41
+
42
+ Args:
43
+ credentials: Database connection credentials
44
+ """
45
+ self.credentials = credentials
46
+ self.connection = None
47
+
48
+ @abstractmethod
49
+ def connect(self):
50
+ """
51
+ Establish database connection.
52
+
53
+ Returns:
54
+ Database connection object
55
+ """
56
+ pass
57
+
58
+ @abstractmethod
59
+ def get_placeholder(self) -> str:
60
+ """
61
+ Get SQL parameter placeholder for this database.
62
+
63
+ Returns:
64
+ Placeholder string (?, %s, etc.)
65
+ """
66
+ pass
67
+
68
+ @abstractmethod
69
+ def get_autoincrement_syntax(self) -> str:
70
+ """
71
+ Get auto-increment syntax for this database.
72
+
73
+ Returns:
74
+ Auto-increment SQL syntax
75
+ """
76
+ pass
77
+
78
+ @abstractmethod
79
+ def supports_returning(self) -> bool:
80
+ """
81
+ Check if database supports RETURNING clause.
82
+
83
+ Returns:
84
+ True if RETURNING is supported
85
+ """
86
+ pass
87
+
88
+ def close(self):
89
+ """Close database connection."""
90
+ if self.connection:
91
+ self.connection.close()
92
+ self.connection = None
93
+
94
+ def test_connection(self) -> bool:
95
+ """
96
+ Test database connection.
97
+
98
+ Returns:
99
+ True if connection successful
100
+ """
101
+ try:
102
+ conn = self.connect()
103
+ cursor = conn.cursor()
104
+ cursor.execute("SELECT 1")
105
+ cursor.close()
106
+ return True
107
+ except Exception as e:
108
+ logging.error(f"Database connection test failed: {e}")
109
+ return False
110
+
111
+
112
+ class SQLiteBackend(DatabaseBackend):
113
+ """SQLite database backend (local file-based)."""
114
+
115
+ def connect(self):
116
+ """Establish SQLite connection."""
117
+ if self.connection is None:
118
+ self.connection = sqlite3.connect(
119
+ os.path.join(ApplicationPaths().usr_data_root_path, "conversations.db"),
120
+ check_same_thread=False
121
+ )
122
+ self.connection.row_factory = sqlite3.Row
123
+ return self.connection
124
+
125
+ def get_placeholder(self) -> str:
126
+ return "?"
127
+
128
+ def get_autoincrement_syntax(self) -> str:
129
+ return "AUTOINCREMENT"
130
+
131
+ def supports_returning(self) -> bool:
132
+ return False # SQLite < 3.35 doesn't support RETURNING
133
+
134
+
135
+ class MySQLBackend(DatabaseBackend):
136
+ """MySQL/MariaDB database backend."""
137
+
138
+ def connect(self):
139
+ """Establish MySQL connection."""
140
+ try:
141
+ import mysql.connector
142
+ except ImportError:
143
+ raise ImportError(
144
+ "mysql-connector-python is required for MySQL support. "
145
+ "Install it with: pip install mysql-connector-python"
146
+ )
147
+
148
+ if self.connection is None:
149
+ connection_params = {
150
+ 'host': self.credentials.host,
151
+ 'port': self.credentials.port or 3306,
152
+ 'database': self.credentials.database,
153
+ 'user': self.credentials.username,
154
+ 'password': self.credentials.password,
155
+ 'autocommit': False
156
+ }
157
+
158
+ if self.credentials.ssl:
159
+ connection_params['ssl_disabled'] = False
160
+
161
+ self.connection = mysql.connector.connect(**connection_params)
162
+
163
+ return self.connection
164
+
165
+ def get_placeholder(self) -> str:
166
+ return "%s"
167
+
168
+ def get_autoincrement_syntax(self) -> str:
169
+ return "AUTO_INCREMENT"
170
+
171
+ def supports_returning(self) -> bool:
172
+ return False # MySQL doesn't support RETURNING
173
+
174
+
175
+ class PostgreSQLBackend(DatabaseBackend):
176
+ """PostgreSQL database backend."""
177
+
178
+ def connect(self):
179
+ """Establish PostgreSQL connection."""
180
+ try:
181
+ import psycopg2
182
+ import psycopg2.extras
183
+ except ImportError:
184
+ raise ImportError(
185
+ "psycopg2 is required for PostgreSQL support. "
186
+ "Install it with: pip install psycopg2-binary"
187
+ )
188
+
189
+ if self.connection is None:
190
+ connection_params = {
191
+ 'host': self.credentials.host,
192
+ 'port': self.credentials.port or 5432,
193
+ 'database': self.credentials.database,
194
+ 'user': self.credentials.username,
195
+ 'password': self.credentials.password
196
+ }
197
+
198
+ if self.credentials.ssl:
199
+ connection_params['sslmode'] = 'require'
200
+
201
+ self.connection = psycopg2.connect(**connection_params)
202
+ self.connection.autocommit = False
203
+
204
+ return self.connection
205
+
206
+ def get_placeholder(self) -> str:
207
+ return "%s"
208
+
209
+ def get_autoincrement_syntax(self) -> str:
210
+ return "SERIAL"
211
+
212
+ def supports_returning(self) -> bool:
213
+ return True
214
+
215
+
216
+ class MSSQLBackend(DatabaseBackend):
217
+ """Microsoft SQL Server database backend."""
218
+
219
+ def connect(self):
220
+ """Establish MSSQL connection."""
221
+ try:
222
+ import pyodbc
223
+ except ImportError:
224
+ raise ImportError(
225
+ "pyodbc is required for Microsoft SQL Server support. "
226
+ "Install it with: pip install pyodbc"
227
+ )
228
+
229
+ if self.connection is None:
230
+ driver = self.credentials.driver or "ODBC Driver 17 for SQL Server"
231
+
232
+ connection_string = (
233
+ f"DRIVER={{{driver}}};"
234
+ f"SERVER={self.credentials.host};"
235
+ f"DATABASE={self.credentials.database};"
236
+ f"UID={self.credentials.username};"
237
+ f"PWD={self.credentials.password};"
238
+ )
239
+
240
+ if self.credentials.port:
241
+ connection_string = connection_string.replace(
242
+ f"SERVER={self.credentials.host};",
243
+ f"SERVER={self.credentials.host},{self.credentials.port};"
244
+ )
245
+
246
+ if self.credentials.ssl:
247
+ connection_string += "Encrypt=yes;TrustServerCertificate=no;"
248
+
249
+ self.connection = pyodbc.connect(connection_string)
250
+ self.connection.autocommit = False
251
+
252
+ return self.connection
253
+
254
+ def get_placeholder(self) -> str:
255
+ return "?"
256
+
257
+ def get_autoincrement_syntax(self) -> str:
258
+ return "IDENTITY"
259
+
260
+ def supports_returning(self) -> bool:
261
+ return False # MSSQL uses OUTPUT clause instead
262
+
263
+
264
+ def create_backend(db_type: str, credentials: DatabaseCredentials) -> DatabaseBackend:
265
+ """
266
+ Factory function to create appropriate database backend.
267
+
268
+ Args:
269
+ db_type: Database type (sqlite, mysql, mariadb, postgresql, mssql)
270
+ credentials: Database connection credentials
271
+
272
+ Returns:
273
+ Appropriate DatabaseBackend instance
274
+
275
+ Raises:
276
+ ValueError: If database type is not supported
277
+ """
278
+ db_type_lower = db_type.lower()
279
+
280
+ if db_type_lower == 'sqlite':
281
+ return SQLiteBackend(credentials)
282
+ elif db_type_lower in ('mysql', 'mariadb'):
283
+ return MySQLBackend(credentials)
284
+ elif db_type_lower == 'postgresql':
285
+ return PostgreSQLBackend(credentials)
286
+ elif db_type_lower in ('mssql', 'sqlserver', 'mssqlserver'):
287
+ return MSSQLBackend(credentials)
288
+ else:
289
+ raise ValueError(
290
+ f"Unsupported database type: {db_type}. "
291
+ f"Supported types: sqlite, mysql, mariadb, postgresql, mssql"
292
+ )
293
+
294
+
295
+ def validate_credentials(db_type: str, credentials: DatabaseCredentials) -> tuple[bool, Optional[str]]:
296
+ """
297
+ Validate database credentials for given database type.
298
+
299
+ Args:
300
+ db_type: Database type
301
+ credentials: Database credentials
302
+
303
+ Returns:
304
+ Tuple of (is_valid, error_message)
305
+ """
306
+ db_type_lower = db_type.lower()
307
+
308
+ if db_type_lower == 'sqlite':
309
+ if not credentials.path:
310
+ return False, "SQLite database path is required"
311
+ return True, None
312
+
313
+ # Remote databases require connection details
314
+ required_fields = ['host', 'database', 'username', 'password']
315
+
316
+ missing = []
317
+ if not credentials.host:
318
+ missing.append('host')
319
+ if not credentials.database:
320
+ missing.append('database')
321
+ if not credentials.username:
322
+ missing.append('username')
323
+ if not credentials.password:
324
+ missing.append('password')
325
+
326
+ if missing:
327
+ return False, f"Missing required credentials: {', '.join(missing)}"
328
+
329
+ return True, None
@@ -0,0 +1,122 @@
1
+ """
2
+ Database connection management module.
3
+
4
+ This module handles:
5
+ - Multi-database backend support (SQLite, MySQL, PostgreSQL, MSSQL)
6
+ - Database connection setup
7
+ - Connection lifecycle management
8
+ - Directory management for database files
9
+
10
+
11
+ """
12
+
13
+ import sqlite3
14
+ import logging
15
+ import threading
16
+ from pathlib import Path
17
+ from typing import Optional, Any
18
+
19
+ from .backends import (
20
+ DatabaseBackend,
21
+ DatabaseCredentials,
22
+ create_backend,
23
+ validate_credentials
24
+ )
25
+
26
+
27
+ class DatabaseConnection:
28
+ """Manages database connection lifecycle with multi-backend support."""
29
+
30
+ def __init__(self, db_type: str = 'sqlite', credentials: Optional[DatabaseCredentials] = None,
31
+ db_path: Optional[str] = None):
32
+ """
33
+ Initialise the database connection.
34
+
35
+ Args:
36
+ db_type: Database type (sqlite, mysql, mariadb, postgresql, mssql)
37
+ credentials: Database connection credentials
38
+ db_path: Path to SQLite database file (for backward compatibility)
39
+ """
40
+ self.db_type = db_type.lower()
41
+ self._lock = threading.RLock() # Reentrant lock for thread-safe operations
42
+
43
+ # Handle backward compatibility with old SQLite-only constructor
44
+ if credentials is None and db_path:
45
+ credentials = DatabaseCredentials(path=db_path)
46
+ self.db_type = 'sqlite'
47
+
48
+ if credentials is None:
49
+ raise ValueError("Database credentials are required")
50
+
51
+ # Validate credentials
52
+ is_valid, error_msg = validate_credentials(self.db_type, credentials)
53
+ if not is_valid:
54
+ raise ValueError(f"Invalid database credentials: {error_msg}")
55
+
56
+ # Create appropriate backend
57
+ self.backend = create_backend(self.db_type, credentials)
58
+
59
+ # For SQLite, ensure directory exists
60
+ if self.db_type == 'sqlite':
61
+ self._ensure_database_directory(credentials.path)
62
+
63
+ # Establish connection
64
+ self.conn = self.backend.connect()
65
+
66
+ # Configure connection based on backend
67
+ self._configure_connection()
68
+
69
+ logging.info(f"Database connection established: type={self.db_type}")
70
+
71
+ def _ensure_database_directory(self, db_path: str):
72
+ """
73
+ Ensure the directory for the database file exists (SQLite only).
74
+
75
+ Args:
76
+ db_path: Path to database file
77
+ """
78
+ db_dir = Path(db_path).parent
79
+ db_dir.mkdir(parents=True, exist_ok=True)
80
+
81
+ def _configure_connection(self):
82
+ """Configure connection based on database backend."""
83
+ if self.db_type == 'sqlite':
84
+ # Enable WAL (Write-Ahead Logging) mode for better concurrency
85
+ cursor = self.conn.execute("PRAGMA journal_mode=WAL")
86
+ journal_mode = cursor.fetchone()[0]
87
+ cursor.close()
88
+ logging.info(f"SQLite journal mode: {journal_mode}")
89
+
90
+ def get_connection(self) -> Any:
91
+ """
92
+ Get the database connection.
93
+
94
+ Returns:
95
+ Database connection object
96
+ """
97
+ return self.conn
98
+
99
+ def get_backend(self) -> DatabaseBackend:
100
+ """
101
+ Get the database backend.
102
+
103
+ Returns:
104
+ DatabaseBackend instance
105
+ """
106
+ return self.backend
107
+
108
+ def close(self):
109
+ """Close the database connection."""
110
+ if self.conn:
111
+ self.backend.close()
112
+ self.conn = None
113
+ logging.info(f"Database connection closed: type={self.db_type}")
114
+
115
+ def test_connection(self) -> bool:
116
+ """
117
+ Test database connection.
118
+
119
+ Returns:
120
+ True if connection is working
121
+ """
122
+ return self.backend.test_connection()