adcp 1.0.1__py3-none-any.whl → 1.0.3__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/__main__.py +107 -8
- adcp/client.py +130 -75
- adcp/protocols/a2a.py +46 -4
- adcp/protocols/base.py +110 -9
- adcp/protocols/mcp.py +41 -1
- adcp/types/generated.py +19 -2
- adcp/types/tasks.py +313 -83
- adcp/utils/response_parser.py +119 -0
- {adcp-1.0.1.dist-info → adcp-1.0.3.dist-info}/METADATA +1 -1
- adcp-1.0.3.dist-info/RECORD +22 -0
- adcp-1.0.1.dist-info/RECORD +0 -21
- {adcp-1.0.1.dist-info → adcp-1.0.3.dist-info}/WHEEL +0 -0
- {adcp-1.0.1.dist-info → adcp-1.0.3.dist-info}/entry_points.txt +0 -0
- {adcp-1.0.1.dist-info → adcp-1.0.3.dist-info}/licenses/LICENSE +0 -0
- {adcp-1.0.1.dist-info → adcp-1.0.3.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
"""Utilities for parsing protocol responses into structured types."""
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import logging
|
|
7
|
+
from typing import Any, TypeVar
|
|
8
|
+
|
|
9
|
+
from pydantic import BaseModel, ValidationError
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
T = TypeVar("T", bound=BaseModel)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def parse_mcp_content(content: list[dict[str, Any]], response_type: type[T]) -> T:
|
|
17
|
+
"""
|
|
18
|
+
Parse MCP content array into structured response type.
|
|
19
|
+
|
|
20
|
+
MCP tools return content as a list of content items:
|
|
21
|
+
[{"type": "text", "text": "..."}, {"type": "resource", ...}]
|
|
22
|
+
|
|
23
|
+
For AdCP, we expect JSON data in text content items.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
content: MCP content array
|
|
27
|
+
response_type: Expected Pydantic model type
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
Parsed and validated response object
|
|
31
|
+
|
|
32
|
+
Raises:
|
|
33
|
+
ValueError: If content cannot be parsed into expected type
|
|
34
|
+
"""
|
|
35
|
+
if not content:
|
|
36
|
+
raise ValueError("Empty MCP content array")
|
|
37
|
+
|
|
38
|
+
# Look for text content items that might contain JSON
|
|
39
|
+
for item in content:
|
|
40
|
+
if item.get("type") == "text":
|
|
41
|
+
text = item.get("text", "")
|
|
42
|
+
if not text:
|
|
43
|
+
continue
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
# Try parsing as JSON
|
|
47
|
+
data = json.loads(text)
|
|
48
|
+
# Validate against expected schema
|
|
49
|
+
return response_type.model_validate(data)
|
|
50
|
+
except json.JSONDecodeError:
|
|
51
|
+
# Not JSON, try next item
|
|
52
|
+
continue
|
|
53
|
+
except ValidationError as e:
|
|
54
|
+
logger.warning(
|
|
55
|
+
f"MCP content doesn't match expected schema {response_type.__name__}: {e}"
|
|
56
|
+
)
|
|
57
|
+
raise ValueError(f"MCP response doesn't match expected schema: {e}") from e
|
|
58
|
+
elif item.get("type") == "resource":
|
|
59
|
+
# Resource content might have structured data
|
|
60
|
+
try:
|
|
61
|
+
return response_type.model_validate(item)
|
|
62
|
+
except ValidationError:
|
|
63
|
+
# Try next item
|
|
64
|
+
continue
|
|
65
|
+
|
|
66
|
+
# If we get here, no content item could be parsed
|
|
67
|
+
# Include content preview for debugging (first 2 items, max 500 chars each)
|
|
68
|
+
content_preview = json.dumps(content[:2], indent=2, default=str)
|
|
69
|
+
if len(content_preview) > 500:
|
|
70
|
+
content_preview = content_preview[:500] + "..."
|
|
71
|
+
|
|
72
|
+
raise ValueError(
|
|
73
|
+
f"No valid {response_type.__name__} data found in MCP content. "
|
|
74
|
+
f"Content types: {[item.get('type') for item in content]}. "
|
|
75
|
+
f"Content preview:\n{content_preview}"
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def parse_json_or_text(data: Any, response_type: type[T]) -> T:
|
|
80
|
+
"""
|
|
81
|
+
Parse data that might be JSON string, dict, or other format.
|
|
82
|
+
|
|
83
|
+
Used by A2A adapter for flexible response parsing.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
data: Response data (string, dict, or other)
|
|
87
|
+
response_type: Expected Pydantic model type
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
Parsed and validated response object
|
|
91
|
+
|
|
92
|
+
Raises:
|
|
93
|
+
ValueError: If data cannot be parsed into expected type
|
|
94
|
+
"""
|
|
95
|
+
# If already a dict, try direct validation
|
|
96
|
+
if isinstance(data, dict):
|
|
97
|
+
try:
|
|
98
|
+
return response_type.model_validate(data)
|
|
99
|
+
except ValidationError as e:
|
|
100
|
+
raise ValueError(
|
|
101
|
+
f"Response doesn't match expected schema {response_type.__name__}: {e}"
|
|
102
|
+
) from e
|
|
103
|
+
|
|
104
|
+
# If string, try JSON parsing
|
|
105
|
+
if isinstance(data, str):
|
|
106
|
+
try:
|
|
107
|
+
parsed = json.loads(data)
|
|
108
|
+
return response_type.model_validate(parsed)
|
|
109
|
+
except json.JSONDecodeError as e:
|
|
110
|
+
raise ValueError(f"Response is not valid JSON: {e}") from e
|
|
111
|
+
except ValidationError as e:
|
|
112
|
+
raise ValueError(
|
|
113
|
+
f"Response doesn't match expected schema {response_type.__name__}: {e}"
|
|
114
|
+
) from e
|
|
115
|
+
|
|
116
|
+
# Unsupported type
|
|
117
|
+
raise ValueError(
|
|
118
|
+
f"Cannot parse response of type {type(data).__name__} into {response_type.__name__}"
|
|
119
|
+
)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
adcp/__init__.py,sha256=GtTKciZQ6kfkQlsM1s87zUIpkgRAU6bLfbEp2pSdBds,2512
|
|
2
|
+
adcp/__main__.py,sha256=4rjwH3i52Dsy-Pb0hw5hapQuEG8-bW2tTKKB0HsVsSs,12716
|
|
3
|
+
adcp/client.py,sha256=iIZUy5j25H48gGYlR_VuVsB9zP6SF1HtRssdPs2VJkc,24232
|
|
4
|
+
adcp/config.py,sha256=Vsy7ZPOI8G3fB_i5Nk-CHbC7wdasCUWuKlos0fwA0kY,2017
|
|
5
|
+
adcp/exceptions.py,sha256=dNRMKV23DlkGKyB9Xmt6MtlhvDu1crjzD_en4nAEwDY,4399
|
|
6
|
+
adcp/protocols/__init__.py,sha256=6UFwACQ0QadBUzy17wUROHqsJDp8ztPW2jzyl53Zh_g,262
|
|
7
|
+
adcp/protocols/a2a.py,sha256=TN26ac98h2NUZTTs39Tyd6pVoS3k-sASuLKhLpdYV-A,12255
|
|
8
|
+
adcp/protocols/base.py,sha256=Tdxg1hefWVDvRP7q3yYbtPlIfUr01luPG0oT773qi30,4906
|
|
9
|
+
adcp/protocols/mcp.py,sha256=zdnW3RxjEUwWm7jbTzs73dfVywCq3f7fyhwND3w-f54,13081
|
|
10
|
+
adcp/types/__init__.py,sha256=3E_TJUXqQQFcjmSZZSPLwqBP3s_ijsH2LDeuOU-MP30,402
|
|
11
|
+
adcp/types/core.py,sha256=w6CLD2K0riAHUnrktYmQV7qnkO-6Ab4-CN67YSagAE4,4731
|
|
12
|
+
adcp/types/generated.py,sha256=KoILEa5Gg0tsjMYoqDEWulKvPzS0333L3qj75AHr4yI,50855
|
|
13
|
+
adcp/types/tasks.py,sha256=Ae9TSwG2F7oWXTcl4TvLhAzinbQkHNGF1Pc0q8RMNNM,23424
|
|
14
|
+
adcp/utils/__init__.py,sha256=uetvSJB19CjQbtwEYZiTnumJG11GsafQmXm5eR3hL7E,153
|
|
15
|
+
adcp/utils/operation_id.py,sha256=wQX9Bb5epXzRq23xoeYPTqzu5yLuhshg7lKJZihcM2k,294
|
|
16
|
+
adcp/utils/response_parser.py,sha256=s7gEfXwPbvYTSoGqkx0Lp3v9uJ9HlWC2BSic3nO4ZnM,3909
|
|
17
|
+
adcp-1.0.3.dist-info/licenses/LICENSE,sha256=PF39NR3Ae8PLgBhg3Uxw6ju7iGVIf8hfv9LRWQdii_U,629
|
|
18
|
+
adcp-1.0.3.dist-info/METADATA,sha256=nqAcBHeBflsdVV2QNlqIeq93B53PZqQ9DNCKBhJOdGw,12724
|
|
19
|
+
adcp-1.0.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
20
|
+
adcp-1.0.3.dist-info/entry_points.txt,sha256=DQKpcGsJX8DtVI_SGApQ7tNvqUB4zkTLaTAEpFgmi3U,44
|
|
21
|
+
adcp-1.0.3.dist-info/top_level.txt,sha256=T1_NF0GefncFU9v_k56oDwKSJREyCqIM8lAwNZf0EOs,5
|
|
22
|
+
adcp-1.0.3.dist-info/RECORD,,
|
adcp-1.0.1.dist-info/RECORD
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
adcp/__init__.py,sha256=8vcP5vvkGKAn6ESlbFDzTZczzyZmaJ__DjVVx_TlBBY,2512
|
|
2
|
-
adcp/__main__.py,sha256=MR7hiJu0cM7wkBAMpJ9wq9IXWFjWeD5gEOvJOJawnW8,8900
|
|
3
|
-
adcp/client.py,sha256=S1Qe_4wYt_A7T6ltK_6Cnx_9gr_I23brjSSHC00bcVM,20885
|
|
4
|
-
adcp/config.py,sha256=Vsy7ZPOI8G3fB_i5Nk-CHbC7wdasCUWuKlos0fwA0kY,2017
|
|
5
|
-
adcp/exceptions.py,sha256=dNRMKV23DlkGKyB9Xmt6MtlhvDu1crjzD_en4nAEwDY,4399
|
|
6
|
-
adcp/protocols/__init__.py,sha256=6UFwACQ0QadBUzy17wUROHqsJDp8ztPW2jzyl53Zh_g,262
|
|
7
|
-
adcp/protocols/a2a.py,sha256=c5PZ1SlZe2TPzx-eFu-RH9P-h98CW4F5PBdgsU5k8HE,10281
|
|
8
|
-
adcp/protocols/base.py,sha256=fG4tk4UMVSkBE3zv5b7bPEOSEXOCTI2oFyy9V6H406Y,1183
|
|
9
|
-
adcp/protocols/mcp.py,sha256=Oo0i7jyTnmQPoLeKuP6YBCmCzi1WazUDf3SzxMAU6lA,11163
|
|
10
|
-
adcp/types/__init__.py,sha256=3E_TJUXqQQFcjmSZZSPLwqBP3s_ijsH2LDeuOU-MP30,402
|
|
11
|
-
adcp/types/core.py,sha256=w6CLD2K0riAHUnrktYmQV7qnkO-6Ab4-CN67YSagAE4,4731
|
|
12
|
-
adcp/types/generated.py,sha256=5vbr113-rhNJSMePAEvQSPtqqEqiloVw1F8BeT4tk3o,49973
|
|
13
|
-
adcp/types/tasks.py,sha256=iwNoV-1_BxH3PZxmfg2rgZHDUN-o_Ob7zgPNbKtyNPM,21553
|
|
14
|
-
adcp/utils/__init__.py,sha256=uetvSJB19CjQbtwEYZiTnumJG11GsafQmXm5eR3hL7E,153
|
|
15
|
-
adcp/utils/operation_id.py,sha256=wQX9Bb5epXzRq23xoeYPTqzu5yLuhshg7lKJZihcM2k,294
|
|
16
|
-
adcp-1.0.1.dist-info/licenses/LICENSE,sha256=PF39NR3Ae8PLgBhg3Uxw6ju7iGVIf8hfv9LRWQdii_U,629
|
|
17
|
-
adcp-1.0.1.dist-info/METADATA,sha256=Kc-phbnBzt8fV4aQIze_VRNXogyBbnwanrKIwJD8iFE,12724
|
|
18
|
-
adcp-1.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
19
|
-
adcp-1.0.1.dist-info/entry_points.txt,sha256=DQKpcGsJX8DtVI_SGApQ7tNvqUB4zkTLaTAEpFgmi3U,44
|
|
20
|
-
adcp-1.0.1.dist-info/top_level.txt,sha256=T1_NF0GefncFU9v_k56oDwKSJREyCqIM8lAwNZf0EOs,5
|
|
21
|
-
adcp-1.0.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|