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,395 @@
1
+ """
2
+ Custom API Exceptions
3
+
4
+ These exceptions automatically convert to standardized error responses
5
+ via the global exception handlers.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import Any
11
+
12
+ from fastapi import HTTPException, status
13
+
14
+
15
+ class BaseAPIException(HTTPException):
16
+ """
17
+ Base class for all API exceptions.
18
+
19
+ Provides consistent structure for error details.
20
+ """
21
+
22
+ def __init__(
23
+ self,
24
+ status_code: int,
25
+ message: str,
26
+ error_code: str,
27
+ details: dict[str, Any] | None = None,
28
+ headers: dict[str, str] | None = None,
29
+ ):
30
+ # Create structured detail dict
31
+ detail: dict[str, Any] = {"message": message, "error_code": error_code}
32
+ if details:
33
+ detail["details"] = details
34
+
35
+ super().__init__(status_code=status_code, detail=detail, headers=headers)
36
+ self.error_code = error_code
37
+ self.details = details
38
+ # For compatibility with global exception handler
39
+ self.error_details = details
40
+
41
+
42
+ class AgentConfigurationError(BaseAPIException):
43
+ """
44
+ Exception raised when AI agent configuration is invalid.
45
+ """
46
+
47
+ def __init__(
48
+ self,
49
+ message: str = "Agent configuration error",
50
+ error_code: str = "AGENT_CONFIG_ERROR",
51
+ details: dict[str, Any] | None = None,
52
+ ):
53
+ super().__init__(
54
+ status_code=status.HTTP_400_BAD_REQUEST,
55
+ message=message,
56
+ error_code=error_code,
57
+ details=details,
58
+ )
59
+
60
+
61
+ class AITimeoutError(BaseAPIException):
62
+ """
63
+ Exception raised when AI service request times out.
64
+ """
65
+
66
+ def __init__(
67
+ self,
68
+ message: str = "AI service timeout",
69
+ error_code: str = "AI_TIMEOUT",
70
+ details: dict[str, Any] | None = None,
71
+ ):
72
+ super().__init__(
73
+ status_code=status.HTTP_504_GATEWAY_TIMEOUT,
74
+ message=message,
75
+ error_code=error_code,
76
+ details=details,
77
+ )
78
+
79
+
80
+ class AIValidationError(BaseAPIException):
81
+ """
82
+ Exception raised when AI service input validation fails.
83
+ """
84
+
85
+ def __init__(
86
+ self,
87
+ message: str = "AI input validation error",
88
+ error_code: str = "AI_VALIDATION_ERROR",
89
+ details: dict[str, Any] | None = None,
90
+ ):
91
+ super().__init__(
92
+ status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
93
+ message=message,
94
+ error_code=error_code,
95
+ details=details,
96
+ )
97
+
98
+
99
+ class NotFoundException(BaseAPIException):
100
+ """
101
+ Exception for resource not found errors (404).
102
+ """
103
+
104
+ def __init__(
105
+ self,
106
+ message: str = "Resource not found",
107
+ error_code: str = "NOT_FOUND",
108
+ resource_type: str | None = None,
109
+ resource_id: str | None = None,
110
+ details: dict[str, Any] | None = None,
111
+ ):
112
+ # Add resource info to details
113
+ if resource_type or resource_id:
114
+ if details is None:
115
+ details = {}
116
+ if resource_type:
117
+ details["resource_type"] = resource_type
118
+ if resource_id:
119
+ details["resource_id"] = resource_id
120
+
121
+ super().__init__(
122
+ status_code=status.HTTP_404_NOT_FOUND,
123
+ message=message,
124
+ error_code=error_code,
125
+ details=details,
126
+ )
127
+
128
+
129
+ class ValidationException(BaseAPIException):
130
+ """
131
+ Exception for validation errors (400).
132
+
133
+ Use this for business logic validation, not request validation.
134
+ """
135
+
136
+ def __init__(
137
+ self,
138
+ message: str = "Validation failed",
139
+ error_code: str = "VALIDATION_FAILED",
140
+ field: str | None = None,
141
+ details: dict[str, Any] | None = None,
142
+ ):
143
+ # Add field info to details
144
+ if field:
145
+ if details is None:
146
+ details = {}
147
+ details["field"] = field
148
+
149
+ super().__init__(
150
+ status_code=status.HTTP_400_BAD_REQUEST,
151
+ message=message,
152
+ error_code=error_code,
153
+ details=details,
154
+ )
155
+
156
+
157
+ class AuthenticationException(BaseAPIException):
158
+ """
159
+ Exception for authentication failures (401).
160
+ """
161
+
162
+ def __init__(
163
+ self,
164
+ message: str = "Authentication required",
165
+ error_code: str = "UNAUTHORIZED",
166
+ details: dict[str, Any] | None = None,
167
+ headers: dict[str, str] | None = None,
168
+ ):
169
+ # Add WWW-Authenticate header if not present
170
+ if headers is None:
171
+ headers = {}
172
+ if "WWW-Authenticate" not in headers:
173
+ headers["WWW-Authenticate"] = "Bearer"
174
+
175
+ super().__init__(
176
+ status_code=status.HTTP_401_UNAUTHORIZED,
177
+ message=message,
178
+ error_code=error_code,
179
+ details=details,
180
+ headers=headers,
181
+ )
182
+
183
+
184
+ class PermissionException(BaseAPIException):
185
+ """
186
+ Exception for permission/authorization failures (403).
187
+ """
188
+
189
+ def __init__(
190
+ self,
191
+ message: str = "Insufficient permissions",
192
+ error_code: str = "FORBIDDEN",
193
+ required_permission: str | None = None,
194
+ details: dict[str, Any] | None = None,
195
+ ):
196
+ # Add permission info to details
197
+ if required_permission:
198
+ if details is None:
199
+ details = {}
200
+ details["required_permission"] = required_permission
201
+
202
+ super().__init__(
203
+ status_code=status.HTTP_403_FORBIDDEN,
204
+ message=message,
205
+ error_code=error_code,
206
+ details=details,
207
+ )
208
+
209
+
210
+ class ConflictException(BaseAPIException):
211
+ """
212
+ Exception for resource conflicts (409).
213
+ """
214
+
215
+ def __init__(
216
+ self,
217
+ message: str = "Resource conflict",
218
+ error_code: str = "CONFLICT",
219
+ conflicting_resource: str | None = None,
220
+ details: dict[str, Any] | None = None,
221
+ ):
222
+ # Add conflict info to details
223
+ if conflicting_resource:
224
+ if details is None:
225
+ details = {}
226
+ details["conflicting_resource"] = conflicting_resource
227
+
228
+ super().__init__(
229
+ status_code=status.HTTP_409_CONFLICT,
230
+ message=message,
231
+ error_code=error_code,
232
+ details=details,
233
+ )
234
+
235
+
236
+ class ServiceUnavailableException(BaseAPIException):
237
+ """
238
+ Exception for service unavailable errors (503).
239
+ """
240
+
241
+ def __init__(
242
+ self,
243
+ message: str = "Service temporarily unavailable",
244
+ error_code: str = "SERVICE_UNAVAILABLE",
245
+ retry_after: int | None = None,
246
+ details: dict[str, Any] | None = None,
247
+ headers: dict[str, str] | None = None,
248
+ ):
249
+ # Add Retry-After header if specified
250
+ if retry_after:
251
+ if headers is None:
252
+ headers = {}
253
+ headers["Retry-After"] = str(retry_after)
254
+ if details is None:
255
+ details = {}
256
+ details["retry_after_seconds"] = retry_after
257
+
258
+ super().__init__(
259
+ status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
260
+ message=message,
261
+ error_code=error_code,
262
+ details=details,
263
+ headers=headers,
264
+ )
265
+
266
+
267
+ class RateLimitException(BaseAPIException):
268
+ """
269
+ Exception for rate limit errors (429).
270
+ """
271
+
272
+ def __init__(
273
+ self,
274
+ message: str = "Rate limit exceeded",
275
+ error_code: str = "RATE_LIMIT_EXCEEDED",
276
+ retry_after: int | None = None,
277
+ limit: int | None = None,
278
+ remaining: int | None = None,
279
+ reset: int | None = None,
280
+ details: dict[str, Any] | None = None,
281
+ headers: dict[str, str] | None = None,
282
+ ):
283
+ if details is None:
284
+ details = {}
285
+
286
+ # Add rate limit info to details
287
+ if limit is not None:
288
+ details["limit"] = limit
289
+ if remaining is not None:
290
+ details["remaining"] = remaining
291
+ if reset is not None:
292
+ details["reset"] = reset
293
+
294
+ # Add rate limit headers
295
+ if headers is None:
296
+ headers = {}
297
+ if retry_after:
298
+ headers["Retry-After"] = str(retry_after)
299
+ if limit is not None:
300
+ headers["X-RateLimit-Limit"] = str(limit)
301
+ if remaining is not None:
302
+ headers["X-RateLimit-Remaining"] = str(remaining)
303
+ if reset is not None:
304
+ headers["X-RateLimit-Reset"] = str(reset)
305
+
306
+ super().__init__(
307
+ status_code=status.HTTP_429_TOO_MANY_REQUESTS,
308
+ message=message,
309
+ error_code=error_code,
310
+ details=details,
311
+ headers=headers,
312
+ )
313
+
314
+
315
+ class ProcessingException(BaseAPIException):
316
+ """
317
+ Exception for processing errors (422).
318
+
319
+ Use this when the request is valid but cannot be processed.
320
+ """
321
+
322
+ def __init__(
323
+ self,
324
+ message: str = "Unable to process request",
325
+ error_code: str = "UNPROCESSABLE_ENTITY",
326
+ reason: str | None = None,
327
+ details: dict[str, Any] | None = None,
328
+ ):
329
+ # Add reason to details
330
+ if reason:
331
+ if details is None:
332
+ details = {}
333
+ details["reason"] = reason
334
+
335
+ super().__init__(
336
+ status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
337
+ message=message,
338
+ error_code=error_code,
339
+ details=details,
340
+ )
341
+
342
+
343
+ class InternalServerException(BaseAPIException):
344
+ """
345
+ Exception for internal server errors (500).
346
+
347
+ Use sparingly - most errors should have more specific types.
348
+ """
349
+
350
+ def __init__(
351
+ self,
352
+ message: str = "Internal server error",
353
+ error_code: str = "INTERNAL_ERROR",
354
+ details: dict[str, Any] | None = None,
355
+ ):
356
+ super().__init__(
357
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
358
+ message=message,
359
+ error_code=error_code,
360
+ details=details,
361
+ )
362
+
363
+
364
+ class ContainerNotFoundException(BaseAPIException):
365
+ """
366
+ Exception for when a container is not found.
367
+ """
368
+
369
+ def __init__(
370
+ self,
371
+ container_name: str,
372
+ message: str | None = None,
373
+ error_code: str = "CONTAINER_NOT_FOUND",
374
+ details: dict[str, Any] | None = None,
375
+ ):
376
+ if message is None:
377
+ message = f"Container '{container_name}' not found"
378
+
379
+ if details is None:
380
+ details = {}
381
+
382
+ # Add container info to details
383
+ details.update(
384
+ {
385
+ "resource_type": "container",
386
+ "resource_id": container_name,
387
+ }
388
+ )
389
+
390
+ super().__init__(
391
+ status_code=status.HTTP_400_BAD_REQUEST,
392
+ message=message,
393
+ error_code=error_code,
394
+ details=details,
395
+ )