code-analysis-client-dev 1.6.341__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.
- code_analysis_client/__init__.py +142 -0
- code_analysis_client/check_chain.py +374 -0
- code_analysis_client/client.py +728 -0
- code_analysis_client/commands_proxy.py +63 -0
- code_analysis_client/config.py +198 -0
- code_analysis_client/exceptions.py +188 -0
- code_analysis_client/file_session.py +823 -0
- code_analysis_client/project.py +59 -0
- code_analysis_client/py.typed +0 -0
- code_analysis_client/queue_wait.py +341 -0
- code_analysis_client/responses.py +126 -0
- code_analysis_client/schema_disk_cache.py +207 -0
- code_analysis_client/server_api.py +291 -0
- code_analysis_client/server_schema.py +57 -0
- code_analysis_client/symbol_tree.py +13 -0
- code_analysis_client/universal_file.py +50 -0
- code_analysis_client/validation.py +98 -0
- code_analysis_client/vectorizer_state.py +159 -0
- code_analysis_client/version.txt +1 -0
- code_analysis_client/worktree.py +533 -0
- code_analysis_client_dev-1.6.341.dist-info/METADATA +251 -0
- code_analysis_client_dev-1.6.341.dist-info/RECORD +24 -0
- code_analysis_client_dev-1.6.341.dist-info/WHEEL +5 -0
- code_analysis_client_dev-1.6.341.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Shallow JSON-schema validation aligned with BaseMCPCommand.validate_params_against_schema.
|
|
3
|
+
|
|
4
|
+
Author: Vasiliy Zdanovskiy
|
|
5
|
+
email: vasilyvz@gmail.com
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Any, Dict
|
|
11
|
+
|
|
12
|
+
from code_analysis_client.exceptions import ClientValidationError
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def prepare_params_for_schema(
|
|
16
|
+
params: Dict[str, Any], schema: Dict[str, Any] # noqa: ARG001
|
|
17
|
+
) -> Dict[str, Any]:
|
|
18
|
+
"""Return a shallow copy of params for validation and dispatch."""
|
|
19
|
+
return dict(params)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def validate_params_against_schema(
|
|
23
|
+
params: Dict[str, Any],
|
|
24
|
+
schema: Dict[str, Any],
|
|
25
|
+
command_name: str = "command",
|
|
26
|
+
) -> None:
|
|
27
|
+
"""Validate params against a command ``get_schema()``-style object (subset)."""
|
|
28
|
+
if not isinstance(params, dict):
|
|
29
|
+
raise ClientValidationError(
|
|
30
|
+
f"{command_name}: params must be a dict, got {type(params).__name__}",
|
|
31
|
+
field="params",
|
|
32
|
+
)
|
|
33
|
+
props = schema.get("properties") or {}
|
|
34
|
+
additional_ok = schema.get("additionalProperties", False)
|
|
35
|
+
required_set = set(schema.get("required") or [])
|
|
36
|
+
for key, value in params.items():
|
|
37
|
+
if key not in props:
|
|
38
|
+
if not additional_ok:
|
|
39
|
+
raise ClientValidationError(
|
|
40
|
+
f"{command_name}: unknown parameter {key!r}. "
|
|
41
|
+
"Only schema-defined properties are allowed.",
|
|
42
|
+
field=key,
|
|
43
|
+
details={"allowed": list(props.keys())},
|
|
44
|
+
)
|
|
45
|
+
continue
|
|
46
|
+
if value is None:
|
|
47
|
+
continue
|
|
48
|
+
prop = props[key]
|
|
49
|
+
expected_type = prop.get("type")
|
|
50
|
+
if expected_type == "string":
|
|
51
|
+
if not isinstance(value, str):
|
|
52
|
+
raise ClientValidationError(
|
|
53
|
+
f"{command_name}: parameter {key!r} must be string, got {type(value).__name__}",
|
|
54
|
+
field=key,
|
|
55
|
+
)
|
|
56
|
+
elif expected_type == "integer":
|
|
57
|
+
if not isinstance(value, int) or isinstance(value, bool):
|
|
58
|
+
raise ClientValidationError(
|
|
59
|
+
f"{command_name}: parameter {key!r} must be integer, got {type(value).__name__}",
|
|
60
|
+
field=key,
|
|
61
|
+
)
|
|
62
|
+
elif expected_type == "number":
|
|
63
|
+
if not isinstance(value, (int, float)) or isinstance(value, bool):
|
|
64
|
+
raise ClientValidationError(
|
|
65
|
+
f"{command_name}: parameter {key!r} must be number, got {type(value).__name__}",
|
|
66
|
+
field=key,
|
|
67
|
+
)
|
|
68
|
+
elif expected_type == "boolean":
|
|
69
|
+
if not isinstance(value, bool):
|
|
70
|
+
raise ClientValidationError(
|
|
71
|
+
f"{command_name}: parameter {key!r} must be boolean, got {type(value).__name__}",
|
|
72
|
+
field=key,
|
|
73
|
+
)
|
|
74
|
+
elif expected_type == "array":
|
|
75
|
+
if not isinstance(value, list):
|
|
76
|
+
raise ClientValidationError(
|
|
77
|
+
f"{command_name}: parameter {key!r} must be array, got {type(value).__name__}",
|
|
78
|
+
field=key,
|
|
79
|
+
)
|
|
80
|
+
elif expected_type == "object":
|
|
81
|
+
if not isinstance(value, dict):
|
|
82
|
+
raise ClientValidationError(
|
|
83
|
+
f"{command_name}: parameter {key!r} must be object, got {type(value).__name__}",
|
|
84
|
+
field=key,
|
|
85
|
+
)
|
|
86
|
+
if "enum" in prop and value is not None:
|
|
87
|
+
if value not in prop["enum"]:
|
|
88
|
+
raise ClientValidationError(
|
|
89
|
+
f"{command_name}: parameter {key!r} must be one of {prop['enum']!r}, got {value!r}",
|
|
90
|
+
field=key,
|
|
91
|
+
details={"enum": prop["enum"]},
|
|
92
|
+
)
|
|
93
|
+
for key in required_set:
|
|
94
|
+
if key not in params or params[key] is None:
|
|
95
|
+
raise ClientValidationError(
|
|
96
|
+
f"{command_name}: required parameter {key!r} is missing",
|
|
97
|
+
field=key,
|
|
98
|
+
)
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"""Typed models for vectorizer availability state from health and get_worker_status.
|
|
2
|
+
|
|
3
|
+
The vectorizer_availability section of pipeline/matrix.yaml defines the contract.
|
|
4
|
+
Clients must tolerate absence of these fields (deployed 1.6.190 server does not emit
|
|
5
|
+
them yet) without raising.
|
|
6
|
+
|
|
7
|
+
Author: Vasiliy Zdanovskiy
|
|
8
|
+
email: vasilyvz@gmail.com
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from typing import Any, Dict, List, Optional, TypedDict
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ServiceState(TypedDict, total=False):
|
|
17
|
+
"""State of one vectorizer service (embedding or chunker).
|
|
18
|
+
|
|
19
|
+
All fields are optional to tolerate absence in pre-V1 deployments.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
state: str # "available", "unavailable", or "unknown"
|
|
23
|
+
since: str # ISO-8601 UTC timestamp
|
|
24
|
+
last_probe_at: Optional[str] # ISO-8601 UTC timestamp or null
|
|
25
|
+
last_ok_at: Optional[str] # ISO-8601 UTC timestamp or null
|
|
26
|
+
reason: Optional[str] # One of reason_enum or null
|
|
27
|
+
consecutive_failures: int
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class VectorizerState(TypedDict, total=False):
|
|
31
|
+
"""Shared vectorizer availability state read by health and get_worker_status.
|
|
32
|
+
|
|
33
|
+
All fields are optional to tolerate absence in pre-V1 deployments.
|
|
34
|
+
Fields are presented as TypedDict with total=False; callers use .get()
|
|
35
|
+
to safely access optional fields.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
state: str # "available", "unavailable", or "unknown"
|
|
39
|
+
paused: bool # True when state == unavailable
|
|
40
|
+
paused_reason: Optional[str] # Reason from the earliest-failing unavailable service
|
|
41
|
+
paused_since: Optional[str] # ISO-8601 UTC, from the earliest-failing service
|
|
42
|
+
services: Dict[str, ServiceState] # {embedding: ..., chunker: ...}
|
|
43
|
+
probe_interval_seconds: int
|
|
44
|
+
probe_timeout_seconds: int
|
|
45
|
+
staleness_bound_seconds: int
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class VectorizerWarning(TypedDict, total=False):
|
|
49
|
+
"""A warning from health.data.warnings or get_worker_status.data.warnings.
|
|
50
|
+
|
|
51
|
+
Only populated for VECTORIZER_UNAVAILABLE warnings.
|
|
52
|
+
All fields are optional to tolerate absence.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
code: str # "VECTORIZER_UNAVAILABLE"
|
|
56
|
+
service: str # "embedding" or "chunker"
|
|
57
|
+
since: str # ISO-8601 UTC timestamp
|
|
58
|
+
reason: Optional[str] # Reason enum value
|
|
59
|
+
message: str # Human-readable text
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class VectorizationWorkerBlock(TypedDict, total=False):
|
|
63
|
+
"""The vectorization worker block from get_worker_status.
|
|
64
|
+
|
|
65
|
+
Present when worker_type in {vectorization, all} or omitted (defaults to all).
|
|
66
|
+
All fields are optional to tolerate absence in pre-V1 deployments.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
paused: bool # True when vectorizer state == unavailable
|
|
70
|
+
paused_reason: Optional[str] # Reason from the unavailable service
|
|
71
|
+
paused_since: Optional[str] # ISO-8601 UTC
|
|
72
|
+
last_heartbeat_at: str # ISO-8601 UTC
|
|
73
|
+
heartbeat_age_seconds: float # Age of the last heartbeat
|
|
74
|
+
rpc_in_flight: Optional[Dict[str, Any]] # {service, started_at, elapsed_seconds}
|
|
75
|
+
dead_letter: Dict[str, Any] # {total, by_reason}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def safe_get_vectorizer_state(data: Any) -> Optional[VectorizerState]:
|
|
79
|
+
"""Extract vectorizer state from a response dict, tolerating absence.
|
|
80
|
+
|
|
81
|
+
Supports both response formats:
|
|
82
|
+
- health: data.components.vectorizer
|
|
83
|
+
- get_worker_status: data.vectorizer
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
data: Response dict (e.g., from health or get_worker_status)
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
VectorizerState dict if present, None if absent
|
|
90
|
+
"""
|
|
91
|
+
if not isinstance(data, dict):
|
|
92
|
+
return None
|
|
93
|
+
|
|
94
|
+
# Try direct path first (get_worker_status)
|
|
95
|
+
vectorizer = data.get("vectorizer")
|
|
96
|
+
if isinstance(vectorizer, dict):
|
|
97
|
+
return vectorizer # type: ignore
|
|
98
|
+
|
|
99
|
+
# Try components path (health)
|
|
100
|
+
components = data.get("components")
|
|
101
|
+
if isinstance(components, dict):
|
|
102
|
+
vectorizer = components.get("vectorizer")
|
|
103
|
+
if isinstance(vectorizer, dict):
|
|
104
|
+
return vectorizer # type: ignore
|
|
105
|
+
|
|
106
|
+
return None
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def safe_get_warnings(data: Any) -> List[VectorizerWarning]:
|
|
110
|
+
"""Extract warnings list from a response dict, tolerating absence.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
data: Response dict (e.g., from health or get_worker_status)
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
List of VectorizerWarning dicts, empty list if absent
|
|
117
|
+
"""
|
|
118
|
+
if not isinstance(data, dict):
|
|
119
|
+
return []
|
|
120
|
+
warnings = data.get("warnings")
|
|
121
|
+
if not isinstance(warnings, list):
|
|
122
|
+
return []
|
|
123
|
+
return warnings # type: ignore
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def safe_get_vectorization_worker(data: Any) -> Optional[VectorizationWorkerBlock]:
|
|
127
|
+
"""Extract vectorization worker block from response data, tolerating absence.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
data: Worker status data dict (e.g., from get_worker_status)
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
VectorizationWorkerBlock dict if present, None if absent
|
|
134
|
+
"""
|
|
135
|
+
if not isinstance(data, dict):
|
|
136
|
+
return None
|
|
137
|
+
# The worker block is in the response at the same level as other worker types
|
|
138
|
+
# when worker_type is vectorization or all (or omitted which defaults to all)
|
|
139
|
+
worker = data.get("vectorization")
|
|
140
|
+
if not isinstance(worker, dict):
|
|
141
|
+
return None
|
|
142
|
+
return worker # type: ignore
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def extract_vectorizer_state_from_response(response: Dict[str, Any]) -> Optional[VectorizerState]:
|
|
146
|
+
"""Extract vectorizer state from any response that may contain it.
|
|
147
|
+
|
|
148
|
+
Checks both data.components.vectorizer (health/get_worker_status) paths.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
response: Response dict from a command
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
VectorizerState dict if found, None otherwise
|
|
155
|
+
"""
|
|
156
|
+
if not isinstance(response, dict):
|
|
157
|
+
return None
|
|
158
|
+
data = response.get("data")
|
|
159
|
+
return safe_get_vectorizer_state(data)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
1.6.341
|