coding-client 1.21.2__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.
- coding_client/__init__.py +1 -0
- coding_client/api/__init__.py +19 -0
- coding_client/api/exception_handlers.py +158 -0
- coding_client/api/logging_setup.py +44 -0
- coding_client/api/metrics.py +40 -0
- coding_client/api/openapi_config.py +124 -0
- coding_client/api/request_logging.py +60 -0
- coding_client/api/validation.py +150 -0
- coding_client/auth/__init__.py +78 -0
- coding_client/auth/_default_tenant.py +132 -0
- coding_client/auth/_header_trust.py +98 -0
- coding_client/auth/_onbehalf.py +315 -0
- coding_client/auth/_trusted_networks.py +94 -0
- coding_client/auth/dependencies.py +61 -0
- coding_client/auth/middleware.py +445 -0
- coding_client/auth/models.py +83 -0
- coding_client/auth/outbound.py +137 -0
- coding_client/auth/rbac.py +21 -0
- coding_client/auth/rbac_coding.py +5 -0
- coding_client/auth/tenant.py +650 -0
- coding_client/auth/websocket.py +148 -0
- coding_client/clients/__init__.py +24 -0
- coding_client/clients/base.py +131 -0
- coding_client/clients/coding_ai.py +33 -0
- coding_client/clients/coding_analysis.py +36 -0
- coding_client/clients/coding_containers.py +45 -0
- coding_client/clients/coding_github.py +127 -0
- coding_client/clients/coding_realtime.py +127 -0
- coding_client/clients/coding_security.py +51 -0
- coding_client/clients/coding_testing.py +31 -0
- coding_client/clients/coding_workspaces.py +265 -0
- coding_client/config/__init__.py +12 -0
- coding_client/config/base_settings.py +61 -0
- coding_client/config/yaml_config_loader.py +172 -0
- coding_client/db/__init__.py +42 -0
- coding_client/db/db_types.py +122 -0
- coding_client/db/exceptions.py +59 -0
- coding_client/db/postgres_connection.py +572 -0
- coding_client/db/tenant_query_helper.py +417 -0
- coding_client/db_init.py +49 -0
- coding_client/exceptions/__init__.py +67 -0
- coding_client/exceptions/api_exceptions.py +395 -0
- coding_client/exceptions/pydantic_ai_exceptions.py +533 -0
- coding_client/interfaces/__init__.py +22 -0
- coding_client/interfaces/cache_interface.py +179 -0
- coding_client/interfaces/container_interface.py +49 -0
- coding_client/interfaces/docker_interface.py +283 -0
- coding_client/interfaces/graph_interface.py +28 -0
- coding_client/interfaces/security_interface.py +72 -0
- coding_client/interfaces/webhook_interface.py +67 -0
- coding_client/llm/__init__.py +54 -0
- coding_client/llm/base_agent.py +195 -0
- coding_client/llm/context.py +121 -0
- coding_client/llm/responses.py +102 -0
- coding_client/llm/routing.py +82 -0
- coding_client/llm/settings.py +102 -0
- coding_client/models/__init__.py +62 -0
- coding_client/models/agent.py +38 -0
- coding_client/models/ai_test_failure.py +745 -0
- coding_client/models/analysis.py +330 -0
- coding_client/models/architecture.py +685 -0
- coding_client/models/bug.py +128 -0
- coding_client/models/code_graph.py +317 -0
- coding_client/models/container.py +241 -0
- coding_client/models/core.py +878 -0
- coding_client/models/docker_container.py +104 -0
- coding_client/models/github.py +293 -0
- coding_client/models/logging.py +29 -0
- coding_client/models/package_manager.py +257 -0
- coding_client/models/pr_validation.py +440 -0
- coding_client/models/pydantic_ai.py +395 -0
- coding_client/models/repo_scanner.py +249 -0
- coding_client/models/responses.py +431 -0
- coding_client/models/security.py +185 -0
- coding_client/models/static_analysis.py +508 -0
- coding_client/models/system.py +48 -0
- coding_client/models/test_executor.py +204 -0
- coding_client/models/testing.py +673 -0
- coding_client/models/testing_config.py +200 -0
- coding_client/models/validation.py +127 -0
- coding_client/models_init.py +272 -0
- coding_client/telemetry.py +195 -0
- coding_client/testing.py +180 -0
- coding_client/types/__init__.py +130 -0
- coding_client/types/constraint_types.py +276 -0
- coding_client/types/core_types.py +112 -0
- coding_client/types/state_machine.py +443 -0
- coding_client/utils/__init__.py +86 -0
- coding_client/utils/archive_utils.py +387 -0
- coding_client/utils/ast_debug.py +45 -0
- coding_client/utils/async_timeout.py +364 -0
- coding_client/utils/constants.py +518 -0
- coding_client/utils/content_processing.py +326 -0
- coding_client/utils/database.py +916 -0
- coding_client/utils/encoding_helper.py +222 -0
- coding_client/utils/enum_helpers.py +166 -0
- coding_client/utils/file_operations.py +177 -0
- coding_client/utils/filesystem_ops.py +528 -0
- coding_client/utils/http_client.py +562 -0
- coding_client/utils/path_utils.py +471 -0
- coding_client/utils/profiling.py +421 -0
- coding_client/utils/response_builder.py +240 -0
- coding_client/utils/retry_helper.py +712 -0
- coding_client/utils/security_validation.py +423 -0
- coding_client-1.21.2.dist-info/METADATA +234 -0
- coding_client-1.21.2.dist-info/RECORD +109 -0
- coding_client-1.21.2.dist-info/WHEEL +5 -0
- coding_client-1.21.2.dist-info/licenses/LICENSE +201 -0
- coding_client-1.21.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
"""PydanticAI models for intelligent test failure analysis."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from datetime import UTC, datetime
|
|
7
|
+
from enum import StrEnum
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class PydanticAIAnalysisType(StrEnum):
|
|
16
|
+
"""Types of test failure analysis."""
|
|
17
|
+
|
|
18
|
+
FAILURE_ANALYSIS = "failure_analysis"
|
|
19
|
+
CODE_QUALITY = "code_quality"
|
|
20
|
+
PERFORMANCE = "performance"
|
|
21
|
+
SECURITY = "security"
|
|
22
|
+
DEPENDENCY = "dependency"
|
|
23
|
+
RACE_CONDITION = "race_condition"
|
|
24
|
+
MEMORY_LEAK = "memory_leak"
|
|
25
|
+
TIMEOUT = "timeout"
|
|
26
|
+
COMPILATION_ERROR = "compilation_error"
|
|
27
|
+
RUNTIME_ERROR = "runtime_error"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class FailureSeverity(StrEnum):
|
|
31
|
+
"""Severity levels for test failures."""
|
|
32
|
+
|
|
33
|
+
CRITICAL = "critical"
|
|
34
|
+
HIGH = "high"
|
|
35
|
+
MEDIUM = "medium"
|
|
36
|
+
LOW = "low"
|
|
37
|
+
INFO = "info"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class FailureCategory(StrEnum):
|
|
41
|
+
"""Categories of test failures."""
|
|
42
|
+
|
|
43
|
+
SYNTAX_ERROR = "syntax_error"
|
|
44
|
+
TYPE_ERROR = "type_error"
|
|
45
|
+
LOGIC_ERROR = "logic_error"
|
|
46
|
+
RUNTIME_ERROR = "runtime_error"
|
|
47
|
+
ASSERTION_ERROR = "assertion_error"
|
|
48
|
+
TIMEOUT_ERROR = "timeout_error"
|
|
49
|
+
MEMORY_ERROR = "memory_error"
|
|
50
|
+
DEPENDENCY_ERROR = "dependency_error"
|
|
51
|
+
CONFIGURATION_ERROR = "configuration_error"
|
|
52
|
+
ENVIRONMENT_ERROR = "environment_error"
|
|
53
|
+
UNKNOWN = "unknown"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class PydanticAITestFailureContext(BaseModel):
|
|
57
|
+
"""Context information for test failure analysis."""
|
|
58
|
+
|
|
59
|
+
model_config = ConfigDict(
|
|
60
|
+
str_strip_whitespace=True,
|
|
61
|
+
validate_assignment=True,
|
|
62
|
+
extra="forbid",
|
|
63
|
+
frozen=False,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
test_id: str = Field(..., description="Unique test execution ID")
|
|
67
|
+
language: str = Field(..., description="Programming language being tested")
|
|
68
|
+
test_type: str = Field(..., description="Type of test (unit, integration, etc.)")
|
|
69
|
+
exit_code: int = Field(..., description="Test process exit code")
|
|
70
|
+
stdout: str = Field(default="", description="Standard output from test execution")
|
|
71
|
+
stderr: str = Field(default="", description="Standard error from test execution")
|
|
72
|
+
execution_time_ms: float = Field(..., description="Test execution time in milliseconds")
|
|
73
|
+
container_name: str | None = Field(None, description="Container name where test ran")
|
|
74
|
+
source_code: str | None = Field(None, description="Source code being tested")
|
|
75
|
+
test_code: str | None = Field(None, description="Test code that failed")
|
|
76
|
+
dependencies: dict[str, str] = Field(default_factory=dict, description="Project dependencies")
|
|
77
|
+
environment: dict[str, str] = Field(default_factory=dict, description="Environment variables")
|
|
78
|
+
resource_usage: dict[str, Any] | None = Field(None, description="Resource usage during test")
|
|
79
|
+
created_at: datetime = Field(
|
|
80
|
+
default_factory=lambda: datetime.now(UTC),
|
|
81
|
+
description="When context was created",
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
@field_validator("execution_time_ms")
|
|
85
|
+
@classmethod
|
|
86
|
+
def validate_execution_time(cls, v: float) -> float:
|
|
87
|
+
"""Validate execution time is non-negative."""
|
|
88
|
+
if v < 0:
|
|
89
|
+
raise ValueError("Execution time cannot be negative")
|
|
90
|
+
return v
|
|
91
|
+
|
|
92
|
+
@field_validator("exit_code")
|
|
93
|
+
@classmethod
|
|
94
|
+
def validate_exit_code(cls, v: int) -> int:
|
|
95
|
+
"""Validate exit code is reasonable."""
|
|
96
|
+
if v < 0 or v > 255:
|
|
97
|
+
logger.warning(f"Unusual exit code: {v}")
|
|
98
|
+
return v
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class FailureAnalysisResult(BaseModel):
|
|
102
|
+
"""Result of intelligent test failure analysis."""
|
|
103
|
+
|
|
104
|
+
model_config = ConfigDict(
|
|
105
|
+
str_strip_whitespace=True,
|
|
106
|
+
validate_assignment=True,
|
|
107
|
+
extra="forbid",
|
|
108
|
+
frozen=False,
|
|
109
|
+
protected_namespaces=(),
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
analysis_id: str = Field(..., description="Unique analysis result ID")
|
|
113
|
+
test_id: str = Field(..., description="Test ID that was analyzed")
|
|
114
|
+
analysis_type: PydanticAIAnalysisType = Field(..., description="Type of analysis performed")
|
|
115
|
+
category: FailureCategory = Field(..., description="Category of failure")
|
|
116
|
+
severity: FailureSeverity = Field(..., description="Severity of the failure")
|
|
117
|
+
confidence: float = Field(..., ge=0.0, le=1.0, description="Confidence in analysis (0-1)")
|
|
118
|
+
root_cause: str = Field(..., description="Identified root cause of failure")
|
|
119
|
+
symptoms: list[str] = Field(default_factory=list, description="Observable symptoms")
|
|
120
|
+
potential_causes: list[str] = Field(
|
|
121
|
+
default_factory=list, description="Potential causes considered"
|
|
122
|
+
)
|
|
123
|
+
recommendations: list[str] = Field(default_factory=list, description="Recommended actions")
|
|
124
|
+
code_fixes: list[str] = Field(default_factory=list, description="Suggested code fixes")
|
|
125
|
+
related_issues: list[str] = Field(
|
|
126
|
+
default_factory=list, description="Related issues or patterns"
|
|
127
|
+
)
|
|
128
|
+
analysis_duration_ms: float = Field(..., description="Time taken for analysis")
|
|
129
|
+
ai_model_provider: str = Field(..., description="AI model provider used")
|
|
130
|
+
ai_model_name: str = Field(..., description="Specific model used for analysis")
|
|
131
|
+
raw_response: str | None = Field(None, description="Raw AI response for debugging")
|
|
132
|
+
metadata: dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
|
|
133
|
+
created_at: datetime = Field(
|
|
134
|
+
default_factory=lambda: datetime.now(UTC),
|
|
135
|
+
description="When analysis was completed",
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
@field_validator("confidence")
|
|
139
|
+
@classmethod
|
|
140
|
+
def validate_confidence(cls, v: float) -> float:
|
|
141
|
+
"""Validate confidence is between 0 and 1."""
|
|
142
|
+
if not 0.0 <= v <= 1.0:
|
|
143
|
+
raise ValueError("Confidence must be between 0.0 and 1.0")
|
|
144
|
+
return v
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class CodeQualityIssue(BaseModel):
|
|
148
|
+
"""Code quality issue identified during analysis."""
|
|
149
|
+
|
|
150
|
+
model_config = ConfigDict(
|
|
151
|
+
str_strip_whitespace=True,
|
|
152
|
+
validate_assignment=True,
|
|
153
|
+
extra="forbid",
|
|
154
|
+
frozen=False,
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
issue_id: str = Field(..., description="Unique issue identifier")
|
|
158
|
+
category: str = Field(..., description="Issue category (style, bug, performance, etc.)")
|
|
159
|
+
severity: FailureSeverity = Field(..., description="Issue severity")
|
|
160
|
+
message: str = Field(..., description="Issue description")
|
|
161
|
+
file_path: str | None = Field(None, description="File where issue was found")
|
|
162
|
+
line_number: int | None = Field(None, description="Line number of issue")
|
|
163
|
+
column_number: int | None = Field(None, description="Column number of issue")
|
|
164
|
+
code_snippet: str | None = Field(None, description="Code snippet showing issue")
|
|
165
|
+
suggested_fix: str | None = Field(None, description="Suggested fix for issue")
|
|
166
|
+
fix_confidence: float | None = Field(None, ge=0.0, le=1.0, description="Confidence in fix")
|
|
167
|
+
rule_id: str | None = Field(None, description="Rule ID that triggered issue")
|
|
168
|
+
tool_name: str | None = Field(None, description="Tool that detected issue")
|
|
169
|
+
|
|
170
|
+
@field_validator("line_number")
|
|
171
|
+
@classmethod
|
|
172
|
+
def validate_line_number(cls, v: int | None) -> int | None:
|
|
173
|
+
"""Validate line number is positive."""
|
|
174
|
+
if v is not None and v <= 0:
|
|
175
|
+
raise ValueError("Line number must be positive")
|
|
176
|
+
return v
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
class PerformanceAnalysis(BaseModel):
|
|
180
|
+
"""Performance analysis results."""
|
|
181
|
+
|
|
182
|
+
model_config = ConfigDict(
|
|
183
|
+
str_strip_whitespace=True,
|
|
184
|
+
validate_assignment=True,
|
|
185
|
+
extra="forbid",
|
|
186
|
+
frozen=False,
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
analysis_id: str = Field(..., description="Unique analysis identifier")
|
|
190
|
+
execution_time_ms: float = Field(..., description="Execution time in milliseconds")
|
|
191
|
+
memory_usage_mb: float | None = Field(None, description="Memory usage in MB")
|
|
192
|
+
cpu_usage_percent: float | None = Field(None, description="CPU usage percentage")
|
|
193
|
+
bottlenecks: list[str] = Field(default_factory=list, description="Identified bottlenecks")
|
|
194
|
+
optimization_suggestions: list[str] = Field(
|
|
195
|
+
default_factory=list, description="Optimization suggestions"
|
|
196
|
+
)
|
|
197
|
+
performance_score: float | None = Field(
|
|
198
|
+
None, ge=0.0, le=10.0, description="Performance score (0-10)"
|
|
199
|
+
)
|
|
200
|
+
baseline_comparison: dict[str, Any] | None = Field(None, description="Comparison with baseline")
|
|
201
|
+
|
|
202
|
+
@field_validator("performance_score")
|
|
203
|
+
@classmethod
|
|
204
|
+
def validate_performance_score(cls, v: float | None) -> float | None:
|
|
205
|
+
"""Validate performance score is between 0 and 10."""
|
|
206
|
+
if v is not None and not 0.0 <= v <= 10.0:
|
|
207
|
+
raise ValueError("Performance score must be between 0.0 and 10.0")
|
|
208
|
+
return v
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
class SecurityAnalysis(BaseModel):
|
|
212
|
+
"""Security analysis results."""
|
|
213
|
+
|
|
214
|
+
model_config = ConfigDict(
|
|
215
|
+
str_strip_whitespace=True,
|
|
216
|
+
validate_assignment=True,
|
|
217
|
+
extra="forbid",
|
|
218
|
+
frozen=False,
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
analysis_id: str = Field(..., description="Unique analysis identifier")
|
|
222
|
+
vulnerabilities: list[str] = Field(
|
|
223
|
+
default_factory=list, description="Identified vulnerabilities"
|
|
224
|
+
)
|
|
225
|
+
security_score: float | None = Field(None, ge=0.0, le=10.0, description="Security score (0-10)")
|
|
226
|
+
risk_level: FailureSeverity = Field(..., description="Overall risk level")
|
|
227
|
+
recommendations: list[str] = Field(default_factory=list, description="Security recommendations")
|
|
228
|
+
cve_references: list[str] = Field(default_factory=list, description="Related CVE references")
|
|
229
|
+
compliance_issues: list[str] = Field(default_factory=list, description="Compliance issues")
|
|
230
|
+
|
|
231
|
+
@field_validator("security_score")
|
|
232
|
+
@classmethod
|
|
233
|
+
def validate_security_score(cls, v: float | None) -> float | None:
|
|
234
|
+
"""Validate security score is between 0 and 10."""
|
|
235
|
+
if v is not None and not 0.0 <= v <= 10.0:
|
|
236
|
+
raise ValueError("Security score must be between 0.0 and 10.0")
|
|
237
|
+
return v
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
class AIAnalysisRequest(BaseModel):
|
|
241
|
+
"""Request for AI-powered test failure analysis."""
|
|
242
|
+
|
|
243
|
+
model_config = ConfigDict(
|
|
244
|
+
str_strip_whitespace=True,
|
|
245
|
+
validate_assignment=True,
|
|
246
|
+
extra="forbid",
|
|
247
|
+
frozen=False,
|
|
248
|
+
protected_namespaces=(),
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
context: PydanticAITestFailureContext = Field(..., description="Test failure context")
|
|
252
|
+
analysis_types: list[PydanticAIAnalysisType] = Field(
|
|
253
|
+
default_factory=list, description="Types of analysis to perform"
|
|
254
|
+
)
|
|
255
|
+
ai_model_provider: str = Field(default="openai", description="AI model provider to use")
|
|
256
|
+
ai_model_name: str = Field(default="gpt-4", description="Specific model to use")
|
|
257
|
+
max_tokens: int = Field(default=2000, description="Maximum tokens for AI response")
|
|
258
|
+
temperature: float = Field(default=0.1, ge=0.0, le=2.0, description="Model temperature")
|
|
259
|
+
timeout_seconds: int = Field(default=30, description="Request timeout in seconds")
|
|
260
|
+
include_code_fixes: bool = Field(default=True, description="Include code fix suggestions")
|
|
261
|
+
include_performance_analysis: bool = Field(
|
|
262
|
+
default=True, description="Include performance analysis"
|
|
263
|
+
)
|
|
264
|
+
include_security_analysis: bool = Field(default=True, description="Include security analysis")
|
|
265
|
+
metadata: dict[str, Any] = Field(
|
|
266
|
+
default_factory=dict, description="Additional request metadata"
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
@field_validator("temperature")
|
|
270
|
+
@classmethod
|
|
271
|
+
def validate_temperature(cls, v: float) -> float:
|
|
272
|
+
"""Validate temperature is between 0 and 2."""
|
|
273
|
+
if not 0.0 <= v <= 2.0:
|
|
274
|
+
raise ValueError("Temperature must be between 0.0 and 2.0")
|
|
275
|
+
return v
|
|
276
|
+
|
|
277
|
+
@field_validator("max_tokens")
|
|
278
|
+
@classmethod
|
|
279
|
+
def validate_max_tokens(cls, v: int) -> int:
|
|
280
|
+
"""Validate max tokens is reasonable."""
|
|
281
|
+
if v <= 0 or v > 32000:
|
|
282
|
+
raise ValueError("Max tokens must be between 1 and 32000")
|
|
283
|
+
return v
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
class AIAnalysisResponse(BaseModel):
|
|
287
|
+
"""Response from AI-powered test failure analysis."""
|
|
288
|
+
|
|
289
|
+
model_config = ConfigDict(
|
|
290
|
+
str_strip_whitespace=True,
|
|
291
|
+
validate_assignment=True,
|
|
292
|
+
extra="forbid",
|
|
293
|
+
frozen=False,
|
|
294
|
+
protected_namespaces=(),
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
request_id: str = Field(..., description="Unique request identifier")
|
|
298
|
+
success: bool = Field(..., description="Whether analysis was successful")
|
|
299
|
+
failure_analysis: FailureAnalysisResult | None = Field(
|
|
300
|
+
None, description="Main failure analysis"
|
|
301
|
+
)
|
|
302
|
+
code_quality_issues: list[CodeQualityIssue] = Field(
|
|
303
|
+
default_factory=list, description="Code quality issues"
|
|
304
|
+
)
|
|
305
|
+
performance_analysis: PerformanceAnalysis | None = Field(
|
|
306
|
+
None, description="Performance analysis"
|
|
307
|
+
)
|
|
308
|
+
security_analysis: SecurityAnalysis | None = Field(None, description="Security analysis")
|
|
309
|
+
total_analysis_time_ms: float = Field(..., description="Total analysis time")
|
|
310
|
+
ai_model_usage: dict[str, Any] = Field(
|
|
311
|
+
default_factory=dict, description="Model usage statistics"
|
|
312
|
+
)
|
|
313
|
+
error_message: str | None = Field(None, description="Error message if analysis failed")
|
|
314
|
+
warnings: list[str] = Field(default_factory=list, description="Analysis warnings")
|
|
315
|
+
metadata: dict[str, Any] = Field(
|
|
316
|
+
default_factory=dict, description="Additional response metadata"
|
|
317
|
+
)
|
|
318
|
+
created_at: datetime = Field(
|
|
319
|
+
default_factory=lambda: datetime.now(UTC),
|
|
320
|
+
description="When response was created",
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
class AgentConfiguration(BaseModel):
|
|
325
|
+
"""Configuration for PydanticAI agents."""
|
|
326
|
+
|
|
327
|
+
model_config = ConfigDict(
|
|
328
|
+
str_strip_whitespace=True,
|
|
329
|
+
validate_assignment=True,
|
|
330
|
+
extra="forbid",
|
|
331
|
+
frozen=False,
|
|
332
|
+
protected_namespaces=(),
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
agent_id: str = Field(..., description="Unique agent identifier")
|
|
336
|
+
ai_model_provider: str = Field(..., description="AI model provider")
|
|
337
|
+
ai_model_name: str = Field(..., description="Model name")
|
|
338
|
+
api_key: str | None = Field(None, description="API key for model provider")
|
|
339
|
+
base_url: str | None = Field(None, description="Base URL for API")
|
|
340
|
+
max_retries: int = Field(default=3, description="Maximum retry attempts")
|
|
341
|
+
timeout_seconds: int = Field(default=30, description="Request timeout")
|
|
342
|
+
rate_limit_rpm: int = Field(default=100, description="Rate limit requests per minute")
|
|
343
|
+
rate_limit_tpm: int = Field(default=10000, description="Rate limit tokens per minute")
|
|
344
|
+
temperature: float = Field(default=0.1, ge=0.0, le=2.0, description="Model temperature")
|
|
345
|
+
max_tokens: int = Field(default=2000, description="Maximum tokens per request")
|
|
346
|
+
system_prompt: str | None = Field(None, description="System prompt for agent")
|
|
347
|
+
enabled: bool = Field(default=True, description="Whether agent is enabled")
|
|
348
|
+
metadata: dict[str, Any] = Field(default_factory=dict, description="Additional configuration")
|
|
349
|
+
|
|
350
|
+
@field_validator("temperature")
|
|
351
|
+
@classmethod
|
|
352
|
+
def validate_temperature(cls, v: float) -> float:
|
|
353
|
+
"""Validate temperature is between 0 and 2."""
|
|
354
|
+
if not 0.0 <= v <= 2.0:
|
|
355
|
+
raise ValueError("Temperature must be between 0.0 and 2.0")
|
|
356
|
+
return v
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
class AgentMetrics(BaseModel):
|
|
360
|
+
"""Metrics for PydanticAI agent performance."""
|
|
361
|
+
|
|
362
|
+
model_config = ConfigDict(
|
|
363
|
+
str_strip_whitespace=True,
|
|
364
|
+
validate_assignment=True,
|
|
365
|
+
extra="forbid",
|
|
366
|
+
frozen=False,
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
agent_id: str = Field(..., description="Agent identifier")
|
|
370
|
+
total_requests: int = Field(default=0, description="Total requests processed")
|
|
371
|
+
successful_requests: int = Field(default=0, description="Successful requests")
|
|
372
|
+
failed_requests: int = Field(default=0, description="Failed requests")
|
|
373
|
+
average_response_time_ms: float = Field(default=0.0, description="Average response time")
|
|
374
|
+
total_tokens_used: int = Field(default=0, description="Total tokens used")
|
|
375
|
+
total_cost_usd: float = Field(default=0.0, description="Total cost in USD")
|
|
376
|
+
last_request_at: datetime | None = Field(None, description="Last request timestamp")
|
|
377
|
+
error_rate: float = Field(default=0.0, description="Error rate (0-1)")
|
|
378
|
+
uptime_percent: float = Field(default=100.0, description="Uptime percentage")
|
|
379
|
+
metadata: dict[str, Any] = Field(default_factory=dict, description="Additional metrics")
|
|
380
|
+
|
|
381
|
+
@field_validator("error_rate")
|
|
382
|
+
@classmethod
|
|
383
|
+
def validate_error_rate(cls, v: float) -> float:
|
|
384
|
+
"""Validate error rate is between 0 and 1."""
|
|
385
|
+
if not 0.0 <= v <= 1.0:
|
|
386
|
+
raise ValueError("Error rate must be between 0.0 and 1.0")
|
|
387
|
+
return v
|
|
388
|
+
|
|
389
|
+
@field_validator("uptime_percent")
|
|
390
|
+
@classmethod
|
|
391
|
+
def validate_uptime_percent(cls, v: float) -> float:
|
|
392
|
+
"""Validate uptime percentage is between 0 and 100."""
|
|
393
|
+
if not 0.0 <= v <= 100.0:
|
|
394
|
+
raise ValueError("Uptime percentage must be between 0.0 and 100.0")
|
|
395
|
+
return v
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
"""Repository scanner models for comprehensive code analysis operations.
|
|
2
|
+
|
|
3
|
+
This module provides data models for the repository scanning and analysis API endpoint,
|
|
4
|
+
supporting various types of code analysis including dependency scanning, import analysis,
|
|
5
|
+
and code validation. The models ensure consistent request/response structures for
|
|
6
|
+
comprehensive code analysis operations in containerized environments.
|
|
7
|
+
|
|
8
|
+
The repository scanner supports:
|
|
9
|
+
- Dependency analysis with validation and security checking
|
|
10
|
+
- Import statement analysis and resolution
|
|
11
|
+
- Code validation across multiple file types
|
|
12
|
+
- Full repository scanning with configurable options
|
|
13
|
+
- Detailed error reporting and issue tracking
|
|
14
|
+
|
|
15
|
+
Key Model Categories:
|
|
16
|
+
Request Models: ScanRequest with configurable action types and options
|
|
17
|
+
Response Models: Specialized responses for different scan types
|
|
18
|
+
Data Models: DependencyValidation, ImportInfo, ValidationError for scan results
|
|
19
|
+
Union Types: ScanResponseUnion for type-safe response handling
|
|
20
|
+
|
|
21
|
+
Scan Types:
|
|
22
|
+
- DEPENDENCIES: Analyze package dependencies and versions
|
|
23
|
+
- IMPORTS: Scan import statements and module usage
|
|
24
|
+
- VALIDATION: Validate code syntax and structure
|
|
25
|
+
- FULL_SCAN: Comprehensive analysis combining all scan types
|
|
26
|
+
|
|
27
|
+
Example:
|
|
28
|
+
```python
|
|
29
|
+
from repo_scanner_models import RepoScannerScanRequest, RepoScanAction, RepoScannerDependencyScanResponse
|
|
30
|
+
|
|
31
|
+
# Create scan request
|
|
32
|
+
request = RepoScannerScanRequest(
|
|
33
|
+
container_name="my-analysis-container",
|
|
34
|
+
action=RepoScanAction.DEPENDENCIES,
|
|
35
|
+
target_files=["src/main.py", "app/models.py"],
|
|
36
|
+
options={"include_dev_dependencies": True}
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
# Process scan response
|
|
40
|
+
response = RepoScannerDependencyScanResponse(
|
|
41
|
+
container_name="my-analysis-container",
|
|
42
|
+
action="dependencies",
|
|
43
|
+
scan_duration=2.5,
|
|
44
|
+
file_count=15,
|
|
45
|
+
request_id="scan-123",
|
|
46
|
+
dependencies=[
|
|
47
|
+
DependencyValidation(
|
|
48
|
+
name="fastapi",
|
|
49
|
+
version="0.104.1",
|
|
50
|
+
source="pip",
|
|
51
|
+
valid=True
|
|
52
|
+
)
|
|
53
|
+
],
|
|
54
|
+
dependency_count=42,
|
|
55
|
+
invalid_dependencies=[]
|
|
56
|
+
)
|
|
57
|
+
```
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
from __future__ import annotations
|
|
61
|
+
|
|
62
|
+
from enum import StrEnum
|
|
63
|
+
from typing import Any
|
|
64
|
+
|
|
65
|
+
from pydantic import BaseModel, Field
|
|
66
|
+
|
|
67
|
+
from .responses import LegacyBaseResponse
|
|
68
|
+
|
|
69
|
+
# ================================================================================================
|
|
70
|
+
# REQUEST MODELS
|
|
71
|
+
# ================================================================================================
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class RepoScanAction(StrEnum):
|
|
75
|
+
"""Available scan action types."""
|
|
76
|
+
|
|
77
|
+
DEPENDENCIES = "dependencies"
|
|
78
|
+
IMPORTS = "imports"
|
|
79
|
+
VALIDATION = "validation"
|
|
80
|
+
FULL_SCAN = "full_scan"
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class RepoScannerScanRequest(BaseModel):
|
|
84
|
+
"""Request model for repository scanning operations."""
|
|
85
|
+
|
|
86
|
+
container_name: str = Field(
|
|
87
|
+
description="Name of the Docker container to scan",
|
|
88
|
+
examples=["my-repo-container-123"],
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
action: RepoScanAction = Field(
|
|
92
|
+
description="Type of scan to perform",
|
|
93
|
+
examples=[RepoScanAction.DEPENDENCIES],
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
target_files: list[str] | None = Field(
|
|
97
|
+
default=None,
|
|
98
|
+
description="Specific files to scan (optional, defaults to all Python files)",
|
|
99
|
+
examples=[["src/main.py", "app/models.py"]],
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
options: dict[str, Any] | None = Field(
|
|
103
|
+
default=None,
|
|
104
|
+
description="Additional scan options and parameters",
|
|
105
|
+
examples=[{"max_files": 100, "include_tests": True}],
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
# ================================================================================================
|
|
110
|
+
# RESPONSE MODELS
|
|
111
|
+
# ================================================================================================
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class DependencyValidation(BaseModel):
|
|
115
|
+
"""Information about a single dependency."""
|
|
116
|
+
|
|
117
|
+
name: str = Field(description="Package name", examples=["fastapi"])
|
|
118
|
+
version: str = Field(description="Package version", examples=["0.104.1"])
|
|
119
|
+
source: str = Field(description="Source of dependency info", examples=["pip"])
|
|
120
|
+
valid: bool = Field(description="Whether the dependency is valid", examples=[True])
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class RepoScannerImportInfo(BaseModel):
|
|
124
|
+
"""Information about a single import statement."""
|
|
125
|
+
|
|
126
|
+
type: str = Field(description="Type of import", examples=["from_import"])
|
|
127
|
+
module: str = Field(description="Module name", examples=["fastapi"])
|
|
128
|
+
name: str = Field(description="Imported name", examples=["FastAPI"])
|
|
129
|
+
alias: str | None = Field(default=None, description="Import alias", examples=["app"])
|
|
130
|
+
file: str = Field(description="File containing the import", examples=["src/main.py"])
|
|
131
|
+
line: int = Field(description="Line number", examples=[1])
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class ScannerValidationError(BaseModel):
|
|
135
|
+
"""Code validation error information."""
|
|
136
|
+
|
|
137
|
+
type: str = Field(..., description="Error type", examples=["SyntaxError"])
|
|
138
|
+
message: str = Field(..., description="Error message", examples=["invalid syntax"])
|
|
139
|
+
line: int | None = Field(None, description="Line number", examples=[10])
|
|
140
|
+
offset: int | None = Field(None, description="Character offset", examples=[5])
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class ValidationWarning(BaseModel):
|
|
144
|
+
"""Code validation warning information."""
|
|
145
|
+
|
|
146
|
+
type: str = Field(..., description="Warning type", examples=["LongLine"])
|
|
147
|
+
message: str = Field(
|
|
148
|
+
...,
|
|
149
|
+
description="Warning message",
|
|
150
|
+
examples=["Line too long (130 characters)"],
|
|
151
|
+
)
|
|
152
|
+
line: int = Field(..., description="Line number", examples=[15])
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class RepoScannerValidationResult(BaseModel):
|
|
156
|
+
"""Validation result for a single file."""
|
|
157
|
+
|
|
158
|
+
file: str = Field(description="File path", examples=["src/main.py"])
|
|
159
|
+
valid: bool = Field(description="Whether the file is valid", examples=[True])
|
|
160
|
+
syntax_valid: bool | None = Field(
|
|
161
|
+
default=None, description="Whether syntax is valid", examples=[True]
|
|
162
|
+
)
|
|
163
|
+
errors: list[ScannerValidationError] = Field(default=[], description="Validation errors")
|
|
164
|
+
warnings: list[ValidationWarning] = Field(default=[], description="Validation warnings")
|
|
165
|
+
line_count: int = Field(description="Number of lines in file", examples=[150])
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class BaseScanResponse(LegacyBaseResponse):
|
|
169
|
+
"""Base response model for all scan operations."""
|
|
170
|
+
|
|
171
|
+
container_name: str = Field(description="Container name that was scanned")
|
|
172
|
+
action: str = Field(description="Type of scan performed")
|
|
173
|
+
scan_duration: float = Field(description="Scan duration in seconds", examples=[2.5])
|
|
174
|
+
file_count: int = Field(description="Number of files processed", examples=[25])
|
|
175
|
+
request_id: str = Field(description="Unique request identifier")
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class RepoScannerDependencyScanResponse(BaseScanResponse):
|
|
179
|
+
"""Response model for dependency scan operations."""
|
|
180
|
+
|
|
181
|
+
dependencies: list[DependencyValidation] = Field(
|
|
182
|
+
default=[], description="List of discovered dependencies"
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
dependency_count: int = Field(description="Total number of dependencies found", examples=[42])
|
|
186
|
+
|
|
187
|
+
invalid_dependencies: list[dict[str, Any]] = Field(
|
|
188
|
+
default=[], description="List of invalid or problematic dependencies"
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
class RepoScannerImportScanResponse(BaseScanResponse):
|
|
193
|
+
"""Response model for import scan operations."""
|
|
194
|
+
|
|
195
|
+
imports: list[RepoScannerImportInfo] = Field(
|
|
196
|
+
default=[], description="List of discovered import statements"
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
import_count: int = Field(description="Total number of imports found", examples=[156])
|
|
200
|
+
|
|
201
|
+
invalid_imports: list[dict[str, Any]] = Field(
|
|
202
|
+
default=[], description="List of invalid or problematic imports"
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
class RepoScannerValidationScanResponse(BaseScanResponse):
|
|
207
|
+
"""Response model for code validation operations."""
|
|
208
|
+
|
|
209
|
+
validation_results: list[RepoScannerValidationResult] = Field(
|
|
210
|
+
default=[], description="Detailed validation results for each file"
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
valid_files: int = Field(..., description="Number of valid files", examples=[20])
|
|
214
|
+
|
|
215
|
+
invalid_files: int = Field(..., description="Number of invalid files", examples=[2])
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
class RepoScannerScanResponse(BaseScanResponse):
|
|
219
|
+
"""Response model for full scan operations."""
|
|
220
|
+
|
|
221
|
+
results: dict[str, Any] = Field(
|
|
222
|
+
...,
|
|
223
|
+
description="Combined results from all scan types",
|
|
224
|
+
examples=[
|
|
225
|
+
{
|
|
226
|
+
"dependencies": {"dependency_count": 42, "dependencies": []},
|
|
227
|
+
"imports": {"import_count": 156, "imports": []},
|
|
228
|
+
"validation": {"valid_files": 20, "invalid_files": 2},
|
|
229
|
+
"summary": {
|
|
230
|
+
"total_dependencies": 42,
|
|
231
|
+
"total_imports": 156,
|
|
232
|
+
"valid_files": 20,
|
|
233
|
+
"invalid_files": 2,
|
|
234
|
+
},
|
|
235
|
+
}
|
|
236
|
+
],
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
# ================================================================================================
|
|
241
|
+
# UNION RESPONSE TYPE
|
|
242
|
+
# ================================================================================================
|
|
243
|
+
|
|
244
|
+
ScanResponseUnion = (
|
|
245
|
+
RepoScannerDependencyScanResponse
|
|
246
|
+
| RepoScannerImportScanResponse
|
|
247
|
+
| RepoScannerValidationScanResponse
|
|
248
|
+
| RepoScannerScanResponse
|
|
249
|
+
)
|