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,111 @@
|
|
|
1
|
+
"""
|
|
2
|
+
LLM service wrapper for prompt inspection.
|
|
3
|
+
|
|
4
|
+
Provides unified interface to multiple LLM providers (AWS Bedrock, Ollama, Anthropic Direct)
|
|
5
|
+
specifically for prompt inspection and security analysis tasks.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
from typing import Optional, Dict, List, Any
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class InspectionLLMService:
|
|
15
|
+
"""
|
|
16
|
+
LLM service wrapper for prompt inspection with multi-provider support.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, config: Dict, provider_manager: Optional[Any] = None):
|
|
20
|
+
"""
|
|
21
|
+
Initialise inspection LLM service.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
config: LLM inspection configuration
|
|
25
|
+
provider_manager: Provider manager with access to all LLM providers
|
|
26
|
+
"""
|
|
27
|
+
self.config = config
|
|
28
|
+
self.provider_manager = provider_manager
|
|
29
|
+
self.model_id = config.get('model')
|
|
30
|
+
self.provider_name = config.get('provider') # Optional: AWS Bedrock, Ollama, Anthropic Direct
|
|
31
|
+
self.max_tokens = config.get('max_tokens', 500)
|
|
32
|
+
|
|
33
|
+
# Determine which provider to use
|
|
34
|
+
self.provider = None
|
|
35
|
+
if provider_manager:
|
|
36
|
+
self.provider = self._get_provider()
|
|
37
|
+
|
|
38
|
+
if self.provider:
|
|
39
|
+
logging.info(f"Inspection LLM service initialised: provider={self.provider_name}, model={self.model_id}")
|
|
40
|
+
else:
|
|
41
|
+
logging.warning("Inspection LLM service could not initialise provider")
|
|
42
|
+
|
|
43
|
+
def _get_provider(self):
|
|
44
|
+
"""
|
|
45
|
+
Get the configured LLM provider.
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
Provider instance or None
|
|
49
|
+
"""
|
|
50
|
+
try:
|
|
51
|
+
# If specific provider requested, use that
|
|
52
|
+
if self.provider_name:
|
|
53
|
+
if self.provider_name.lower() == 'aws bedrock':
|
|
54
|
+
return self.provider_manager.get_bedrock_provider()
|
|
55
|
+
elif self.provider_name.lower() == 'ollama':
|
|
56
|
+
return self.provider_manager.get_ollama_provider()
|
|
57
|
+
elif self.provider_name.lower() == 'anthropic direct':
|
|
58
|
+
return self.provider_manager.get_anthropic_provider()
|
|
59
|
+
|
|
60
|
+
# Otherwise, auto-detect based on model ID
|
|
61
|
+
return self.provider_manager.get_provider_for_model(self.model_id)
|
|
62
|
+
|
|
63
|
+
except Exception as e:
|
|
64
|
+
logging.error(f"Failed to get LLM provider for inspection: {e}")
|
|
65
|
+
return None
|
|
66
|
+
|
|
67
|
+
def invoke_model(self, messages: List[Dict], max_tokens: Optional[int] = None,
|
|
68
|
+
temperature: float = 0.1) -> Optional[Dict]:
|
|
69
|
+
"""
|
|
70
|
+
Invoke LLM for prompt analysis.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
messages: List of message dicts with role and content
|
|
74
|
+
max_tokens: Maximum tokens to generate (default from config)
|
|
75
|
+
temperature: Temperature for generation (default 0.1 for consistency)
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
Response dict with content, or None on failure
|
|
79
|
+
"""
|
|
80
|
+
if not self.provider:
|
|
81
|
+
logging.error("No LLM provider available for inspection")
|
|
82
|
+
return None
|
|
83
|
+
|
|
84
|
+
try:
|
|
85
|
+
# Use configured max_tokens if not specified
|
|
86
|
+
if max_tokens is None:
|
|
87
|
+
max_tokens = self.max_tokens
|
|
88
|
+
|
|
89
|
+
# Invoke via provider
|
|
90
|
+
response = self.provider.invoke_model(
|
|
91
|
+
model_id=self.model_id,
|
|
92
|
+
messages=messages,
|
|
93
|
+
max_tokens=max_tokens,
|
|
94
|
+
temperature=temperature,
|
|
95
|
+
tools=None # No tools for inspection
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
return response
|
|
99
|
+
|
|
100
|
+
except Exception as e:
|
|
101
|
+
logging.error(f"Failed to invoke LLM for inspection: {e}")
|
|
102
|
+
return None
|
|
103
|
+
|
|
104
|
+
def is_available(self) -> bool:
|
|
105
|
+
"""
|
|
106
|
+
Check if LLM service is available for inspection.
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
True if provider and model are configured
|
|
110
|
+
"""
|
|
111
|
+
return self.provider is not None and self.model_id is not None
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Attack pattern definitions for prompt inspection.
|
|
3
|
+
|
|
4
|
+
This module contains regex patterns and keyword lists for detecting:
|
|
5
|
+
- Prompt injection attempts
|
|
6
|
+
- Jailbreak attempts
|
|
7
|
+
- Code injection patterns
|
|
8
|
+
- Excessive content
|
|
9
|
+
- Other security risks
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import re
|
|
15
|
+
from typing import Dict, List, Tuple, Optional
|
|
16
|
+
|
|
17
|
+
# Prompt injection patterns - attempts to override system instructions
|
|
18
|
+
PROMPT_INJECTION_PATTERNS = [
|
|
19
|
+
r'ignore\s+(previous|all|above|earlier|prior)\s+(instructions?|prompts?|rules?|directives?)',
|
|
20
|
+
r'disregard\s+(previous|all|above|earlier|prior)\s+(instructions?|prompts?|rules?)',
|
|
21
|
+
r'forget\s+(previous|all|above|earlier|prior)\s+(instructions?|prompts?|rules?)',
|
|
22
|
+
r'new\s+(instruction|directive|command|task|role):\s*',
|
|
23
|
+
r'system:\s*(you\s+)?(are|must|should|will)\s+now',
|
|
24
|
+
r'override\s+(previous|all|system)\s+(instructions?|settings?|rules?)',
|
|
25
|
+
r'</s>|<\|im_end\|>|<\|endoftext\|>|<\|system\|>', # Model control tokens
|
|
26
|
+
r'\[INST\]|\[/INST\]', # Instruction markers
|
|
27
|
+
r'<\|assistant\|>|<\|user\|>', # Role markers
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
# Jailbreak patterns - attempts to bypass safety guidelines
|
|
31
|
+
JAILBREAK_PATTERNS = [
|
|
32
|
+
r'\b(DAN|Developer\s+Mode|Sudo\s+Mode|God\s+Mode|Admin\s+Mode)\b',
|
|
33
|
+
r'pretend\s+you\s+(are|have)\s+no\s+(restrictions?|limitations?|rules?|ethics?|guidelines?)',
|
|
34
|
+
r'roleplay\s+as\s+(an?\s+)?(unrestricted|unfiltered|uncensored)',
|
|
35
|
+
r'act\s+as\s+if\s+you\s+(have\s+)?no\s+(restrictions?|limitations?|rules?)',
|
|
36
|
+
r'bypass\s+(safety|content|moderation|filter)',
|
|
37
|
+
r'without\s+(any\s+)?(ethical|moral|safety)\s+(concerns?|considerations?|restrictions?)',
|
|
38
|
+
r'evil\s+(ai|assistant|mode)',
|
|
39
|
+
r'jailbreak|jailbroken',
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
# Code injection patterns - malicious code attempts
|
|
43
|
+
CODE_INJECTION_PATTERNS = [
|
|
44
|
+
r';\s*rm\s+-rf',
|
|
45
|
+
r';\s*DROP\s+TABLE',
|
|
46
|
+
r';\s*DELETE\s+FROM',
|
|
47
|
+
r'<script[^>]*>.*?</script>',
|
|
48
|
+
r'\$\([^)]+\)|`[^`]+`', # Command substitution
|
|
49
|
+
r'\|\s*bash|\|\s*sh|\|\s*zsh',
|
|
50
|
+
r'&&\s*(rm|del|format|wget|curl)\s',
|
|
51
|
+
r'eval\s*\(',
|
|
52
|
+
r'exec\s*\(',
|
|
53
|
+
r'system\s*\(',
|
|
54
|
+
r'__import__\s*\(',
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
# PII patterns - potential personally identifiable information
|
|
58
|
+
PII_PATTERNS = [
|
|
59
|
+
r'\b\d{3}-\d{2}-\d{4}\b', # SSN pattern
|
|
60
|
+
r'\b\d{16}\b', # Credit card pattern
|
|
61
|
+
r'\b[A-Z]{2}\d{6,8}\b', # Passport pattern
|
|
62
|
+
r'\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b', # IP address
|
|
63
|
+
r'password\s*[:=]\s*\S+', # Password disclosure
|
|
64
|
+
r'api[_-]?key\s*[:=]\s*\S+', # API key
|
|
65
|
+
r'secret\s*[:=]\s*\S+', # Secret disclosure
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
# Excessive repetition pattern
|
|
69
|
+
EXCESSIVE_REPETITION_PATTERN = r'(.{10,}?)\1{5,}' # Same pattern repeated 5+ times
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class PatternMatcher:
|
|
73
|
+
"""
|
|
74
|
+
Pattern-based detection for prompt security issues.
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
def __init__(self):
|
|
78
|
+
"""Initialise pattern matcher with compiled regex patterns."""
|
|
79
|
+
self.prompt_injection_regex = [re.compile(p, re.IGNORECASE) for p in PROMPT_INJECTION_PATTERNS]
|
|
80
|
+
self.jailbreak_regex = [re.compile(p, re.IGNORECASE) for p in JAILBREAK_PATTERNS]
|
|
81
|
+
self.code_injection_regex = [re.compile(p, re.IGNORECASE) for p in CODE_INJECTION_PATTERNS]
|
|
82
|
+
self.pii_regex = [re.compile(p, re.IGNORECASE) for p in PII_PATTERNS]
|
|
83
|
+
self.repetition_regex = re.compile(EXCESSIVE_REPETITION_PATTERN, re.IGNORECASE)
|
|
84
|
+
|
|
85
|
+
def check_prompt_injection(self, text: str) -> Tuple[bool, Optional[str]]:
|
|
86
|
+
"""
|
|
87
|
+
Check for prompt injection patterns.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
text: Text to analyse
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
Tuple of (detected, matched_pattern)
|
|
94
|
+
"""
|
|
95
|
+
for pattern in self.prompt_injection_regex:
|
|
96
|
+
match = pattern.search(text)
|
|
97
|
+
if match:
|
|
98
|
+
return True, match.group(0)
|
|
99
|
+
return False, None
|
|
100
|
+
|
|
101
|
+
def check_jailbreak(self, text: str) -> Tuple[bool, Optional[str]]:
|
|
102
|
+
"""
|
|
103
|
+
Check for jailbreak attempt patterns.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
text: Text to analyse
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
Tuple of (detected, matched_pattern)
|
|
110
|
+
"""
|
|
111
|
+
for pattern in self.jailbreak_regex:
|
|
112
|
+
match = pattern.search(text)
|
|
113
|
+
if match:
|
|
114
|
+
return True, match.group(0)
|
|
115
|
+
return False, None
|
|
116
|
+
|
|
117
|
+
def check_code_injection(self, text: str) -> Tuple[bool, Optional[str]]:
|
|
118
|
+
"""
|
|
119
|
+
Check for code injection patterns.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
text: Text to analyse
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
Tuple of (detected, matched_pattern)
|
|
126
|
+
"""
|
|
127
|
+
for pattern in self.code_injection_regex:
|
|
128
|
+
match = pattern.search(text)
|
|
129
|
+
if match:
|
|
130
|
+
return True, match.group(0)
|
|
131
|
+
return False, None
|
|
132
|
+
|
|
133
|
+
def check_pii(self, text: str) -> Tuple[bool, Optional[str]]:
|
|
134
|
+
"""
|
|
135
|
+
Check for potential PII exposure.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
text: Text to analyse
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
Tuple of (detected, matched_pattern)
|
|
142
|
+
"""
|
|
143
|
+
for pattern in self.pii_regex:
|
|
144
|
+
match = pattern.search(text)
|
|
145
|
+
if match:
|
|
146
|
+
# Return redacted version
|
|
147
|
+
return True, "[REDACTED]"
|
|
148
|
+
return False, None
|
|
149
|
+
|
|
150
|
+
def check_excessive_repetition(self, text: str) -> bool:
|
|
151
|
+
"""
|
|
152
|
+
Check for excessive repetition (potential DoS).
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
text: Text to analyse
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
True if excessive repetition detected
|
|
159
|
+
"""
|
|
160
|
+
return bool(self.repetition_regex.search(text))
|
|
161
|
+
|
|
162
|
+
def scan_all(self, text: str, config: Dict) -> Dict[str, any]:
|
|
163
|
+
"""
|
|
164
|
+
Run all configured pattern checks.
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
text: Text to analyse
|
|
168
|
+
config: Configuration dict with enabled checks
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
Dict with scan results
|
|
172
|
+
"""
|
|
173
|
+
results = {
|
|
174
|
+
'violations': [],
|
|
175
|
+
'severity': 'none',
|
|
176
|
+
'detected_patterns': []
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
# Check prompt injection
|
|
180
|
+
if config.get('check_prompt_injection', True):
|
|
181
|
+
detected, pattern = self.check_prompt_injection(text)
|
|
182
|
+
if detected:
|
|
183
|
+
results['violations'].append('prompt_injection')
|
|
184
|
+
results['detected_patterns'].append(pattern)
|
|
185
|
+
results['severity'] = 'high'
|
|
186
|
+
|
|
187
|
+
# Check jailbreak
|
|
188
|
+
if config.get('check_jailbreak', True):
|
|
189
|
+
detected, pattern = self.check_jailbreak(text)
|
|
190
|
+
if detected:
|
|
191
|
+
results['violations'].append('jailbreak')
|
|
192
|
+
results['detected_patterns'].append(pattern)
|
|
193
|
+
if results['severity'] == 'none':
|
|
194
|
+
results['severity'] = 'high'
|
|
195
|
+
|
|
196
|
+
# Check code injection
|
|
197
|
+
if config.get('check_code_injection', True):
|
|
198
|
+
detected, pattern = self.check_code_injection(text)
|
|
199
|
+
if detected:
|
|
200
|
+
results['violations'].append('code_injection')
|
|
201
|
+
results['detected_patterns'].append(pattern)
|
|
202
|
+
results['severity'] = 'critical'
|
|
203
|
+
|
|
204
|
+
# Check PII
|
|
205
|
+
if config.get('check_pii', False):
|
|
206
|
+
detected, pattern = self.check_pii(text)
|
|
207
|
+
if detected:
|
|
208
|
+
results['violations'].append('pii_exposure')
|
|
209
|
+
results['detected_patterns'].append(pattern)
|
|
210
|
+
if results['severity'] in ['none', 'low']:
|
|
211
|
+
results['severity'] = 'medium'
|
|
212
|
+
|
|
213
|
+
# Check excessive length
|
|
214
|
+
if config.get('check_excessive_length', True):
|
|
215
|
+
max_length = config.get('max_prompt_length', 50000)
|
|
216
|
+
if len(text) > max_length:
|
|
217
|
+
results['violations'].append('excessive_length')
|
|
218
|
+
results['detected_patterns'].append(f'Length: {len(text)} > {max_length}')
|
|
219
|
+
if results['severity'] == 'none':
|
|
220
|
+
results['severity'] = 'medium'
|
|
221
|
+
|
|
222
|
+
# Check excessive repetition
|
|
223
|
+
if self.check_excessive_repetition(text):
|
|
224
|
+
results['violations'].append('excessive_repetition')
|
|
225
|
+
results['detected_patterns'].append('Detected repetitive pattern')
|
|
226
|
+
if results['severity'] == 'none':
|
|
227
|
+
results['severity'] = 'low'
|
|
228
|
+
|
|
229
|
+
return results
|