adcp 2.16.0__py3-none-any.whl → 2.17.0__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.
- adcp/__init__.py +1 -1
- adcp/utils/response_parser.py +81 -6
- {adcp-2.16.0.dist-info → adcp-2.17.0.dist-info}/METADATA +1 -1
- {adcp-2.16.0.dist-info → adcp-2.17.0.dist-info}/RECORD +8 -8
- {adcp-2.16.0.dist-info → adcp-2.17.0.dist-info}/WHEEL +0 -0
- {adcp-2.16.0.dist-info → adcp-2.17.0.dist-info}/entry_points.txt +0 -0
- {adcp-2.16.0.dist-info → adcp-2.17.0.dist-info}/licenses/LICENSE +0 -0
- {adcp-2.16.0.dist-info → adcp-2.17.0.dist-info}/top_level.txt +0 -0
adcp/__init__.py
CHANGED
adcp/utils/response_parser.py
CHANGED
|
@@ -129,12 +129,67 @@ def parse_mcp_content(content: list[dict[str, Any]], response_type: type[T]) ->
|
|
|
129
129
|
)
|
|
130
130
|
|
|
131
131
|
|
|
132
|
+
# Protocol-level fields from ProtocolResponse (core/response.json) and
|
|
133
|
+
# ProtocolEnvelope (core/protocol_envelope.json). These are separated from
|
|
134
|
+
# task data for schema validation, but preserved at the TaskResult level.
|
|
135
|
+
# Note: 'data' and 'payload' are handled separately as wrapper fields.
|
|
136
|
+
PROTOCOL_FIELDS = {
|
|
137
|
+
"message", # Human-readable summary
|
|
138
|
+
"context_id", # Session continuity identifier
|
|
139
|
+
"task_id", # Async operation identifier
|
|
140
|
+
"status", # Task execution state
|
|
141
|
+
"timestamp", # Response timestamp
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _extract_task_data(data: dict[str, Any]) -> dict[str, Any]:
|
|
146
|
+
"""
|
|
147
|
+
Extract task-specific data from a protocol response.
|
|
148
|
+
|
|
149
|
+
Servers may return responses in ProtocolResponse format:
|
|
150
|
+
{"message": "...", "context_id": "...", "data": {...}}
|
|
151
|
+
|
|
152
|
+
Or ProtocolEnvelope format:
|
|
153
|
+
{"message": "...", "status": "...", "payload": {...}}
|
|
154
|
+
|
|
155
|
+
Or task data directly with protocol fields mixed in:
|
|
156
|
+
{"message": "...", "products": [...], ...}
|
|
157
|
+
|
|
158
|
+
This function separates task-specific data for schema validation.
|
|
159
|
+
Protocol fields are preserved at the TaskResult level.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
data: Response data dict
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
Task-specific data suitable for schema validation.
|
|
166
|
+
Returns the same dict object if no extraction is needed.
|
|
167
|
+
"""
|
|
168
|
+
# Check for wrapped payload fields
|
|
169
|
+
# (ProtocolResponse uses 'data', ProtocolEnvelope uses 'payload')
|
|
170
|
+
if "data" in data and isinstance(data["data"], dict):
|
|
171
|
+
return data["data"]
|
|
172
|
+
if "payload" in data and isinstance(data["payload"], dict):
|
|
173
|
+
return data["payload"]
|
|
174
|
+
|
|
175
|
+
# Check if any protocol fields are present
|
|
176
|
+
if not any(k in PROTOCOL_FIELDS for k in data):
|
|
177
|
+
return data # Return same object for identity check
|
|
178
|
+
|
|
179
|
+
# Separate task data from protocol fields
|
|
180
|
+
return {k: v for k, v in data.items() if k not in PROTOCOL_FIELDS}
|
|
181
|
+
|
|
182
|
+
|
|
132
183
|
def parse_json_or_text(data: Any, response_type: type[T]) -> T:
|
|
133
184
|
"""
|
|
134
185
|
Parse data that might be JSON string, dict, or other format.
|
|
135
186
|
|
|
136
187
|
Used by A2A adapter for flexible response parsing.
|
|
137
188
|
|
|
189
|
+
Handles protocol-level wrapping where servers return:
|
|
190
|
+
- {"message": "...", "data": {...task_data...}}
|
|
191
|
+
- {"message": "...", ...task_fields...}
|
|
192
|
+
|
|
138
193
|
Args:
|
|
139
194
|
data: Response data (string, dict, or other)
|
|
140
195
|
response_type: Expected Pydantic model type
|
|
@@ -147,22 +202,42 @@ def parse_json_or_text(data: Any, response_type: type[T]) -> T:
|
|
|
147
202
|
"""
|
|
148
203
|
# If already a dict, try direct validation
|
|
149
204
|
if isinstance(data, dict):
|
|
205
|
+
# Try direct validation first
|
|
206
|
+
original_error: Exception | None = None
|
|
150
207
|
try:
|
|
151
208
|
return _validate_union_type(data, response_type)
|
|
152
|
-
except ValidationError as e:
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
209
|
+
except (ValidationError, ValueError) as e:
|
|
210
|
+
original_error = e
|
|
211
|
+
|
|
212
|
+
# Try extracting task data (separates protocol fields)
|
|
213
|
+
task_data = _extract_task_data(data)
|
|
214
|
+
if task_data is not data:
|
|
215
|
+
try:
|
|
216
|
+
return _validate_union_type(task_data, response_type)
|
|
217
|
+
except (ValidationError, ValueError):
|
|
218
|
+
pass # Fall through to raise original error
|
|
219
|
+
|
|
220
|
+
# Report the original validation error
|
|
221
|
+
type_name = getattr(response_type, "__name__", str(response_type))
|
|
222
|
+
raise ValueError(
|
|
223
|
+
f"Response doesn't match expected schema {type_name}: {original_error}"
|
|
224
|
+
) from original_error
|
|
156
225
|
|
|
157
226
|
# If string, try JSON parsing
|
|
158
227
|
if isinstance(data, str):
|
|
159
228
|
try:
|
|
160
229
|
parsed = json.loads(data)
|
|
161
|
-
return _validate_union_type(parsed, response_type)
|
|
162
230
|
except json.JSONDecodeError as e:
|
|
163
231
|
raise ValueError(f"Response is not valid JSON: {e}") from e
|
|
232
|
+
|
|
233
|
+
# Recursively handle dict parsing (which includes protocol field extraction)
|
|
234
|
+
if isinstance(parsed, dict):
|
|
235
|
+
return parse_json_or_text(parsed, response_type)
|
|
236
|
+
|
|
237
|
+
# Non-dict JSON (shouldn't happen for AdCP responses)
|
|
238
|
+
try:
|
|
239
|
+
return _validate_union_type(parsed, response_type)
|
|
164
240
|
except ValidationError as e:
|
|
165
|
-
# Get the type name, handling Union types
|
|
166
241
|
type_name = getattr(response_type, "__name__", str(response_type))
|
|
167
242
|
raise ValueError(f"Response doesn't match expected schema {type_name}: {e}") from e
|
|
168
243
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
adcp/ADCP_VERSION,sha256=cy9k2HT5B4jAGZsMGb_Zs7ZDbhQBFOU-RigyUy10xhw,6
|
|
2
|
-
adcp/__init__.py,sha256=
|
|
2
|
+
adcp/__init__.py,sha256=0ihzp5E5L6yBTaALFz3HKjSSN3eTb_rY3EAJTMMVYH4,10319
|
|
3
3
|
adcp/__main__.py,sha256=8v-j_W9IIWDIcvhaR1yuZAhBCDRfehUyN0tusstzyfQ,15139
|
|
4
4
|
adcp/adagents.py,sha256=HY7vZ8QqC8Zjzc_jRA0ZMhAfvm1pJN_VJfS8a8Fbc6c,24481
|
|
5
5
|
adcp/client.py,sha256=gdDnyCyotRKMBHgxyONq3BWr4Seo7U2KZE18gqpw3nU,50839
|
|
@@ -185,10 +185,10 @@ adcp/types/generated_poc/signals/get_signals_response.py,sha256=F-JQXrXg3jXBZfm-
|
|
|
185
185
|
adcp/utils/__init__.py,sha256=uetvSJB19CjQbtwEYZiTnumJG11GsafQmXm5eR3hL7E,153
|
|
186
186
|
adcp/utils/operation_id.py,sha256=wQX9Bb5epXzRq23xoeYPTqzu5yLuhshg7lKJZihcM2k,294
|
|
187
187
|
adcp/utils/preview_cache.py,sha256=fy792IGXX9385FGsOhyuN1ZjoagsCstmcC1a8HAwtlI,18771
|
|
188
|
-
adcp/utils/response_parser.py,sha256=
|
|
189
|
-
adcp-2.
|
|
190
|
-
adcp-2.
|
|
191
|
-
adcp-2.
|
|
192
|
-
adcp-2.
|
|
193
|
-
adcp-2.
|
|
194
|
-
adcp-2.
|
|
188
|
+
adcp/utils/response_parser.py,sha256=WBYq8bZpPiVrG70PNhHDyE5_NyexS1qlsA8YRkxpZaQ,8986
|
|
189
|
+
adcp-2.17.0.dist-info/licenses/LICENSE,sha256=PF39NR3Ae8PLgBhg3Uxw6ju7iGVIf8hfv9LRWQdii_U,629
|
|
190
|
+
adcp-2.17.0.dist-info/METADATA,sha256=3ZsiYib0KMw4oolH1jg9KsGsUV8MgJ-oc-ysyox6Rjg,31359
|
|
191
|
+
adcp-2.17.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
192
|
+
adcp-2.17.0.dist-info/entry_points.txt,sha256=DQKpcGsJX8DtVI_SGApQ7tNvqUB4zkTLaTAEpFgmi3U,44
|
|
193
|
+
adcp-2.17.0.dist-info/top_level.txt,sha256=T1_NF0GefncFU9v_k56oDwKSJREyCqIM8lAwNZf0EOs,5
|
|
194
|
+
adcp-2.17.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|