acra-cli 0.1.1__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.
- acra/__init__.py +7 -0
- acra/__main__.py +4 -0
- acra/agents/__init__.py +0 -0
- acra/agents/__pycache__/__init__.cpython-312.pyc +0 -0
- acra/agents/__pycache__/llm.cpython-312.pyc +0 -0
- acra/agents/__pycache__/llm_utils.cpython-312.pyc +0 -0
- acra/agents/coder/__init__.py +0 -0
- acra/agents/coder/__pycache__/__init__.cpython-312.pyc +0 -0
- acra/agents/coder/__pycache__/coder_agent.cpython-312.pyc +0 -0
- acra/agents/coder/__pycache__/coder_chain.cpython-312.pyc +0 -0
- acra/agents/coder/coder_agent.py +235 -0
- acra/agents/coder/coder_chain.py +204 -0
- acra/agents/critic/__init__.py +0 -0
- acra/agents/critic/__pycache__/__init__.cpython-312.pyc +0 -0
- acra/agents/critic/__pycache__/critic_agent.cpython-312.pyc +0 -0
- acra/agents/critic/__pycache__/critic_chain.cpython-312.pyc +0 -0
- acra/agents/critic/critic_agent.py +199 -0
- acra/agents/critic/critic_chain.py +268 -0
- acra/agents/executor/__init__.py +0 -0
- acra/agents/executor/__pycache__/__init__.cpython-312.pyc +0 -0
- acra/agents/executor/__pycache__/docker_runner.cpython-312.pyc +0 -0
- acra/agents/executor/__pycache__/executor_agent.cpython-312.pyc +0 -0
- acra/agents/executor/__pycache__/sandbox_runner.cpython-312.pyc +0 -0
- acra/agents/executor/docker_runner.py +114 -0
- acra/agents/executor/executor_agent.py +142 -0
- acra/agents/executor/sandbox_runner.py +144 -0
- acra/agents/human/__init__.py +0 -0
- acra/agents/human/__pycache__/__init__.cpython-312.pyc +0 -0
- acra/agents/human/__pycache__/human_node.cpython-312.pyc +0 -0
- acra/agents/human/human_node.py +37 -0
- acra/agents/llm.py +263 -0
- acra/agents/llm_utils.py +161 -0
- acra/agents/memory/__init__.py +0 -0
- acra/agents/memory/__pycache__/__init__.cpython-312.pyc +0 -0
- acra/agents/memory/__pycache__/memory_agent.cpython-312.pyc +0 -0
- acra/agents/memory/__pycache__/memory_manager.cpython-312.pyc +0 -0
- acra/agents/memory/__pycache__/retrieval.cpython-312.pyc +0 -0
- acra/agents/memory/memory_agent.py +239 -0
- acra/agents/memory/memory_manager.py +273 -0
- acra/agents/memory/retrieval.py +355 -0
- acra/agents/planner/__init__.py +0 -0
- acra/agents/planner/__pycache__/__init__.cpython-312.pyc +0 -0
- acra/agents/planner/__pycache__/planner_agent.cpython-312.pyc +0 -0
- acra/agents/planner/__pycache__/planner_chain.cpython-312.pyc +0 -0
- acra/agents/planner/planner_agent.py +97 -0
- acra/agents/planner/planner_chain.py +160 -0
- acra/agents/researcher/__init__.py +1 -0
- acra/agents/researcher/__pycache__/__init__.cpython-312.pyc +0 -0
- acra/agents/researcher/__pycache__/researcher_agent.cpython-312.pyc +0 -0
- acra/agents/researcher/__pycache__/researcher_chain.cpython-312.pyc +0 -0
- acra/agents/researcher/researcher_agent.py +231 -0
- acra/agents/researcher/researcher_chain.py +193 -0
- acra/brain/__init__.py +6 -0
- acra/brain/model_registry.py +19 -0
- acra/brain/provider_manager.py +21 -0
- acra/cli.py +52 -0
- acra/commands/__init__.py +29 -0
- acra/commands/ask.py +49 -0
- acra/commands/brain.py +58 -0
- acra/commands/config.py +25 -0
- acra/commands/context.py +30 -0
- acra/commands/graph.py +18 -0
- acra/commands/keys.py +40 -0
- acra/commands/logs.py +13 -0
- acra/commands/memory.py +27 -0
- acra/commands/plugin.py +18 -0
- acra/commands/research.py +65 -0
- acra/commands/serve.py +13 -0
- acra/commands/session.py +20 -0
- acra/commands/utils.py +12 -0
- acra/config/__init__.py +36 -0
- acra/config/__pycache__/__init__.cpython-312.pyc +0 -0
- acra/config/__pycache__/defaults.cpython-312.pyc +0 -0
- acra/config/__pycache__/profile_manager.cpython-312.pyc +0 -0
- acra/config/defaults.py +88 -0
- acra/config/profile_manager.py +87 -0
- acra/execution/__init__.py +1 -0
- acra/execution/logs/__init__.py +1 -0
- acra/execution/logs/error_logs/__init__.py +1 -0
- acra/execution/logs/execution_logs/__init__.py +1 -0
- acra/execution/sandbox/__init__.py +1 -0
- acra/execution/sandbox/docker_executor.py +209 -0
- acra/execution/sandbox/isolated_runner.py +148 -0
- acra/execution/sandbox/sandbox_manager.py +178 -0
- acra/graph/__init__.py +0 -0
- acra/graph/checkpoint.py +85 -0
- acra/graph/conditional_edges.py +103 -0
- acra/graph/edges.py +85 -0
- acra/graph/graph_visualizer.py +175 -0
- acra/graph/nodes.py +68 -0
- acra/graph/router.py +69 -0
- acra/graph/state.py +93 -0
- acra/graph/workflow.py +117 -0
- acra/memory/__init__.py +1 -0
- acra/memory/checkpoints/__init__.py +1 -0
- acra/memory/checkpoints/data/__init__.py +1 -0
- acra/memory/checkpoints/postgres_checkpoint.py +247 -0
- acra/memory/checkpoints/sqlite_checkpoint.py +309 -0
- acra/memory/chroma_db/__init__.py +1 -0
- acra/memory/conversation_memory/__init__.py +1 -0
- acra/memory/conversation_memory/long_term.py +229 -0
- acra/memory/conversation_memory/short_term.py +158 -0
- acra/memory/storage/__init__.py +1 -0
- acra/memory/vector_store/__init__.py +0 -0
- acra/memory/vector_store/chroma_store.py +260 -0
- acra/memory/vector_store/embeddings.py +43 -0
- acra/observability/__init__.py +1 -0
- acra/observability/langsmith_tracing.py +91 -0
- acra/observability/metrics.py +189 -0
- acra/observability/monitoring.py +162 -0
- acra/observability/token_tracking.py +131 -0
- acra/prompts/__init__.py +1 -0
- acra/schemas/__init__.py +0 -0
- acra/schemas/__pycache__/__init__.cpython-312.pyc +0 -0
- acra/schemas/__pycache__/coder_schema.cpython-312.pyc +0 -0
- acra/schemas/__pycache__/critic_schema.cpython-312.pyc +0 -0
- acra/schemas/__pycache__/execution_schema.cpython-312.pyc +0 -0
- acra/schemas/__pycache__/planner_schema.cpython-312.pyc +0 -0
- acra/schemas/__pycache__/researcher_schema.cpython-312.pyc +0 -0
- acra/schemas/__pycache__/shared_schema.cpython-312.pyc +0 -0
- acra/schemas/coder_schema.py +257 -0
- acra/schemas/critic_schema.py +177 -0
- acra/schemas/execution_schema.py +74 -0
- acra/schemas/planner_schema.py +129 -0
- acra/schemas/researcher_schema.py +220 -0
- acra/schemas/shared_schema.py +329 -0
- acra/tools/__init__.py +1 -0
- acra/tools/code_tools/__init__.py +1 -0
- acra/tools/code_tools/file_reader.py +156 -0
- acra/tools/code_tools/file_writer.py +108 -0
- acra/tools/code_tools/python_repl.py +72 -0
- acra/tools/code_tools/terminal_runner.py +98 -0
- acra/tools/github_tools/__init__.py +1 -0
- acra/tools/github_tools/commit_generator.py +91 -0
- acra/tools/github_tools/github_search.py +113 -0
- acra/tools/github_tools/repo_analyzer.py +138 -0
- acra/tools/integration_test_helper.py +370 -0
- acra/tools/validation_tools/__init__.py +1 -0
- acra/tools/validation_tools/dependency_checker.py +101 -0
- acra/tools/validation_tools/environment_config_validator.py +353 -0
- acra/tools/validation_tools/http_validator.py +336 -0
- acra/tools/validation_tools/response_validator.py +407 -0
- acra/tools/validation_tools/security_checker.py +93 -0
- acra/tools/validation_tools/syntax_checker.py +75 -0
- acra/tools/validation_tools/web_framework_validator.py +382 -0
- acra/tools/web_tools/__init__.py +1 -0
- acra/tools/web_tools/arxiv_search.py +110 -0
- acra/tools/web_tools/serpapi_search.py +106 -0
- acra/tools/web_tools/tavily_search.py +96 -0
- acra/ui/__init__.py +8 -0
- acra/ui/__pycache__/__init__.cpython-312.pyc +0 -0
- acra/ui/__pycache__/banner.cpython-312.pyc +0 -0
- acra/ui/__pycache__/components.cpython-312.pyc +0 -0
- acra/ui/__pycache__/shell.cpython-312.pyc +0 -0
- acra/ui/__pycache__/theme.cpython-312.pyc +0 -0
- acra/ui/banner.py +40 -0
- acra/ui/components.py +32 -0
- acra/ui/shell.py +91 -0
- acra/ui/theme.py +28 -0
- acra/utils/__init__.py +21 -0
- acra/utils/context_manager.py +20 -0
- acra/utils/keyring_manager.py +83 -0
- acra/utils/output_formatter.py +18 -0
- acra/utils/session_manager.py +59 -0
- acra_cli-0.1.1.dist-info/METADATA +616 -0
- acra_cli-0.1.1.dist-info/RECORD +169 -0
- acra_cli-0.1.1.dist-info/WHEEL +5 -0
- acra_cli-0.1.1.dist-info/licenses/LICENSE +661 -0
- acra_cli-0.1.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import re
|
|
3
|
+
from typing import Dict, Any, List, Optional, Union
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class APIContractRule:
|
|
9
|
+
"""API contract validation rule"""
|
|
10
|
+
field: str
|
|
11
|
+
type: str # "string", "number", "integer", "boolean", "array", "object"
|
|
12
|
+
required: bool = True
|
|
13
|
+
min_value: Optional[Union[int, float]] = None
|
|
14
|
+
max_value: Optional[Union[int, float]] = None
|
|
15
|
+
min_length: Optional[int] = None
|
|
16
|
+
max_length: Optional[int] = None
|
|
17
|
+
pattern: Optional[str] = None # Regex pattern
|
|
18
|
+
enum: Optional[List[Any]] = None # Allowed values
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ResponseValidatorTool:
|
|
22
|
+
"""
|
|
23
|
+
Response validation tool for HTTP responses.
|
|
24
|
+
|
|
25
|
+
Responsibilities:
|
|
26
|
+
- Validate response structure
|
|
27
|
+
- Check response types
|
|
28
|
+
- Verify response codes
|
|
29
|
+
- Validate API contracts
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
@staticmethod
|
|
33
|
+
def validate_response_structure(
|
|
34
|
+
response_data: Dict,
|
|
35
|
+
schema: Dict
|
|
36
|
+
) -> Dict:
|
|
37
|
+
"""
|
|
38
|
+
Validate response against a schema.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
response_data: Response data to validate
|
|
42
|
+
schema: JSON schema
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
Validation result with errors
|
|
46
|
+
"""
|
|
47
|
+
errors = []
|
|
48
|
+
warnings = []
|
|
49
|
+
|
|
50
|
+
# Check required fields
|
|
51
|
+
required_fields = schema.get("required", [])
|
|
52
|
+
for field in required_fields:
|
|
53
|
+
if field not in response_data:
|
|
54
|
+
errors.append(f"Missing required field: {field}")
|
|
55
|
+
|
|
56
|
+
# Check field types
|
|
57
|
+
properties = schema.get("properties", {})
|
|
58
|
+
for field, field_schema in properties.items():
|
|
59
|
+
if field not in response_data:
|
|
60
|
+
continue
|
|
61
|
+
|
|
62
|
+
field_value = response_data[field]
|
|
63
|
+
expected_type = field_schema.get("type", "string")
|
|
64
|
+
|
|
65
|
+
# Type validation
|
|
66
|
+
type_valid = ResponseValidatorTool._validate_type(
|
|
67
|
+
field_value,
|
|
68
|
+
expected_type
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
if not type_valid:
|
|
72
|
+
errors.append(
|
|
73
|
+
f"Field '{field}' has incorrect type. "
|
|
74
|
+
f"Expected {expected_type}, got {type(field_value).__name__}"
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
# Min/max validation for numbers
|
|
78
|
+
if expected_type in ["number", "integer"]:
|
|
79
|
+
if "minimum" in field_schema:
|
|
80
|
+
if field_value < field_schema["minimum"]:
|
|
81
|
+
errors.append(
|
|
82
|
+
f"Field '{field}' is below minimum: {field_schema['minimum']}"
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
if "maximum" in field_schema:
|
|
86
|
+
if field_value > field_schema["maximum"]:
|
|
87
|
+
errors.append(
|
|
88
|
+
f"Field '{field}' is above maximum: {field_schema['maximum']}"
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
# String length validation
|
|
92
|
+
if expected_type == "string":
|
|
93
|
+
if "minLength" in field_schema:
|
|
94
|
+
if len(field_value) < field_schema["minLength"]:
|
|
95
|
+
errors.append(
|
|
96
|
+
f"Field '{field}' is shorter than minimum: {field_schema['minLength']}"
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
if "maxLength" in field_schema:
|
|
100
|
+
if len(field_value) > field_schema["maxLength"]:
|
|
101
|
+
errors.append(
|
|
102
|
+
f"Field '{field}' is longer than maximum: {field_schema['maxLength']}"
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
# Regex pattern validation
|
|
106
|
+
if "pattern" in field_schema:
|
|
107
|
+
if not re.match(field_schema["pattern"], field_value):
|
|
108
|
+
errors.append(
|
|
109
|
+
f"Field '{field}' does not match pattern: {field_schema['pattern']}"
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
# Enum validation
|
|
113
|
+
if "enum" in field_schema:
|
|
114
|
+
if field_value not in field_schema["enum"]:
|
|
115
|
+
errors.append(
|
|
116
|
+
f"Field '{field}' value '{field_value}' not in allowed values: "
|
|
117
|
+
f"{field_schema['enum']}"
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
# Nested object validation
|
|
121
|
+
if expected_type == "object" and "properties" in field_schema:
|
|
122
|
+
nested_result = ResponseValidatorTool.validate_response_structure(
|
|
123
|
+
field_value,
|
|
124
|
+
field_schema
|
|
125
|
+
)
|
|
126
|
+
errors.extend(nested_result["errors"])
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
"valid": len(errors) == 0,
|
|
130
|
+
"errors": errors,
|
|
131
|
+
"warnings": warnings,
|
|
132
|
+
"error_count": len(errors)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
@staticmethod
|
|
136
|
+
def _validate_type(value: Any, expected_type: str) -> bool:
|
|
137
|
+
"""
|
|
138
|
+
Validate value type.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
value: Value to validate
|
|
142
|
+
expected_type: Expected type name
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
True if type matches
|
|
146
|
+
"""
|
|
147
|
+
type_map = {
|
|
148
|
+
"string": str,
|
|
149
|
+
"number": (int, float),
|
|
150
|
+
"integer": int,
|
|
151
|
+
"boolean": bool,
|
|
152
|
+
"array": list,
|
|
153
|
+
"object": dict,
|
|
154
|
+
"null": type(None)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if expected_type not in type_map:
|
|
158
|
+
return True
|
|
159
|
+
|
|
160
|
+
expected = type_map[expected_type]
|
|
161
|
+
return isinstance(value, expected)
|
|
162
|
+
|
|
163
|
+
@staticmethod
|
|
164
|
+
def validate_api_contract(
|
|
165
|
+
response_data: Dict,
|
|
166
|
+
contract_rules: List[APIContractRule]
|
|
167
|
+
) -> Dict:
|
|
168
|
+
"""
|
|
169
|
+
Validate response against API contract rules.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
response_data: Response data
|
|
173
|
+
contract_rules: List of contract rules
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
Validation result
|
|
177
|
+
"""
|
|
178
|
+
violations = []
|
|
179
|
+
|
|
180
|
+
for rule in contract_rules:
|
|
181
|
+
if rule.field not in response_data:
|
|
182
|
+
if rule.required:
|
|
183
|
+
violations.append({
|
|
184
|
+
"field": rule.field,
|
|
185
|
+
"violation": "Missing required field",
|
|
186
|
+
"severity": "error"
|
|
187
|
+
})
|
|
188
|
+
continue
|
|
189
|
+
|
|
190
|
+
value = response_data[rule.field]
|
|
191
|
+
|
|
192
|
+
# Type check
|
|
193
|
+
if not ResponseValidatorTool._validate_type(value, rule.type):
|
|
194
|
+
violations.append({
|
|
195
|
+
"field": rule.field,
|
|
196
|
+
"violation": f"Type mismatch: expected {rule.type}, got {type(value).__name__}",
|
|
197
|
+
"severity": "error"
|
|
198
|
+
})
|
|
199
|
+
continue
|
|
200
|
+
|
|
201
|
+
# Value range checks
|
|
202
|
+
if rule.min_value is not None and isinstance(value, (int, float)):
|
|
203
|
+
if value < rule.min_value:
|
|
204
|
+
violations.append({
|
|
205
|
+
"field": rule.field,
|
|
206
|
+
"violation": f"Value below minimum: {rule.min_value}",
|
|
207
|
+
"severity": "error"
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
if rule.max_value is not None and isinstance(value, (int, float)):
|
|
211
|
+
if value > rule.max_value:
|
|
212
|
+
violations.append({
|
|
213
|
+
"field": rule.field,
|
|
214
|
+
"violation": f"Value above maximum: {rule.max_value}",
|
|
215
|
+
"severity": "error"
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
# String length checks
|
|
219
|
+
if rule.min_length is not None and isinstance(value, str):
|
|
220
|
+
if len(value) < rule.min_length:
|
|
221
|
+
violations.append({
|
|
222
|
+
"field": rule.field,
|
|
223
|
+
"violation": f"String shorter than minimum: {rule.min_length}",
|
|
224
|
+
"severity": "error"
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
if rule.max_length is not None and isinstance(value, str):
|
|
228
|
+
if len(value) > rule.max_length:
|
|
229
|
+
violations.append({
|
|
230
|
+
"field": rule.field,
|
|
231
|
+
"violation": f"String longer than maximum: {rule.max_length}",
|
|
232
|
+
"severity": "error"
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
# Pattern matching
|
|
236
|
+
if rule.pattern and isinstance(value, str):
|
|
237
|
+
if not re.match(rule.pattern, value):
|
|
238
|
+
violations.append({
|
|
239
|
+
"field": rule.field,
|
|
240
|
+
"violation": f"Value does not match pattern: {rule.pattern}",
|
|
241
|
+
"severity": "error"
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
# Enum validation
|
|
245
|
+
if rule.enum and value not in rule.enum:
|
|
246
|
+
violations.append({
|
|
247
|
+
"field": rule.field,
|
|
248
|
+
"violation": f"Value not in allowed list: {rule.enum}",
|
|
249
|
+
"severity": "error"
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
return {
|
|
253
|
+
"valid": len(violations) == 0,
|
|
254
|
+
"violations": violations,
|
|
255
|
+
"violation_count": len(violations)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
@staticmethod
|
|
259
|
+
def extract_json_schema_from_response(
|
|
260
|
+
response_data: Union[Dict, List]
|
|
261
|
+
) -> Dict:
|
|
262
|
+
"""
|
|
263
|
+
Extract JSON schema from response data.
|
|
264
|
+
|
|
265
|
+
Args:
|
|
266
|
+
response_data: Response data
|
|
267
|
+
|
|
268
|
+
Returns:
|
|
269
|
+
Inferred JSON schema
|
|
270
|
+
"""
|
|
271
|
+
if isinstance(response_data, dict):
|
|
272
|
+
properties = {}
|
|
273
|
+
required = []
|
|
274
|
+
|
|
275
|
+
for key, value in response_data.items():
|
|
276
|
+
prop_type = ResponseValidatorTool._infer_type(value)
|
|
277
|
+
properties[key] = {"type": prop_type}
|
|
278
|
+
|
|
279
|
+
# All top-level keys are considered required
|
|
280
|
+
required.append(key)
|
|
281
|
+
|
|
282
|
+
return {
|
|
283
|
+
"type": "object",
|
|
284
|
+
"properties": properties,
|
|
285
|
+
"required": required
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
elif isinstance(response_data, list):
|
|
289
|
+
if not response_data:
|
|
290
|
+
return {"type": "array", "items": {}}
|
|
291
|
+
|
|
292
|
+
# Infer from first element
|
|
293
|
+
first_type = ResponseValidatorTool._infer_type(response_data[0])
|
|
294
|
+
return {
|
|
295
|
+
"type": "array",
|
|
296
|
+
"items": {"type": first_type}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
else:
|
|
300
|
+
return {"type": ResponseValidatorTool._infer_type(response_data)}
|
|
301
|
+
|
|
302
|
+
@staticmethod
|
|
303
|
+
def _infer_type(value: Any) -> str:
|
|
304
|
+
"""
|
|
305
|
+
Infer JSON schema type from Python value.
|
|
306
|
+
|
|
307
|
+
Args:
|
|
308
|
+
value: Value to analyze
|
|
309
|
+
|
|
310
|
+
Returns:
|
|
311
|
+
JSON schema type
|
|
312
|
+
"""
|
|
313
|
+
if isinstance(value, bool):
|
|
314
|
+
return "boolean"
|
|
315
|
+
elif isinstance(value, int):
|
|
316
|
+
return "integer"
|
|
317
|
+
elif isinstance(value, float):
|
|
318
|
+
return "number"
|
|
319
|
+
elif isinstance(value, str):
|
|
320
|
+
return "string"
|
|
321
|
+
elif isinstance(value, list):
|
|
322
|
+
return "array"
|
|
323
|
+
elif isinstance(value, dict):
|
|
324
|
+
return "object"
|
|
325
|
+
elif value is None:
|
|
326
|
+
return "null"
|
|
327
|
+
else:
|
|
328
|
+
return "string"
|
|
329
|
+
|
|
330
|
+
@staticmethod
|
|
331
|
+
def compare_responses(
|
|
332
|
+
response1: Dict,
|
|
333
|
+
response2: Dict
|
|
334
|
+
) -> Dict:
|
|
335
|
+
"""
|
|
336
|
+
Compare two responses for structural differences.
|
|
337
|
+
|
|
338
|
+
Args:
|
|
339
|
+
response1: First response
|
|
340
|
+
response2: Second response
|
|
341
|
+
|
|
342
|
+
Returns:
|
|
343
|
+
Comparison result with differences
|
|
344
|
+
"""
|
|
345
|
+
differences = []
|
|
346
|
+
|
|
347
|
+
# Check for missing keys in response2
|
|
348
|
+
for key in response1.keys():
|
|
349
|
+
if key not in response2:
|
|
350
|
+
differences.append(f"Missing key in second response: {key}")
|
|
351
|
+
|
|
352
|
+
# Check for extra keys in response2
|
|
353
|
+
for key in response2.keys():
|
|
354
|
+
if key not in response1:
|
|
355
|
+
differences.append(f"Extra key in second response: {key}")
|
|
356
|
+
|
|
357
|
+
# Check for value differences
|
|
358
|
+
for key in set(response1.keys()) & set(response2.keys()):
|
|
359
|
+
if response1[key] != response2[key]:
|
|
360
|
+
differences.append(
|
|
361
|
+
f"Value difference for key '{key}': "
|
|
362
|
+
f"{response1[key]} vs {response2[key]}"
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
return {
|
|
366
|
+
"identical": len(differences) == 0,
|
|
367
|
+
"differences": differences,
|
|
368
|
+
"difference_count": len(differences)
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
@staticmethod
|
|
372
|
+
def validate_error_response(error_response: Dict) -> Dict:
|
|
373
|
+
"""
|
|
374
|
+
Validate error response structure.
|
|
375
|
+
|
|
376
|
+
Args:
|
|
377
|
+
error_response: Error response data
|
|
378
|
+
|
|
379
|
+
Returns:
|
|
380
|
+
Validation result
|
|
381
|
+
"""
|
|
382
|
+
issues = []
|
|
383
|
+
warnings = []
|
|
384
|
+
|
|
385
|
+
# Check for error code
|
|
386
|
+
if "error_code" not in error_response and "code" not in error_response:
|
|
387
|
+
issues.append("Error code not present in response")
|
|
388
|
+
|
|
389
|
+
# Check for error message
|
|
390
|
+
if "error_message" not in error_response and "message" not in error_response:
|
|
391
|
+
issues.append("Error message not present in response")
|
|
392
|
+
|
|
393
|
+
# Check for error details
|
|
394
|
+
if "error_details" not in error_response and "details" not in error_response:
|
|
395
|
+
warnings.append("Consider including error details in error response")
|
|
396
|
+
|
|
397
|
+
# Check for HTTP status code consistency
|
|
398
|
+
if "status" in error_response:
|
|
399
|
+
status = error_response["status"]
|
|
400
|
+
if not (400 <= status <= 599):
|
|
401
|
+
issues.append(f"Invalid HTTP status code: {status}")
|
|
402
|
+
|
|
403
|
+
return {
|
|
404
|
+
"valid": len(issues) == 0,
|
|
405
|
+
"issues": issues,
|
|
406
|
+
"warnings": warnings
|
|
407
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from typing import Dict, List
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
# security checker tool
|
|
6
|
+
|
|
7
|
+
class SecurityCheckerTool:
|
|
8
|
+
"""
|
|
9
|
+
Security validation tool.
|
|
10
|
+
|
|
11
|
+
Responsibilities:
|
|
12
|
+
- detect dangerous code patterns
|
|
13
|
+
- identify unsafe execution
|
|
14
|
+
- support autonomous safety checks
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# dangerous patterns
|
|
19
|
+
|
|
20
|
+
DANGEROUS_PATTERNS = {
|
|
21
|
+
|
|
22
|
+
"eval_usage": r"\beval\s*\(",
|
|
23
|
+
|
|
24
|
+
"exec_usage": r"\bexec\s*\(",
|
|
25
|
+
|
|
26
|
+
"os_system": r"\bos\.system\s*\(",
|
|
27
|
+
|
|
28
|
+
"subprocess_shell_true": (
|
|
29
|
+
r"shell\s*=\s*True"
|
|
30
|
+
),
|
|
31
|
+
|
|
32
|
+
"pickle_usage": r"\bpickle\.load",
|
|
33
|
+
|
|
34
|
+
"hardcoded_password": (
|
|
35
|
+
r"password\s*=\s*[\"'].*[\"']"
|
|
36
|
+
),
|
|
37
|
+
|
|
38
|
+
"hardcoded_api_key": (
|
|
39
|
+
r"api_key\s*=\s*[\"'].*[\"']"
|
|
40
|
+
),
|
|
41
|
+
|
|
42
|
+
"unsafe_yaml_load": (
|
|
43
|
+
r"yaml\.load\s*\("
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# check security
|
|
49
|
+
|
|
50
|
+
@classmethod
|
|
51
|
+
def check_security(
|
|
52
|
+
cls,
|
|
53
|
+
code: str
|
|
54
|
+
) -> Dict:
|
|
55
|
+
"""
|
|
56
|
+
Analyze code for security risks.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
issues = []
|
|
60
|
+
|
|
61
|
+
for issue_name, pattern in (
|
|
62
|
+
cls.DANGEROUS_PATTERNS.items()
|
|
63
|
+
):
|
|
64
|
+
|
|
65
|
+
matches = re.findall(
|
|
66
|
+
pattern,
|
|
67
|
+
code
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
if matches:
|
|
71
|
+
|
|
72
|
+
issues.append({
|
|
73
|
+
|
|
74
|
+
"issue": issue_name,
|
|
75
|
+
|
|
76
|
+
"matches_found": len(matches)
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
|
|
81
|
+
"success": True,
|
|
82
|
+
|
|
83
|
+
"safe": len(issues) == 0,
|
|
84
|
+
|
|
85
|
+
"issues": issues
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
# global helper
|
|
90
|
+
|
|
91
|
+
security_checker = (
|
|
92
|
+
SecurityCheckerTool()
|
|
93
|
+
)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
from typing import Dict
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
# syntax checker tool
|
|
6
|
+
|
|
7
|
+
class SyntaxCheckerTool:
|
|
8
|
+
"""
|
|
9
|
+
Python syntax validation tool.
|
|
10
|
+
|
|
11
|
+
Responsibilities:
|
|
12
|
+
- validate Python syntax
|
|
13
|
+
- detect parsing failures
|
|
14
|
+
- support autonomous debugging
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# check python syntax
|
|
19
|
+
|
|
20
|
+
@staticmethod
|
|
21
|
+
def check_syntax(
|
|
22
|
+
code: str
|
|
23
|
+
) -> Dict:
|
|
24
|
+
"""
|
|
25
|
+
Validate Python syntax.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
|
|
30
|
+
ast.parse(code)
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
|
|
34
|
+
"success": True,
|
|
35
|
+
|
|
36
|
+
"valid": True,
|
|
37
|
+
|
|
38
|
+
"error": None
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
except SyntaxError as e:
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
|
|
45
|
+
"success": False,
|
|
46
|
+
|
|
47
|
+
"valid": False,
|
|
48
|
+
|
|
49
|
+
"error": {
|
|
50
|
+
|
|
51
|
+
"message": str(e),
|
|
52
|
+
|
|
53
|
+
"line": e.lineno,
|
|
54
|
+
|
|
55
|
+
"offset": e.offset,
|
|
56
|
+
|
|
57
|
+
"text": e.text
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
except Exception as e:
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
|
|
65
|
+
"success": False,
|
|
66
|
+
|
|
67
|
+
"valid": False,
|
|
68
|
+
|
|
69
|
+
"error": str(e)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
# global helper
|
|
74
|
+
|
|
75
|
+
syntax_checker = SyntaxCheckerTool()
|