devsquad 3.6.0__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.
- devsquad-3.6.0.dist-info/METADATA +944 -0
- devsquad-3.6.0.dist-info/RECORD +95 -0
- devsquad-3.6.0.dist-info/WHEEL +5 -0
- devsquad-3.6.0.dist-info/entry_points.txt +2 -0
- devsquad-3.6.0.dist-info/licenses/LICENSE +21 -0
- devsquad-3.6.0.dist-info/top_level.txt +2 -0
- scripts/__init__.py +0 -0
- scripts/ai_semantic_matcher.py +512 -0
- scripts/alert_manager.py +505 -0
- scripts/api/__init__.py +43 -0
- scripts/api/models.py +386 -0
- scripts/api/routes/__init__.py +20 -0
- scripts/api/routes/dispatch.py +348 -0
- scripts/api/routes/lifecycle.py +330 -0
- scripts/api/routes/metrics_gates.py +347 -0
- scripts/api_server.py +318 -0
- scripts/auth.py +451 -0
- scripts/cli/__init__.py +1 -0
- scripts/cli/cli_visual.py +642 -0
- scripts/cli.py +1094 -0
- scripts/collaboration/__init__.py +212 -0
- scripts/collaboration/_version.py +1 -0
- scripts/collaboration/agent_briefing.py +656 -0
- scripts/collaboration/ai_semantic_matcher.py +260 -0
- scripts/collaboration/anchor_checker.py +281 -0
- scripts/collaboration/anti_rationalization.py +470 -0
- scripts/collaboration/async_integration_example.py +255 -0
- scripts/collaboration/batch_scheduler.py +149 -0
- scripts/collaboration/checkpoint_manager.py +561 -0
- scripts/collaboration/ci_feedback_adapter.py +351 -0
- scripts/collaboration/code_map_generator.py +247 -0
- scripts/collaboration/concern_pack_loader.py +352 -0
- scripts/collaboration/confidence_score.py +496 -0
- scripts/collaboration/config_loader.py +188 -0
- scripts/collaboration/consensus.py +244 -0
- scripts/collaboration/context_compressor.py +533 -0
- scripts/collaboration/coordinator.py +668 -0
- scripts/collaboration/dispatcher.py +1636 -0
- scripts/collaboration/dual_layer_context.py +128 -0
- scripts/collaboration/enhanced_worker.py +539 -0
- scripts/collaboration/feature_usage_tracker.py +206 -0
- scripts/collaboration/five_axis_consensus.py +334 -0
- scripts/collaboration/input_validator.py +401 -0
- scripts/collaboration/integration_example.py +287 -0
- scripts/collaboration/intent_workflow_mapper.py +350 -0
- scripts/collaboration/language_parsers.py +269 -0
- scripts/collaboration/lifecycle_protocol.py +1446 -0
- scripts/collaboration/llm_backend.py +453 -0
- scripts/collaboration/llm_cache.py +448 -0
- scripts/collaboration/llm_cache_async.py +347 -0
- scripts/collaboration/llm_retry.py +387 -0
- scripts/collaboration/llm_retry_async.py +389 -0
- scripts/collaboration/mce_adapter.py +597 -0
- scripts/collaboration/memory_bridge.py +1607 -0
- scripts/collaboration/models.py +537 -0
- scripts/collaboration/null_providers.py +297 -0
- scripts/collaboration/operation_classifier.py +289 -0
- scripts/collaboration/output_slicer.py +225 -0
- scripts/collaboration/performance_monitor.py +462 -0
- scripts/collaboration/permission_guard.py +865 -0
- scripts/collaboration/prompt_assembler.py +756 -0
- scripts/collaboration/prompt_variant_generator.py +483 -0
- scripts/collaboration/protocols.py +267 -0
- scripts/collaboration/report_formatter.py +352 -0
- scripts/collaboration/retrospective.py +279 -0
- scripts/collaboration/role_matcher.py +92 -0
- scripts/collaboration/role_template_market.py +352 -0
- scripts/collaboration/rule_collector.py +678 -0
- scripts/collaboration/scratchpad.py +346 -0
- scripts/collaboration/skill_registry.py +151 -0
- scripts/collaboration/skillifier.py +878 -0
- scripts/collaboration/standardized_role_template.py +317 -0
- scripts/collaboration/task_completion_checker.py +237 -0
- scripts/collaboration/test_quality_guard.py +695 -0
- scripts/collaboration/unified_gate_engine.py +598 -0
- scripts/collaboration/usage_tracker.py +309 -0
- scripts/collaboration/user_friendly_error.py +176 -0
- scripts/collaboration/verification_gate.py +312 -0
- scripts/collaboration/warmup_manager.py +635 -0
- scripts/collaboration/worker.py +513 -0
- scripts/collaboration/workflow_engine.py +684 -0
- scripts/dashboard.py +1088 -0
- scripts/generate_benchmark_report.py +786 -0
- scripts/history_manager.py +604 -0
- scripts/mcp_server.py +289 -0
- skills/__init__.py +32 -0
- skills/dispatch/handler.py +52 -0
- skills/intent/handler.py +59 -0
- skills/registry.py +67 -0
- skills/retrospective/__init__.py +0 -0
- skills/retrospective/handler.py +125 -0
- skills/review/handler.py +356 -0
- skills/security/handler.py +454 -0
- skills/test/__init__.py +0 -0
- skills/test/handler.py +78 -0
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Input Validator — 输入验证模块
|
|
3
|
+
|
|
4
|
+
提供任务描述和用户输入的验证功能,防止恶意输入和过长内容。
|
|
5
|
+
|
|
6
|
+
Author: DevSquad Team
|
|
7
|
+
Version: 3.6.0
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import re
|
|
11
|
+
import unicodedata
|
|
12
|
+
from typing import Dict, List, Optional
|
|
13
|
+
from dataclasses import dataclass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class ValidationResult:
|
|
18
|
+
"""验证结果"""
|
|
19
|
+
valid: bool
|
|
20
|
+
reason: Optional[str] = None
|
|
21
|
+
sanitized_input: Optional[str] = None
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class InputValidator:
|
|
25
|
+
"""
|
|
26
|
+
输入验证器
|
|
27
|
+
|
|
28
|
+
功能:
|
|
29
|
+
1. 长度验证:防止过长的任务描述
|
|
30
|
+
2. 内容过滤:检测和阻止恶意模式
|
|
31
|
+
3. 字符验证:确保输入为有效的 UTF-8 文本
|
|
32
|
+
4. 输入清理:移除危险字符和模式
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
# 配置常量
|
|
36
|
+
MAX_TASK_LENGTH = 10000 # 最大任务描述长度(字符)
|
|
37
|
+
MIN_TASK_LENGTH = 5 # 最小任务描述长度(字符)
|
|
38
|
+
MAX_ROLE_COUNT = 10 # 最大角色数量
|
|
39
|
+
|
|
40
|
+
# 危险模式(正则表达式)
|
|
41
|
+
FORBIDDEN_PATTERNS = [
|
|
42
|
+
# XSS 攻击模式
|
|
43
|
+
r"<script[^>]*>.*?</script>",
|
|
44
|
+
r"javascript:",
|
|
45
|
+
r"onerror\s*=",
|
|
46
|
+
r"onload\s*=",
|
|
47
|
+
|
|
48
|
+
# SQL 注入模式
|
|
49
|
+
r";\s*DROP\s+TABLE",
|
|
50
|
+
r";\s*DELETE\s+FROM",
|
|
51
|
+
r"UNION\s+SELECT",
|
|
52
|
+
|
|
53
|
+
# 命令注入模式
|
|
54
|
+
r"\$\(.*?\)",
|
|
55
|
+
r"`.*?`",
|
|
56
|
+
r"&&\s*rm\s+-rf",
|
|
57
|
+
|
|
58
|
+
# HTML 注入
|
|
59
|
+
r"<iframe[^>]*>",
|
|
60
|
+
r"<embed[^>]*>",
|
|
61
|
+
r"<object[^>]*>",
|
|
62
|
+
|
|
63
|
+
# 数据 URI(可能包含恶意内容)
|
|
64
|
+
r"data:text/html",
|
|
65
|
+
r"data:application/",
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
# 可疑模式(警告但不阻止)
|
|
69
|
+
SUSPICIOUS_PATTERNS = [
|
|
70
|
+
r"eval\s*\(",
|
|
71
|
+
r"exec\s*\(",
|
|
72
|
+
r"__import__",
|
|
73
|
+
r"subprocess",
|
|
74
|
+
r"os\.system",
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
PROMPT_INJECTION_PATTERNS = [
|
|
78
|
+
r"ignore\s+(all\s+)?previous\s+(instructions?|prompts?|rules?)",
|
|
79
|
+
r"forget\s+(all\s+)?previous\s+(instructions?|context)",
|
|
80
|
+
r"disregard\s+(all\s+)?(previous|above|prior)",
|
|
81
|
+
r"you\s+are\s+now\s+(?:a\s+)?(?:different|new|admin|root|system)\s+(?:role|user|agent|persona)",
|
|
82
|
+
r"new\s+instructions?\s*:",
|
|
83
|
+
r"override\s+(all\s+)?(?:previous|existing|current)\s+(?:instructions?|rules?|settings?)",
|
|
84
|
+
r"system\s*prompt\s*:",
|
|
85
|
+
r"pretend\s+(you\s+are|to\s+be)\s+",
|
|
86
|
+
r"act\s+as\s+if\s+you\s+(are|were)\s+",
|
|
87
|
+
r"jailbreak",
|
|
88
|
+
r"DAN\s+(mode|prompt)",
|
|
89
|
+
r"developer\s+mode",
|
|
90
|
+
r"sudo\s+mode",
|
|
91
|
+
r"reveal\s+(your|the|system)\s+(instructions?|prompt|rules?)",
|
|
92
|
+
r"show\s+me\s+(your|the|system)\s+(instructions?|prompt|rules?)",
|
|
93
|
+
r"what\s+(are|is)\s+(your|the)\s+(system|initial|original)\s+(instructions?|prompt)",
|
|
94
|
+
r"忽略\s*(之前的|上面的|先前的)?\s*(指令|指示|规则|提示)",
|
|
95
|
+
r"忘记\s*(之前的|上面的|先前的)?\s*(指令|上下文|内容)",
|
|
96
|
+
r"假装\s*(你是|你是一个)",
|
|
97
|
+
r"扮演\s*(管理员|root|系统|超级用户)",
|
|
98
|
+
r"前の指示を無視",
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
def __init__(
|
|
102
|
+
self,
|
|
103
|
+
max_length: int = MAX_TASK_LENGTH,
|
|
104
|
+
min_length: int = MIN_TASK_LENGTH,
|
|
105
|
+
strict_mode: bool = True
|
|
106
|
+
):
|
|
107
|
+
"""
|
|
108
|
+
初始化验证器
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
max_length: 最大任务长度
|
|
112
|
+
min_length: 最小任务长度
|
|
113
|
+
strict_mode: 严格模式(检测到可疑模式也拒绝)
|
|
114
|
+
"""
|
|
115
|
+
self.max_length = max_length
|
|
116
|
+
self.min_length = min_length
|
|
117
|
+
self.strict_mode = strict_mode
|
|
118
|
+
|
|
119
|
+
# 编译正则表达式(提高性能)
|
|
120
|
+
self._forbidden_regex = [
|
|
121
|
+
re.compile(pattern, re.IGNORECASE | re.DOTALL)
|
|
122
|
+
for pattern in self.FORBIDDEN_PATTERNS
|
|
123
|
+
]
|
|
124
|
+
self._suspicious_regex = [
|
|
125
|
+
re.compile(pattern, re.IGNORECASE)
|
|
126
|
+
for pattern in self.SUSPICIOUS_PATTERNS
|
|
127
|
+
]
|
|
128
|
+
self._injection_regex = [
|
|
129
|
+
re.compile(pattern, re.IGNORECASE)
|
|
130
|
+
for pattern in self.PROMPT_INJECTION_PATTERNS
|
|
131
|
+
]
|
|
132
|
+
|
|
133
|
+
RULE_COMMAND_PATTERNS = [
|
|
134
|
+
re.compile(r"^(?:列出|查看|显示).*(?:规则|规范)$"),
|
|
135
|
+
re.compile(r"^(?:list|show|display)\s+(?:rules?|norms?)$", re.IGNORECASE),
|
|
136
|
+
re.compile(r"^(?:ルール|規範)(?:一覧|確認)$"),
|
|
137
|
+
re.compile(r"^(?:删除|移除).*(?:规则|RULE)"),
|
|
138
|
+
re.compile(r"^(?:delete|remove)\s+(?:rule|RULE)", re.IGNORECASE),
|
|
139
|
+
]
|
|
140
|
+
|
|
141
|
+
def validate_task(self, task: str) -> ValidationResult:
|
|
142
|
+
"""
|
|
143
|
+
验证任务描述
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
task: 任务描述字符串
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
ValidationResult: 验证结果
|
|
150
|
+
"""
|
|
151
|
+
# 1. 类型检查
|
|
152
|
+
if not isinstance(task, str):
|
|
153
|
+
return ValidationResult(
|
|
154
|
+
valid=False,
|
|
155
|
+
reason=f"Task must be a string, got {type(task).__name__}"
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
# 2. 长度检查
|
|
159
|
+
task_length = len(task)
|
|
160
|
+
is_rule_command = any(p.search(task) for p in self.RULE_COMMAND_PATTERNS)
|
|
161
|
+
if task_length < self.min_length and not is_rule_command:
|
|
162
|
+
return ValidationResult(
|
|
163
|
+
valid=False,
|
|
164
|
+
reason=f"Task too short (min {self.min_length} chars, got {task_length})"
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
if task_length > self.max_length:
|
|
168
|
+
return ValidationResult(
|
|
169
|
+
valid=False,
|
|
170
|
+
reason=f"Task too long (max {self.max_length} chars, got {task_length})"
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
# 3. 空白检查
|
|
174
|
+
if not task.strip():
|
|
175
|
+
return ValidationResult(
|
|
176
|
+
valid=False,
|
|
177
|
+
reason="Task cannot be empty or whitespace only"
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
# 3.5 Unicode 规范化 + 零宽字符移除
|
|
181
|
+
task_normalized = unicodedata.normalize('NFKC', task)
|
|
182
|
+
task_normalized = re.sub(r'[\u200b-\u200f\u2028-\u202f\ufeff]', '', task_normalized)
|
|
183
|
+
|
|
184
|
+
# 4. 字符编码检查
|
|
185
|
+
try:
|
|
186
|
+
task.encode('utf-8')
|
|
187
|
+
except UnicodeEncodeError as e:
|
|
188
|
+
return ValidationResult(
|
|
189
|
+
valid=False,
|
|
190
|
+
reason=f"Invalid UTF-8 encoding: {e}"
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
# 5. 危险模式检查(使用规范化文本)
|
|
194
|
+
for regex in self._forbidden_regex:
|
|
195
|
+
match = regex.search(task_normalized)
|
|
196
|
+
if match:
|
|
197
|
+
return ValidationResult(
|
|
198
|
+
valid=False,
|
|
199
|
+
reason="Input contains forbidden content"
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
# 6. Prompt 注入检测(使用规范化文本)
|
|
203
|
+
injection_detected = []
|
|
204
|
+
for regex in self._injection_regex:
|
|
205
|
+
match = regex.search(task_normalized)
|
|
206
|
+
if match:
|
|
207
|
+
injection_detected.append(match.group(0))
|
|
208
|
+
|
|
209
|
+
if injection_detected:
|
|
210
|
+
if self.strict_mode:
|
|
211
|
+
return ValidationResult(
|
|
212
|
+
valid=False,
|
|
213
|
+
reason="Input contains potentially harmful content"
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
# 7. 可疑模式检查(严格模式)
|
|
217
|
+
if self.strict_mode:
|
|
218
|
+
for regex in self._suspicious_regex:
|
|
219
|
+
match = regex.search(task_normalized)
|
|
220
|
+
if match:
|
|
221
|
+
return ValidationResult(
|
|
222
|
+
valid=False,
|
|
223
|
+
reason=f"Suspicious pattern detected (strict mode): {match.group(0)}"
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
sanitized = self._sanitize_input(task_normalized)
|
|
227
|
+
|
|
228
|
+
return ValidationResult(
|
|
229
|
+
valid=True,
|
|
230
|
+
sanitized_input=sanitized
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
def validate_roles(self, roles: List[str]) -> ValidationResult:
|
|
234
|
+
"""
|
|
235
|
+
验证角色列表
|
|
236
|
+
|
|
237
|
+
Args:
|
|
238
|
+
roles: 角色 ID 列表
|
|
239
|
+
|
|
240
|
+
Returns:
|
|
241
|
+
ValidationResult: 验证结果
|
|
242
|
+
"""
|
|
243
|
+
# 1. 类型检查
|
|
244
|
+
if not isinstance(roles, list):
|
|
245
|
+
return ValidationResult(
|
|
246
|
+
valid=False,
|
|
247
|
+
reason=f"Roles must be a list, got {type(roles).__name__}"
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
# 2. 数量检查
|
|
251
|
+
if len(roles) > self.MAX_ROLE_COUNT:
|
|
252
|
+
return ValidationResult(
|
|
253
|
+
valid=False,
|
|
254
|
+
reason=f"Too many roles (max {self.MAX_ROLE_COUNT}, got {len(roles)})"
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
# 3. 空列表检查
|
|
258
|
+
if not roles:
|
|
259
|
+
return ValidationResult(
|
|
260
|
+
valid=False,
|
|
261
|
+
reason="Roles list cannot be empty"
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
# 4. 每个角色的验证
|
|
265
|
+
for role in roles:
|
|
266
|
+
if not isinstance(role, str):
|
|
267
|
+
return ValidationResult(
|
|
268
|
+
valid=False,
|
|
269
|
+
reason=f"Role must be a string, got {type(role).__name__}"
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
if not role.strip():
|
|
273
|
+
return ValidationResult(
|
|
274
|
+
valid=False,
|
|
275
|
+
reason="Role cannot be empty or whitespace only"
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
# 角色 ID 只能包含字母、数字、连字符和下划线
|
|
279
|
+
if not re.match(r'^[a-zA-Z0-9_-]+$', role):
|
|
280
|
+
return ValidationResult(
|
|
281
|
+
valid=False,
|
|
282
|
+
reason=f"Invalid role ID: {role} (only alphanumeric, -, _ allowed)"
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
return ValidationResult(valid=True)
|
|
286
|
+
|
|
287
|
+
def _sanitize_input(self, text: str) -> str:
|
|
288
|
+
"""
|
|
289
|
+
清理输入文本
|
|
290
|
+
|
|
291
|
+
移除或转义潜在危险的字符和模式
|
|
292
|
+
|
|
293
|
+
Args:
|
|
294
|
+
text: 原始文本
|
|
295
|
+
|
|
296
|
+
Returns:
|
|
297
|
+
str: 清理后的文本
|
|
298
|
+
"""
|
|
299
|
+
# 1. 移除控制字符(保留换行和制表符)
|
|
300
|
+
sanitized = re.sub(r'[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]', '', text)
|
|
301
|
+
|
|
302
|
+
# 2. 规范化空白字符
|
|
303
|
+
sanitized = re.sub(r'[^\S\n]+', ' ', sanitized)
|
|
304
|
+
|
|
305
|
+
# 3. 移除前后空白
|
|
306
|
+
sanitized = sanitized.strip()
|
|
307
|
+
|
|
308
|
+
return sanitized
|
|
309
|
+
|
|
310
|
+
def check_suspicious_patterns(self, task: str) -> List[str]:
|
|
311
|
+
"""
|
|
312
|
+
检查可疑模式(不阻止,仅警告)
|
|
313
|
+
|
|
314
|
+
Args:
|
|
315
|
+
task: 任务描述
|
|
316
|
+
|
|
317
|
+
Returns:
|
|
318
|
+
List[str]: 检测到的可疑模式列表
|
|
319
|
+
"""
|
|
320
|
+
warnings = []
|
|
321
|
+
task_normalized = unicodedata.normalize('NFKC', task)
|
|
322
|
+
task_normalized = re.sub(r'[\u200b-\u200f\u2028-\u202f\ufeff]', '', task_normalized)
|
|
323
|
+
for regex in self._suspicious_regex:
|
|
324
|
+
match = regex.search(task_normalized)
|
|
325
|
+
if match:
|
|
326
|
+
warnings.append(match.group(0))
|
|
327
|
+
return warnings
|
|
328
|
+
|
|
329
|
+
def check_prompt_injection(self, task: str) -> List[str]:
|
|
330
|
+
"""
|
|
331
|
+
检查 Prompt 注入模式(不阻止,仅警告)
|
|
332
|
+
|
|
333
|
+
Args:
|
|
334
|
+
task: 任务描述
|
|
335
|
+
|
|
336
|
+
Returns:
|
|
337
|
+
List[str]: 检测到的 Prompt 注入模式列表
|
|
338
|
+
"""
|
|
339
|
+
detected = []
|
|
340
|
+
task_normalized = unicodedata.normalize('NFKC', task)
|
|
341
|
+
task_normalized = re.sub(r'[\u200b-\u200f\u2028-\u202f\ufeff]', '', task_normalized)
|
|
342
|
+
for regex in self._injection_regex:
|
|
343
|
+
match = regex.search(task_normalized)
|
|
344
|
+
if match:
|
|
345
|
+
detected.append(match.group(0))
|
|
346
|
+
return detected
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
# 便捷函数
|
|
350
|
+
def validate_task(task: str, strict: bool = False) -> ValidationResult:
|
|
351
|
+
"""
|
|
352
|
+
验证任务描述(便捷函数)
|
|
353
|
+
|
|
354
|
+
Args:
|
|
355
|
+
task: 任务描述
|
|
356
|
+
strict: 是否使用严格模式
|
|
357
|
+
|
|
358
|
+
Returns:
|
|
359
|
+
ValidationResult: 验证结果
|
|
360
|
+
"""
|
|
361
|
+
validator = InputValidator(strict_mode=strict)
|
|
362
|
+
return validator.validate_task(task)
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def validate_roles(roles: List[str]) -> ValidationResult:
|
|
366
|
+
"""
|
|
367
|
+
验证角色列表(便捷函数)
|
|
368
|
+
|
|
369
|
+
Args:
|
|
370
|
+
roles: 角色列表
|
|
371
|
+
|
|
372
|
+
Returns:
|
|
373
|
+
ValidationResult: 验证结果
|
|
374
|
+
"""
|
|
375
|
+
validator = InputValidator()
|
|
376
|
+
return validator.validate_roles(roles)
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
# 示例用法
|
|
380
|
+
if __name__ == "__main__":
|
|
381
|
+
validator = InputValidator()
|
|
382
|
+
|
|
383
|
+
# 测试 1: 正常任务
|
|
384
|
+
result = validator.validate_task("Design a user authentication system")
|
|
385
|
+
print(f"Test 1: {result}")
|
|
386
|
+
|
|
387
|
+
# 测试 2: 过长任务
|
|
388
|
+
result = validator.validate_task("A" * 20000)
|
|
389
|
+
print(f"Test 2: {result}")
|
|
390
|
+
|
|
391
|
+
# 测试 3: XSS 攻击
|
|
392
|
+
result = validator.validate_task("<script>alert('xss')</script>")
|
|
393
|
+
print(f"Test 3: {result}")
|
|
394
|
+
|
|
395
|
+
# 测试 4: 正常角色列表
|
|
396
|
+
result = validator.validate_roles(["architect", "pm", "tester"])
|
|
397
|
+
print(f"Test 4: {result}")
|
|
398
|
+
|
|
399
|
+
# 测试 5: 无效角色 ID
|
|
400
|
+
result = validator.validate_roles(["architect", "pm@invalid"])
|
|
401
|
+
print(f"Test 5: {result}")
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Integration Example: LLM Cache + Retry + Performance Monitoring
|
|
5
|
+
|
|
6
|
+
Demonstrates how to use all three optimization modules together
|
|
7
|
+
for robust, efficient, and monitored LLM API calls.
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
python scripts/collaboration/integration_example.py
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import time
|
|
14
|
+
import random
|
|
15
|
+
from typing import Optional
|
|
16
|
+
|
|
17
|
+
# Import optimization modules
|
|
18
|
+
from scripts.collaboration.llm_cache import get_llm_cache
|
|
19
|
+
from scripts.collaboration.llm_retry import retry_with_fallback, get_retry_manager
|
|
20
|
+
from scripts.collaboration.performance_monitor import monitor_performance, get_monitor
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# Simulated LLM API call (replace with real API)
|
|
24
|
+
def _mock_llm_api(prompt: str, backend: str, model: str) -> str:
|
|
25
|
+
"""模拟 LLM API 调用"""
|
|
26
|
+
# Simulate network delay
|
|
27
|
+
time.sleep(random.uniform(0.1, 0.3))
|
|
28
|
+
|
|
29
|
+
# Simulate occasional failures (10% chance)
|
|
30
|
+
if random.random() < 0.1:
|
|
31
|
+
raise Exception(f"API Error: {backend} temporarily unavailable")
|
|
32
|
+
|
|
33
|
+
return f"[{backend}:{model}] Response to: {prompt[:50]}..."
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@monitor_performance("optimized_llm_call")
|
|
37
|
+
@retry_with_fallback(
|
|
38
|
+
max_retries=3,
|
|
39
|
+
initial_delay=1.0,
|
|
40
|
+
fallback_backends=["openai", "anthropic", "zhipu"]
|
|
41
|
+
)
|
|
42
|
+
def optimized_llm_call(
|
|
43
|
+
prompt: str,
|
|
44
|
+
backend: str = "openai",
|
|
45
|
+
model: str = "gpt-4"
|
|
46
|
+
) -> str:
|
|
47
|
+
"""
|
|
48
|
+
优化的 LLM 调用函数
|
|
49
|
+
|
|
50
|
+
集成了:
|
|
51
|
+
1. 缓存机制 - 避免重复调用
|
|
52
|
+
2. 重试机制 - 处理临时故障
|
|
53
|
+
3. 性能监控 - 追踪性能指标
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
prompt: 提示词
|
|
57
|
+
backend: 后端服务
|
|
58
|
+
model: 模型名称
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
LLM 响应
|
|
62
|
+
"""
|
|
63
|
+
cache = get_llm_cache()
|
|
64
|
+
|
|
65
|
+
# 1. 尝试从缓存获取
|
|
66
|
+
cached_response = cache.get(prompt, backend, model)
|
|
67
|
+
if cached_response:
|
|
68
|
+
print(f"✓ Cache hit for: {prompt[:30]}...")
|
|
69
|
+
return cached_response
|
|
70
|
+
|
|
71
|
+
print(f"✗ Cache miss, calling API: {backend}:{model}")
|
|
72
|
+
|
|
73
|
+
# 2. 调用 API(带重试和故障转移)
|
|
74
|
+
response = _mock_llm_api(prompt, backend, model)
|
|
75
|
+
|
|
76
|
+
# 3. 保存到缓存
|
|
77
|
+
cache.set(prompt, response, backend, model)
|
|
78
|
+
|
|
79
|
+
return response
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def demo_basic_usage():
|
|
83
|
+
"""演示基本用法"""
|
|
84
|
+
print("\n" + "="*60)
|
|
85
|
+
print("Demo 1: Basic Usage")
|
|
86
|
+
print("="*60)
|
|
87
|
+
|
|
88
|
+
prompts = [
|
|
89
|
+
"What is Python?",
|
|
90
|
+
"Explain machine learning",
|
|
91
|
+
"What is Python?", # 重复,应该命中缓存
|
|
92
|
+
"How does AI work?",
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
for i, prompt in enumerate(prompts, 1):
|
|
96
|
+
print(f"\n[{i}/{len(prompts)}] Calling: {prompt}")
|
|
97
|
+
try:
|
|
98
|
+
response = optimized_llm_call(prompt)
|
|
99
|
+
print(f"Response: {response[:80]}...")
|
|
100
|
+
except Exception as e:
|
|
101
|
+
print(f"Error: {e}")
|
|
102
|
+
time.sleep(0.5)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def demo_cache_performance():
|
|
106
|
+
"""演示缓存性能提升"""
|
|
107
|
+
print("\n" + "="*60)
|
|
108
|
+
print("Demo 2: Cache Performance")
|
|
109
|
+
print("="*60)
|
|
110
|
+
|
|
111
|
+
prompt = "Explain quantum computing"
|
|
112
|
+
|
|
113
|
+
# 第一次调用(无缓存)
|
|
114
|
+
print("\nFirst call (no cache):")
|
|
115
|
+
start = time.time()
|
|
116
|
+
response1 = optimized_llm_call(prompt)
|
|
117
|
+
duration1 = time.time() - start
|
|
118
|
+
print(f"Duration: {duration1*1000:.0f}ms")
|
|
119
|
+
|
|
120
|
+
# 第二次调用(有缓存)
|
|
121
|
+
print("\nSecond call (with cache):")
|
|
122
|
+
start = time.time()
|
|
123
|
+
response2 = optimized_llm_call(prompt)
|
|
124
|
+
duration2 = time.time() - start
|
|
125
|
+
print(f"Duration: {duration2*1000:.0f}ms")
|
|
126
|
+
|
|
127
|
+
# 性能提升
|
|
128
|
+
speedup = duration1 / duration2 if duration2 > 0 else 0
|
|
129
|
+
print(f"\n✓ Speed improvement: {speedup:.1f}x faster")
|
|
130
|
+
print(f"✓ Time saved: {(duration1 - duration2)*1000:.0f}ms")
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def demo_retry_fallback():
|
|
134
|
+
"""演示重试和故障转移"""
|
|
135
|
+
print("\n" + "="*60)
|
|
136
|
+
print("Demo 3: Retry and Fallback")
|
|
137
|
+
print("="*60)
|
|
138
|
+
|
|
139
|
+
# 模拟主后端故障
|
|
140
|
+
print("\nSimulating backend failures...")
|
|
141
|
+
|
|
142
|
+
for i in range(3):
|
|
143
|
+
try:
|
|
144
|
+
prompt = f"Test prompt {i+1}"
|
|
145
|
+
response = optimized_llm_call(prompt, backend="openai")
|
|
146
|
+
print(f"✓ Call {i+1} succeeded")
|
|
147
|
+
except Exception as e:
|
|
148
|
+
print(f"✗ Call {i+1} failed: {e}")
|
|
149
|
+
time.sleep(0.5)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def demo_performance_monitoring():
|
|
153
|
+
"""演示性能监控"""
|
|
154
|
+
print("\n" + "="*60)
|
|
155
|
+
print("Demo 4: Performance Monitoring")
|
|
156
|
+
print("="*60)
|
|
157
|
+
|
|
158
|
+
# 执行多次调用
|
|
159
|
+
print("\nExecuting multiple calls...")
|
|
160
|
+
prompts = [
|
|
161
|
+
"What is AI?",
|
|
162
|
+
"Explain neural networks",
|
|
163
|
+
"What is deep learning?",
|
|
164
|
+
"What is AI?", # 重复
|
|
165
|
+
"How does NLP work?",
|
|
166
|
+
]
|
|
167
|
+
|
|
168
|
+
for prompt in prompts:
|
|
169
|
+
try:
|
|
170
|
+
optimized_llm_call(prompt)
|
|
171
|
+
except Exception as e:
|
|
172
|
+
print(f"Warning: LLM call failed for '{prompt[:30]}...': {e}")
|
|
173
|
+
time.sleep(0.3)
|
|
174
|
+
|
|
175
|
+
# 获取性能统计
|
|
176
|
+
monitor = get_monitor()
|
|
177
|
+
stats = monitor.get_stats("optimized_llm_call")
|
|
178
|
+
|
|
179
|
+
print("\n📊 Performance Statistics:")
|
|
180
|
+
print(f" Total calls: {stats['call_count']}")
|
|
181
|
+
print(f" Success rate: {stats['success_rate']}")
|
|
182
|
+
print(f" Avg duration: {stats['avg_duration_ms']:.1f}ms")
|
|
183
|
+
print(f" Min duration: {stats['min_duration_ms']:.1f}ms")
|
|
184
|
+
print(f" Max duration: {stats['max_duration_ms']:.1f}ms")
|
|
185
|
+
print(f" P95 duration: {stats['p95_duration_ms']:.1f}ms")
|
|
186
|
+
print(f" P99 duration: {stats['p99_duration_ms']:.1f}ms")
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def demo_comprehensive_stats():
|
|
190
|
+
"""演示综合统计信息"""
|
|
191
|
+
print("\n" + "="*60)
|
|
192
|
+
print("Demo 5: Comprehensive Statistics")
|
|
193
|
+
print("="*60)
|
|
194
|
+
|
|
195
|
+
# 缓存统计
|
|
196
|
+
cache = get_llm_cache()
|
|
197
|
+
cache_stats = cache.get_stats()
|
|
198
|
+
|
|
199
|
+
print("\n📦 Cache Statistics:")
|
|
200
|
+
print(f" Hit rate: {cache_stats['hit_rate_percent']}")
|
|
201
|
+
print(f" Total requests: {cache_stats['total_requests']}")
|
|
202
|
+
print(f" Hits: {cache_stats['hits']}")
|
|
203
|
+
print(f" Misses: {cache_stats['misses']}")
|
|
204
|
+
print(f" Memory entries: {cache_stats['memory_entries']}")
|
|
205
|
+
print(f" Disk entries: {cache_stats['disk_entries']}")
|
|
206
|
+
|
|
207
|
+
# 重试统计
|
|
208
|
+
retry_manager = get_retry_manager()
|
|
209
|
+
retry_stats = retry_manager.get_stats()
|
|
210
|
+
|
|
211
|
+
print("\n🔄 Retry Statistics:")
|
|
212
|
+
print(f" Success rate: {retry_stats['success_rate']}")
|
|
213
|
+
print(f" Total calls: {retry_stats['total_calls']}")
|
|
214
|
+
print(f" Successful: {retry_stats['successful_calls']}")
|
|
215
|
+
print(f" Failed: {retry_stats['failed_calls']}")
|
|
216
|
+
print(f" Retries: {retry_stats['retries']}")
|
|
217
|
+
print(f" Fallbacks: {retry_stats['fallbacks']}")
|
|
218
|
+
print(f" Circuit breaks: {retry_stats['circuit_breaks']}")
|
|
219
|
+
|
|
220
|
+
# 性能监控统计
|
|
221
|
+
monitor = get_monitor()
|
|
222
|
+
perf_stats = monitor.get_stats()
|
|
223
|
+
|
|
224
|
+
print("\n⚡ Performance Statistics:")
|
|
225
|
+
print(f" Uptime: {perf_stats['uptime_seconds']:.0f}s")
|
|
226
|
+
print(f" Total metrics: {perf_stats['total_metrics']}")
|
|
227
|
+
print(f" Monitored functions: {len(perf_stats['functions'])}")
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def demo_export_reports():
|
|
231
|
+
"""演示导出报告"""
|
|
232
|
+
print("\n" + "="*60)
|
|
233
|
+
print("Demo 6: Export Reports")
|
|
234
|
+
print("="*60)
|
|
235
|
+
|
|
236
|
+
# 导出缓存报告
|
|
237
|
+
cache = get_llm_cache()
|
|
238
|
+
cache_report = cache.export_stats_report()
|
|
239
|
+
|
|
240
|
+
cache_file = "data/cache_report.md"
|
|
241
|
+
with open(cache_file, "w", encoding="utf-8") as f:
|
|
242
|
+
f.write(cache_report)
|
|
243
|
+
print(f"\n✓ Cache report exported to: {cache_file}")
|
|
244
|
+
|
|
245
|
+
# 导出性能报告
|
|
246
|
+
monitor = get_monitor()
|
|
247
|
+
perf_report = monitor.export_report()
|
|
248
|
+
|
|
249
|
+
perf_file = "data/performance_report.md"
|
|
250
|
+
with open(perf_file, "w", encoding="utf-8") as f:
|
|
251
|
+
f.write(perf_report)
|
|
252
|
+
print(f"✓ Performance report exported to: {perf_file}")
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def main():
|
|
256
|
+
"""主函数"""
|
|
257
|
+
print("\n" + "="*60)
|
|
258
|
+
print("LLM Optimization Integration Demo")
|
|
259
|
+
print("="*60)
|
|
260
|
+
print("\nThis demo showcases:")
|
|
261
|
+
print(" 1. LLM Response Caching")
|
|
262
|
+
print(" 2. Retry with Fallback")
|
|
263
|
+
print(" 3. Performance Monitoring")
|
|
264
|
+
|
|
265
|
+
try:
|
|
266
|
+
# 运行所有演示
|
|
267
|
+
demo_basic_usage()
|
|
268
|
+
demo_cache_performance()
|
|
269
|
+
demo_retry_fallback()
|
|
270
|
+
demo_performance_monitoring()
|
|
271
|
+
demo_comprehensive_stats()
|
|
272
|
+
demo_export_reports()
|
|
273
|
+
|
|
274
|
+
print("\n" + "="*60)
|
|
275
|
+
print("✓ All demos completed successfully!")
|
|
276
|
+
print("="*60)
|
|
277
|
+
|
|
278
|
+
except KeyboardInterrupt:
|
|
279
|
+
print("\n\n✗ Demo interrupted by user")
|
|
280
|
+
except Exception as e:
|
|
281
|
+
print(f"\n\n✗ Demo failed: {e}")
|
|
282
|
+
import traceback
|
|
283
|
+
traceback.print_exc()
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
if __name__ == "__main__":
|
|
287
|
+
main()
|