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.
Files changed (109) hide show
  1. coding_client/__init__.py +1 -0
  2. coding_client/api/__init__.py +19 -0
  3. coding_client/api/exception_handlers.py +158 -0
  4. coding_client/api/logging_setup.py +44 -0
  5. coding_client/api/metrics.py +40 -0
  6. coding_client/api/openapi_config.py +124 -0
  7. coding_client/api/request_logging.py +60 -0
  8. coding_client/api/validation.py +150 -0
  9. coding_client/auth/__init__.py +78 -0
  10. coding_client/auth/_default_tenant.py +132 -0
  11. coding_client/auth/_header_trust.py +98 -0
  12. coding_client/auth/_onbehalf.py +315 -0
  13. coding_client/auth/_trusted_networks.py +94 -0
  14. coding_client/auth/dependencies.py +61 -0
  15. coding_client/auth/middleware.py +445 -0
  16. coding_client/auth/models.py +83 -0
  17. coding_client/auth/outbound.py +137 -0
  18. coding_client/auth/rbac.py +21 -0
  19. coding_client/auth/rbac_coding.py +5 -0
  20. coding_client/auth/tenant.py +650 -0
  21. coding_client/auth/websocket.py +148 -0
  22. coding_client/clients/__init__.py +24 -0
  23. coding_client/clients/base.py +131 -0
  24. coding_client/clients/coding_ai.py +33 -0
  25. coding_client/clients/coding_analysis.py +36 -0
  26. coding_client/clients/coding_containers.py +45 -0
  27. coding_client/clients/coding_github.py +127 -0
  28. coding_client/clients/coding_realtime.py +127 -0
  29. coding_client/clients/coding_security.py +51 -0
  30. coding_client/clients/coding_testing.py +31 -0
  31. coding_client/clients/coding_workspaces.py +265 -0
  32. coding_client/config/__init__.py +12 -0
  33. coding_client/config/base_settings.py +61 -0
  34. coding_client/config/yaml_config_loader.py +172 -0
  35. coding_client/db/__init__.py +42 -0
  36. coding_client/db/db_types.py +122 -0
  37. coding_client/db/exceptions.py +59 -0
  38. coding_client/db/postgres_connection.py +572 -0
  39. coding_client/db/tenant_query_helper.py +417 -0
  40. coding_client/db_init.py +49 -0
  41. coding_client/exceptions/__init__.py +67 -0
  42. coding_client/exceptions/api_exceptions.py +395 -0
  43. coding_client/exceptions/pydantic_ai_exceptions.py +533 -0
  44. coding_client/interfaces/__init__.py +22 -0
  45. coding_client/interfaces/cache_interface.py +179 -0
  46. coding_client/interfaces/container_interface.py +49 -0
  47. coding_client/interfaces/docker_interface.py +283 -0
  48. coding_client/interfaces/graph_interface.py +28 -0
  49. coding_client/interfaces/security_interface.py +72 -0
  50. coding_client/interfaces/webhook_interface.py +67 -0
  51. coding_client/llm/__init__.py +54 -0
  52. coding_client/llm/base_agent.py +195 -0
  53. coding_client/llm/context.py +121 -0
  54. coding_client/llm/responses.py +102 -0
  55. coding_client/llm/routing.py +82 -0
  56. coding_client/llm/settings.py +102 -0
  57. coding_client/models/__init__.py +62 -0
  58. coding_client/models/agent.py +38 -0
  59. coding_client/models/ai_test_failure.py +745 -0
  60. coding_client/models/analysis.py +330 -0
  61. coding_client/models/architecture.py +685 -0
  62. coding_client/models/bug.py +128 -0
  63. coding_client/models/code_graph.py +317 -0
  64. coding_client/models/container.py +241 -0
  65. coding_client/models/core.py +878 -0
  66. coding_client/models/docker_container.py +104 -0
  67. coding_client/models/github.py +293 -0
  68. coding_client/models/logging.py +29 -0
  69. coding_client/models/package_manager.py +257 -0
  70. coding_client/models/pr_validation.py +440 -0
  71. coding_client/models/pydantic_ai.py +395 -0
  72. coding_client/models/repo_scanner.py +249 -0
  73. coding_client/models/responses.py +431 -0
  74. coding_client/models/security.py +185 -0
  75. coding_client/models/static_analysis.py +508 -0
  76. coding_client/models/system.py +48 -0
  77. coding_client/models/test_executor.py +204 -0
  78. coding_client/models/testing.py +673 -0
  79. coding_client/models/testing_config.py +200 -0
  80. coding_client/models/validation.py +127 -0
  81. coding_client/models_init.py +272 -0
  82. coding_client/telemetry.py +195 -0
  83. coding_client/testing.py +180 -0
  84. coding_client/types/__init__.py +130 -0
  85. coding_client/types/constraint_types.py +276 -0
  86. coding_client/types/core_types.py +112 -0
  87. coding_client/types/state_machine.py +443 -0
  88. coding_client/utils/__init__.py +86 -0
  89. coding_client/utils/archive_utils.py +387 -0
  90. coding_client/utils/ast_debug.py +45 -0
  91. coding_client/utils/async_timeout.py +364 -0
  92. coding_client/utils/constants.py +518 -0
  93. coding_client/utils/content_processing.py +326 -0
  94. coding_client/utils/database.py +916 -0
  95. coding_client/utils/encoding_helper.py +222 -0
  96. coding_client/utils/enum_helpers.py +166 -0
  97. coding_client/utils/file_operations.py +177 -0
  98. coding_client/utils/filesystem_ops.py +528 -0
  99. coding_client/utils/http_client.py +562 -0
  100. coding_client/utils/path_utils.py +471 -0
  101. coding_client/utils/profiling.py +421 -0
  102. coding_client/utils/response_builder.py +240 -0
  103. coding_client/utils/retry_helper.py +712 -0
  104. coding_client/utils/security_validation.py +423 -0
  105. coding_client-1.21.2.dist-info/METADATA +234 -0
  106. coding_client-1.21.2.dist-info/RECORD +109 -0
  107. coding_client-1.21.2.dist-info/WHEEL +5 -0
  108. coding_client-1.21.2.dist-info/licenses/LICENSE +201 -0
  109. coding_client-1.21.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,745 @@
1
+ """
2
+ Comprehensive Pydantic v2 models for AI-powered test failure analysis.
3
+
4
+ This module provides type-safe, well-documented models for analyzing test failures
5
+ using PydanticAI. All models are designed with Pydantic v2 compliance and support
6
+ comprehensive error handling and validation.
7
+
8
+ The models support:
9
+ - Test failure input data and context
10
+ - AI-powered analysis results
11
+ - Root cause identification
12
+ - Intelligent fix suggestions
13
+ - Pattern detection and classification
14
+ - Confidence scoring and reasoning
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import re
20
+ from datetime import UTC, datetime
21
+ from enum import StrEnum
22
+ from typing import Any, Literal
23
+
24
+ from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
25
+
26
+
27
+ class TestFramework(StrEnum):
28
+ """Supported test frameworks for failure analysis."""
29
+
30
+ PYTEST = "pytest"
31
+ UNITTEST = "unittest"
32
+ JEST = "jest"
33
+ MOCHA = "mocha"
34
+ JUNIT = "junit"
35
+ XUNIT = "xunit"
36
+ RSPEC = "rspec"
37
+ UNKNOWN = "unknown"
38
+
39
+
40
+ class FailureType(StrEnum):
41
+ """Types of test failures that can be analyzed."""
42
+
43
+ ASSERTION_ERROR = "assertion_error"
44
+ SYNTAX_ERROR = "syntax_error"
45
+ IMPORT_ERROR = "import_error"
46
+ RUNTIME_ERROR = "runtime_error"
47
+ TIMEOUT_ERROR = "timeout_error"
48
+ MEMORY_ERROR = "memory_error"
49
+ NETWORK_ERROR = "network_error"
50
+ DEPENDENCY_ERROR = "dependency_error"
51
+ CONFIGURATION_ERROR = "configuration_error"
52
+ UNKNOWN_ERROR = "unknown_error"
53
+
54
+
55
+ class SeverityLevel(StrEnum):
56
+ """Severity levels for test failures and analysis results."""
57
+
58
+ CRITICAL = "critical"
59
+ HIGH = "high"
60
+ MEDIUM = "medium"
61
+ LOW = "low"
62
+ INFO = "info"
63
+
64
+
65
+ class ConfidenceLevel(StrEnum):
66
+ """Confidence levels for AI analysis results."""
67
+
68
+ VERY_HIGH = "very_high" # 90-100%
69
+ HIGH = "high" # 75-89%
70
+ MEDIUM = "medium" # 50-74%
71
+ LOW = "low" # 25-49%
72
+ VERY_LOW = "very_low" # 0-24%
73
+
74
+
75
+ class TestFileMetadata(BaseModel):
76
+ """Metadata about a test file."""
77
+
78
+ model_config = ConfigDict(
79
+ str_strip_whitespace=True,
80
+ validate_default=True,
81
+ use_enum_values=True,
82
+ extra="forbid",
83
+ )
84
+
85
+ file_path: str = Field(..., description="Path to the test file", min_length=1, max_length=1000)
86
+
87
+ file_size: int | None = Field(default=None, description="Size of the test file in bytes", ge=0)
88
+
89
+ line_count: int | None = Field(
90
+ default=None, description="Number of lines in the test file", ge=0
91
+ )
92
+
93
+ last_modified: datetime | None = Field(default=None, description="Last modification timestamp")
94
+
95
+ framework: TestFramework = Field(
96
+ default=TestFramework.UNKNOWN, description="Test framework used in the file"
97
+ )
98
+
99
+ test_count: int | None = Field(default=None, description="Number of tests in the file", ge=0)
100
+
101
+
102
+ class TestFailureContext(BaseModel):
103
+ """Context information about a test failure."""
104
+
105
+ model_config = ConfigDict(
106
+ str_strip_whitespace=True,
107
+ validate_default=True,
108
+ use_enum_values=True,
109
+ extra="forbid",
110
+ )
111
+
112
+ test_name: str = Field(
113
+ ..., description="Name of the failing test", min_length=1, max_length=200
114
+ )
115
+
116
+ test_class: str | None = Field(
117
+ default=None, description="Test class name if applicable", max_length=200
118
+ )
119
+
120
+ test_method: str | None = Field(
121
+ default=None, description="Test method name if applicable", max_length=200
122
+ )
123
+
124
+ test_file: TestFileMetadata | None = Field(
125
+ default=None, description="Metadata about the test file"
126
+ )
127
+
128
+ source_file: str | None = Field(
129
+ default=None,
130
+ description="Path to the source file being tested",
131
+ max_length=1000,
132
+ )
133
+
134
+ test_line_number: int | None = Field(
135
+ default=None, description="Line number where the test failed", ge=1
136
+ )
137
+
138
+ framework: TestFramework = Field(
139
+ default=TestFramework.UNKNOWN, description="Test framework used"
140
+ )
141
+
142
+ execution_environment: dict[str, Any] | None = Field(
143
+ default=None, description="Environment details (OS, Python version, etc.)"
144
+ )
145
+
146
+
147
+ class TestFailureData(BaseModel):
148
+ """Raw test failure data and output."""
149
+
150
+ model_config = ConfigDict(
151
+ str_strip_whitespace=False, # Preserve formatting in output
152
+ validate_default=True,
153
+ use_enum_values=True,
154
+ extra="forbid",
155
+ )
156
+
157
+ failure_id: str = Field(
158
+ ...,
159
+ description="Unique identifier for this test failure",
160
+ min_length=1,
161
+ max_length=100,
162
+ )
163
+
164
+ timestamp: datetime = Field(
165
+ default_factory=lambda: datetime.now(UTC),
166
+ description="When the test failure occurred",
167
+ )
168
+
169
+ raw_output: str = Field(
170
+ ...,
171
+ description="Raw test output including failure details",
172
+ max_length=100_000, # 100KB limit
173
+ )
174
+
175
+ exit_code: int | None = Field(default=None, description="Exit code from test execution")
176
+
177
+ execution_time: float | None = Field(
178
+ default=None, description="Test execution time in seconds", ge=0
179
+ )
180
+
181
+ memory_usage: int | None = Field(default=None, description="Memory usage in bytes", ge=0)
182
+
183
+ context: TestFailureContext = Field(
184
+ ..., description="Context information about the test failure"
185
+ )
186
+
187
+ @field_validator("failure_id")
188
+ @classmethod
189
+ def validate_failure_id(cls, v: str) -> str:
190
+ """Validate that failure_id contains only safe characters."""
191
+ if not re.match(r"^[a-zA-Z0-9_-]+$", v):
192
+ raise ValueError(
193
+ "failure_id must contain only alphanumeric characters, hyphens, and underscores"
194
+ )
195
+ return v
196
+
197
+
198
+ class ErrorDetails(BaseModel):
199
+ """Detailed information about a specific error."""
200
+
201
+ model_config = ConfigDict(
202
+ str_strip_whitespace=True,
203
+ validate_default=True,
204
+ use_enum_values=True,
205
+ extra="forbid",
206
+ )
207
+
208
+ error_type: FailureType = Field(..., description="Type of error that occurred")
209
+
210
+ error_message: str = Field(..., description="Error message text", max_length=5000)
211
+
212
+ traceback: str | None = Field(
213
+ default=None, description="Full traceback if available", max_length=50_000
214
+ )
215
+
216
+ file_path: str | None = Field(
217
+ default=None, description="File where the error occurred", max_length=1000
218
+ )
219
+
220
+ line_number: int | None = Field(
221
+ default=None, description="Line number where the error occurred", ge=1
222
+ )
223
+
224
+ column_number: int | None = Field(
225
+ default=None, description="Column number where the error occurred", ge=1
226
+ )
227
+
228
+ stack_trace: list[str] | None = Field(default=None, description="Parsed stack trace entries")
229
+
230
+
231
+ class AssertionDetails(BaseModel):
232
+ """Details about assertion failures."""
233
+
234
+ model_config = ConfigDict(
235
+ str_strip_whitespace=True,
236
+ validate_default=True,
237
+ use_enum_values=True,
238
+ extra="forbid",
239
+ )
240
+
241
+ assertion_type: Literal[
242
+ "assertEqual",
243
+ "assertNotEqual",
244
+ "assertTrue",
245
+ "assertFalse",
246
+ "assertIs",
247
+ "assertIsNot",
248
+ "assertIsNone",
249
+ "assertIsNotNone",
250
+ "assertIn",
251
+ "assertNotIn",
252
+ "assertRaises",
253
+ "assertRaisesRegex",
254
+ "assertAlmostEqual",
255
+ "assertNotAlmostEqual",
256
+ "assertGreater",
257
+ "assertGreaterEqual",
258
+ "assertLess",
259
+ "assertLessEqual",
260
+ "assertRegex",
261
+ "assertNotRegex",
262
+ "assertCountEqual",
263
+ "assertMultiLineEqual",
264
+ "assertSequenceEqual",
265
+ "assertListEqual",
266
+ "assertTupleEqual",
267
+ "assertSetEqual",
268
+ "assertDictEqual",
269
+ "custom",
270
+ "unknown",
271
+ ] = Field(default="unknown", description="Type of assertion that failed")
272
+
273
+ expected_value: str | None = Field(
274
+ default=None, description="Expected value in the assertion", max_length=10_000
275
+ )
276
+
277
+ actual_value: str | None = Field(
278
+ default=None,
279
+ description="Actual value that caused the failure",
280
+ max_length=10_000,
281
+ )
282
+
283
+ comparison_operator: str | None = Field(
284
+ default=None,
285
+ description="Comparison operator used (==, !=, <, >, etc.)",
286
+ max_length=10,
287
+ )
288
+
289
+ assertion_message: str | None = Field(
290
+ default=None,
291
+ description="Custom assertion message if provided",
292
+ max_length=1000,
293
+ )
294
+
295
+ value_difference: str | None = Field(
296
+ default=None,
297
+ description="Calculated difference between expected and actual values",
298
+ max_length=10_000,
299
+ )
300
+
301
+
302
+ class RootCause(BaseModel):
303
+ """Identified root cause of a test failure."""
304
+
305
+ model_config = ConfigDict(
306
+ str_strip_whitespace=True,
307
+ validate_default=True,
308
+ use_enum_values=True,
309
+ extra="forbid",
310
+ )
311
+
312
+ cause_id: str = Field(
313
+ ...,
314
+ description="Unique identifier for this root cause",
315
+ min_length=1,
316
+ max_length=100,
317
+ )
318
+
319
+ category: Literal[
320
+ "logic_error",
321
+ "type_mismatch",
322
+ "null_reference",
323
+ "index_out_of_bounds",
324
+ "configuration_issue",
325
+ "dependency_missing",
326
+ "environment_issue",
327
+ "data_validation",
328
+ "race_condition",
329
+ "resource_exhaustion",
330
+ "network_connectivity",
331
+ "permission_denied",
332
+ "timeout",
333
+ "incompatible_versions",
334
+ "missing_initialization",
335
+ "state_corruption",
336
+ "external_service_failure",
337
+ "file_system_error",
338
+ "unknown",
339
+ ] = Field(..., description="Category of the root cause")
340
+
341
+ description: str = Field(
342
+ ...,
343
+ description="Human-readable description of the root cause",
344
+ min_length=10,
345
+ max_length=1000,
346
+ )
347
+
348
+ severity: SeverityLevel = Field(..., description="Severity level of this root cause")
349
+
350
+ confidence: ConfidenceLevel = Field(
351
+ ..., description="Confidence level in this root cause identification"
352
+ )
353
+
354
+ confidence_score: float = Field(
355
+ ..., description="Numerical confidence score (0.0 to 1.0)", ge=0.0, le=1.0
356
+ )
357
+
358
+ evidence: list[str] = Field(
359
+ default_factory=list,
360
+ description="Evidence supporting this root cause identification",
361
+ )
362
+
363
+ related_patterns: list[str] | None = Field(
364
+ default=None, description="Related failure patterns or signatures"
365
+ )
366
+
367
+ @field_validator("cause_id")
368
+ @classmethod
369
+ def validate_cause_id(cls, v: str) -> str:
370
+ """Validate that cause_id contains only safe characters."""
371
+ if not re.match(r"^[a-zA-Z0-9_-]+$", v):
372
+ raise ValueError(
373
+ "cause_id must contain only alphanumeric characters, hyphens, and underscores"
374
+ )
375
+ return v
376
+
377
+ @model_validator(mode="after")
378
+ def validate_confidence_consistency(self) -> RootCause:
379
+ """Ensure confidence level matches confidence score."""
380
+ score = self.confidence_score
381
+ level = self.confidence
382
+
383
+ if level == ConfidenceLevel.VERY_HIGH and not (0.9 <= score <= 1.0):
384
+ raise ValueError("VERY_HIGH confidence must have score 0.9-1.0")
385
+ elif level == ConfidenceLevel.HIGH and not (0.75 <= score < 0.9):
386
+ raise ValueError("HIGH confidence must have score 0.75-0.89")
387
+ elif level == ConfidenceLevel.MEDIUM and not (0.5 <= score < 0.75):
388
+ raise ValueError("MEDIUM confidence must have score 0.5-0.74")
389
+ elif level == ConfidenceLevel.LOW and not (0.25 <= score < 0.5):
390
+ raise ValueError("LOW confidence must have score 0.25-0.49")
391
+ elif level == ConfidenceLevel.VERY_LOW and not (0.0 <= score < 0.25):
392
+ raise ValueError("VERY_LOW confidence must have score 0.0-0.24")
393
+
394
+ return self
395
+
396
+
397
+ class FixSuggestion(BaseModel):
398
+ """AI-generated suggestion for resolving a test failure."""
399
+
400
+ model_config = ConfigDict(
401
+ str_strip_whitespace=True,
402
+ validate_default=True,
403
+ use_enum_values=True,
404
+ extra="forbid",
405
+ )
406
+
407
+ suggestion_id: str = Field(
408
+ ...,
409
+ description="Unique identifier for this suggestion",
410
+ min_length=1,
411
+ max_length=100,
412
+ )
413
+
414
+ title: str = Field(
415
+ ...,
416
+ description="Brief title describing the suggestion",
417
+ min_length=5,
418
+ max_length=200,
419
+ )
420
+
421
+ description: str = Field(
422
+ ...,
423
+ description="Detailed description of the suggested action",
424
+ min_length=20,
425
+ max_length=2000,
426
+ )
427
+
428
+ fix_type: Literal[
429
+ "code_change",
430
+ "configuration_change",
431
+ "dependency_update",
432
+ "environment_setup",
433
+ "test_modification",
434
+ "data_correction",
435
+ "infrastructure_change",
436
+ "documentation_update",
437
+ "process_change",
438
+ "investigation_needed",
439
+ "unknown",
440
+ ] = Field(..., description="Type of action required to implement the suggestion")
441
+
442
+ priority: Literal["critical", "high", "medium", "low"] = Field(
443
+ ..., description="Priority level for implementing this suggestion"
444
+ )
445
+
446
+ confidence: ConfidenceLevel = Field(..., description="Confidence level in this suggestion")
447
+
448
+ confidence_score: float = Field(
449
+ ..., description="Numerical confidence score (0.0 to 1.0)", ge=0.0, le=1.0
450
+ )
451
+
452
+ estimated_effort: Literal["trivial", "low", "medium", "high", "very_high"] = Field(
453
+ ..., description="Estimated effort required to implement"
454
+ )
455
+
456
+ code_examples: list[str] | None = Field(
457
+ default=None, description="Code examples demonstrating the suggestion"
458
+ )
459
+
460
+ files_to_modify: list[str] | None = Field(
461
+ default=None, description="List of files that need to be modified"
462
+ )
463
+
464
+ prerequisites: list[str] | None = Field(
465
+ default=None, description="Prerequisites that must be met before implementing"
466
+ )
467
+
468
+ potential_side_effects: list[str] | None = Field(
469
+ default=None,
470
+ description="Potential side effects of implementing this suggestion",
471
+ )
472
+
473
+ verification_steps: list[str] | None = Field(
474
+ default=None,
475
+ description="Steps to verify the suggestion was implemented correctly",
476
+ )
477
+
478
+ @field_validator("suggestion_id")
479
+ @classmethod
480
+ def validate_suggestion_id(cls, v: str) -> str:
481
+ """Validate that suggestion_id contains only safe characters."""
482
+ if not re.match(r"^[a-zA-Z0-9_-]+$", v):
483
+ raise ValueError(
484
+ "suggestion_id must contain only alphanumeric characters, hyphens, and underscores"
485
+ )
486
+ return v
487
+
488
+
489
+ class TestFailurePattern(BaseModel):
490
+ """Pattern detected in test failures for classification."""
491
+
492
+ model_config = ConfigDict(
493
+ str_strip_whitespace=True,
494
+ validate_default=True,
495
+ use_enum_values=True,
496
+ extra="forbid",
497
+ )
498
+
499
+ pattern_id: str = Field(
500
+ ...,
501
+ description="Unique identifier for this pattern",
502
+ min_length=1,
503
+ max_length=100,
504
+ )
505
+
506
+ pattern_name: str = Field(
507
+ ...,
508
+ description="Human-readable name for the pattern",
509
+ min_length=5,
510
+ max_length=200,
511
+ )
512
+
513
+ pattern_signature: str = Field(
514
+ ...,
515
+ description="Signature or fingerprint of the pattern",
516
+ min_length=10,
517
+ max_length=500,
518
+ )
519
+
520
+ pattern_type: Literal[
521
+ "error_message",
522
+ "stack_trace",
523
+ "assertion_pattern",
524
+ "execution_flow",
525
+ "timing_pattern",
526
+ "resource_usage",
527
+ "configuration_pattern",
528
+ "dependency_pattern",
529
+ "unknown",
530
+ ] = Field(..., description="Type of pattern detected")
531
+
532
+ frequency: int = Field(
533
+ default=1, description="How many times this pattern has been observed", ge=1
534
+ )
535
+
536
+ confidence: ConfidenceLevel = Field(..., description="Confidence in pattern detection")
537
+
538
+ confidence_score: float = Field(
539
+ ..., description="Numerical confidence score (0.0 to 1.0)", ge=0.0, le=1.0
540
+ )
541
+
542
+ associated_failures: list[str] | None = Field(
543
+ default=None, description="List of failure IDs that match this pattern"
544
+ )
545
+
546
+ typical_causes: list[str] | None = Field(
547
+ default=None, description="Common causes associated with this pattern"
548
+ )
549
+
550
+ common_fixes: list[str] | None = Field(
551
+ default=None, description="Common fixes for this pattern"
552
+ )
553
+
554
+
555
+ class AIAnalysisResult(BaseModel):
556
+ """Complete AI analysis result for a test failure."""
557
+
558
+ model_config = ConfigDict(
559
+ str_strip_whitespace=True,
560
+ validate_default=True,
561
+ use_enum_values=True,
562
+ extra="forbid",
563
+ )
564
+
565
+ analysis_id: str = Field(
566
+ ...,
567
+ description="Unique identifier for this analysis",
568
+ min_length=1,
569
+ max_length=100,
570
+ )
571
+
572
+ timestamp: datetime = Field(
573
+ default_factory=lambda: datetime.now(UTC),
574
+ description="When the analysis was performed",
575
+ )
576
+
577
+ failure_data: TestFailureData = Field(..., description="Original test failure data")
578
+
579
+ error_details: ErrorDetails = Field(..., description="Detailed error information")
580
+
581
+ assertion_details: AssertionDetails | None = Field(
582
+ default=None, description="Assertion-specific details if applicable"
583
+ )
584
+
585
+ root_causes: list[RootCause] = Field(
586
+ default_factory=list, description="Identified root causes ranked by confidence"
587
+ )
588
+
589
+ fix_suggestions: list[FixSuggestion] = Field(
590
+ default_factory=list,
591
+ description="AI-generated suggestions for resolving the failure",
592
+ )
593
+
594
+ detected_patterns: list[TestFailurePattern] = Field(
595
+ default_factory=list, description="Patterns detected in the failure"
596
+ )
597
+
598
+ analysis_confidence: ConfidenceLevel = Field(
599
+ ..., description="Overall confidence in the analysis"
600
+ )
601
+
602
+ analysis_confidence_score: float = Field(
603
+ ..., description="Numerical confidence score (0.0 to 1.0)", ge=0.0, le=1.0
604
+ )
605
+
606
+ processing_time: float | None = Field(
607
+ default=None, description="Time taken to perform the analysis in seconds", ge=0
608
+ )
609
+
610
+ ai_model_version: str | None = Field(
611
+ default=None,
612
+ description="Version of the AI model used for analysis",
613
+ max_length=100,
614
+ )
615
+
616
+ additional_metadata: dict[str, Any] | None = Field(
617
+ default=None, description="Additional metadata from the analysis"
618
+ )
619
+
620
+ @field_validator("analysis_id")
621
+ @classmethod
622
+ def validate_analysis_id(cls, v: str) -> str:
623
+ """Validate that analysis_id contains only safe characters."""
624
+ if not re.match(r"^[a-zA-Z0-9_-]+$", v):
625
+ raise ValueError(
626
+ "analysis_id must contain only alphanumeric characters, hyphens, and underscores"
627
+ )
628
+ return v
629
+
630
+ @field_validator("root_causes")
631
+ @classmethod
632
+ def validate_root_causes_sorted(cls, v: list[RootCause]) -> list[RootCause]:
633
+ """Ensure root causes are sorted by confidence score (highest first)."""
634
+ if len(v) > 1:
635
+ sorted_causes = sorted(v, key=lambda x: x.confidence_score, reverse=True)
636
+ return sorted_causes
637
+ return v
638
+
639
+ @field_validator("fix_suggestions")
640
+ @classmethod
641
+ def validate_fix_suggestions_sorted(cls, v: list[FixSuggestion]) -> list[FixSuggestion]:
642
+ """Ensure fix suggestions are sorted by confidence score (highest first)."""
643
+ if len(v) > 1:
644
+ sorted_suggestions = sorted(v, key=lambda x: x.confidence_score, reverse=True)
645
+ return sorted_suggestions
646
+ return v
647
+
648
+
649
+ class FailureAnalysisRequest(BaseModel):
650
+ """Request model for AI-powered test failure analysis."""
651
+
652
+ model_config = ConfigDict(
653
+ str_strip_whitespace=True,
654
+ validate_default=True,
655
+ use_enum_values=True,
656
+ extra="forbid",
657
+ )
658
+
659
+ request_id: str = Field(
660
+ ...,
661
+ description="Unique identifier for this analysis request",
662
+ min_length=1,
663
+ max_length=100,
664
+ )
665
+
666
+ failure_data: TestFailureData = Field(..., description="Test failure data to analyze")
667
+
668
+ analysis_options: dict[str, Any] | None = Field(
669
+ default=None, description="Options to customize the analysis"
670
+ )
671
+
672
+ include_patterns: bool = Field(
673
+ default=True, description="Whether to include pattern detection in the analysis"
674
+ )
675
+
676
+ include_suggestions: bool = Field(
677
+ default=True, description="Whether to include fix suggestions in the analysis"
678
+ )
679
+
680
+ max_root_causes: int = Field(
681
+ default=5, description="Maximum number of root causes to identify", ge=1, le=20
682
+ )
683
+
684
+ max_suggestions: int = Field(
685
+ default=10,
686
+ description="Maximum number of fix suggestions to generate",
687
+ ge=1,
688
+ le=50,
689
+ )
690
+
691
+ minimum_confidence: float = Field(
692
+ default=0.1,
693
+ description="Minimum confidence threshold for results",
694
+ ge=0.0,
695
+ le=1.0,
696
+ )
697
+
698
+ @field_validator("request_id")
699
+ @classmethod
700
+ def validate_request_id(cls, v: str) -> str:
701
+ """Validate that request_id contains only safe characters."""
702
+ if not re.match(r"^[a-zA-Z0-9_-]+$", v):
703
+ raise ValueError(
704
+ "request_id must contain only alphanumeric characters, hyphens, and underscores"
705
+ )
706
+ return v
707
+
708
+
709
+ class FailureAnalysisResponse(BaseModel):
710
+ """Response model for AI-powered test failure analysis."""
711
+
712
+ model_config = ConfigDict(
713
+ str_strip_whitespace=True,
714
+ validate_default=True,
715
+ use_enum_values=True,
716
+ extra="forbid",
717
+ )
718
+
719
+ request_id: str = Field(
720
+ ...,
721
+ description="Request ID that generated this response",
722
+ min_length=1,
723
+ max_length=100,
724
+ )
725
+
726
+ success: bool = Field(..., description="Whether the analysis was successful")
727
+
728
+ analysis_result: AIAnalysisResult | None = Field(
729
+ default=None, description="Complete analysis result if successful"
730
+ )
731
+
732
+ error_message: str | None = Field(
733
+ default=None, description="Error message if analysis failed", max_length=1000
734
+ )
735
+
736
+ warnings: list[str] | None = Field(
737
+ default=None, description="Warning messages from the analysis"
738
+ )
739
+
740
+ processing_time: float = Field(..., description="Total processing time in seconds", ge=0)
741
+
742
+ timestamp: datetime = Field(
743
+ default_factory=lambda: datetime.now(UTC),
744
+ description="When the response was generated",
745
+ )