acp-sdk 0.0.6__py3-none-any.whl → 1.0.0rc1__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.
- acp_sdk/client/__init__.py +1 -0
- acp_sdk/client/client.py +135 -0
- acp_sdk/models.py +219 -0
- acp_sdk/server/__init__.py +2 -0
- acp_sdk/server/agent.py +32 -0
- acp_sdk/server/bundle.py +133 -0
- acp_sdk/server/context.py +6 -0
- acp_sdk/server/server.py +137 -0
- acp_sdk/server/telemetry.py +45 -0
- acp_sdk/server/utils.py +12 -0
- acp_sdk-1.0.0rc1.dist-info/METADATA +53 -0
- acp_sdk-1.0.0rc1.dist-info/RECORD +15 -0
- acp/__init__.py +0 -138
- acp/cli/__init__.py +0 -6
- acp/cli/claude.py +0 -139
- acp/cli/cli.py +0 -471
- acp/client/__main__.py +0 -79
- acp/client/session.py +0 -372
- acp/client/sse.py +0 -145
- acp/client/stdio.py +0 -153
- acp/server/__init__.py +0 -3
- acp/server/__main__.py +0 -50
- acp/server/highlevel/__init__.py +0 -9
- acp/server/highlevel/agents/__init__.py +0 -5
- acp/server/highlevel/agents/agent_manager.py +0 -110
- acp/server/highlevel/agents/base.py +0 -20
- acp/server/highlevel/agents/templates.py +0 -21
- acp/server/highlevel/context.py +0 -185
- acp/server/highlevel/exceptions.py +0 -25
- acp/server/highlevel/prompts/__init__.py +0 -4
- acp/server/highlevel/prompts/base.py +0 -167
- acp/server/highlevel/prompts/manager.py +0 -50
- acp/server/highlevel/prompts/prompt_manager.py +0 -33
- acp/server/highlevel/resources/__init__.py +0 -23
- acp/server/highlevel/resources/base.py +0 -48
- acp/server/highlevel/resources/resource_manager.py +0 -94
- acp/server/highlevel/resources/templates.py +0 -80
- acp/server/highlevel/resources/types.py +0 -185
- acp/server/highlevel/server.py +0 -705
- acp/server/highlevel/tools/__init__.py +0 -4
- acp/server/highlevel/tools/base.py +0 -83
- acp/server/highlevel/tools/tool_manager.py +0 -53
- acp/server/highlevel/utilities/__init__.py +0 -1
- acp/server/highlevel/utilities/func_metadata.py +0 -210
- acp/server/highlevel/utilities/logging.py +0 -43
- acp/server/highlevel/utilities/types.py +0 -54
- acp/server/lowlevel/__init__.py +0 -3
- acp/server/lowlevel/helper_types.py +0 -9
- acp/server/lowlevel/server.py +0 -643
- acp/server/models.py +0 -17
- acp/server/session.py +0 -315
- acp/server/sse.py +0 -175
- acp/server/stdio.py +0 -83
- acp/server/websocket.py +0 -61
- acp/shared/__init__.py +0 -0
- acp/shared/context.py +0 -14
- acp/shared/exceptions.py +0 -14
- acp/shared/memory.py +0 -87
- acp/shared/progress.py +0 -40
- acp/shared/session.py +0 -413
- acp/shared/version.py +0 -3
- acp/types.py +0 -1258
- acp_sdk-0.0.6.dist-info/METADATA +0 -46
- acp_sdk-0.0.6.dist-info/RECORD +0 -57
- acp_sdk-0.0.6.dist-info/entry_points.txt +0 -2
- acp_sdk-0.0.6.dist-info/licenses/LICENSE +0 -22
- {acp/client → acp_sdk}/__init__.py +0 -0
- {acp → acp_sdk}/py.typed +0 -0
- {acp_sdk-0.0.6.dist-info → acp_sdk-1.0.0rc1.dist-info}/WHEEL +0 -0
acp/client/session.py
DELETED
@@ -1,372 +0,0 @@
|
|
1
|
-
from datetime import timedelta
|
2
|
-
from typing import Any
|
3
|
-
|
4
|
-
from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
|
5
|
-
from pydantic import AnyUrl
|
6
|
-
|
7
|
-
from acp.shared.session import BaseSession
|
8
|
-
from acp.shared.version import SUPPORTED_PROTOCOL_VERSIONS
|
9
|
-
from acp.types import (
|
10
|
-
LATEST_PROTOCOL_VERSION,
|
11
|
-
CallToolRequest,
|
12
|
-
CallToolRequestParams,
|
13
|
-
CallToolResult,
|
14
|
-
ClientCapabilities,
|
15
|
-
ClientNotification,
|
16
|
-
ClientRequest,
|
17
|
-
ClientResult,
|
18
|
-
CompleteRequest,
|
19
|
-
CompleteRequestParams,
|
20
|
-
CompleteResult,
|
21
|
-
CompletionArgument,
|
22
|
-
CreateAgentRequest,
|
23
|
-
CreateAgentRequestParams,
|
24
|
-
CreateAgentResult,
|
25
|
-
DestroyAgentRequest,
|
26
|
-
DestroyAgentRequestParams,
|
27
|
-
DestroyAgentResult,
|
28
|
-
EmptyResult,
|
29
|
-
GetPromptRequest,
|
30
|
-
GetPromptRequestParams,
|
31
|
-
GetPromptResult,
|
32
|
-
Implementation,
|
33
|
-
InitializedNotification,
|
34
|
-
InitializeRequest,
|
35
|
-
InitializeRequestParams,
|
36
|
-
InitializeResult,
|
37
|
-
JSONRPCMessage,
|
38
|
-
ListAgentsRequest,
|
39
|
-
ListAgentsResult,
|
40
|
-
ListAgentTemplatesRequest,
|
41
|
-
ListAgentTemplatesResult,
|
42
|
-
ListPromptsRequest,
|
43
|
-
ListPromptsResult,
|
44
|
-
ListResourcesRequest,
|
45
|
-
ListResourcesResult,
|
46
|
-
ListResourceTemplatesRequest,
|
47
|
-
ListResourceTemplatesResult,
|
48
|
-
ListToolsRequest,
|
49
|
-
ListToolsResult,
|
50
|
-
LoggingLevel,
|
51
|
-
PingRequest,
|
52
|
-
ProgressNotification,
|
53
|
-
ProgressNotificationParams,
|
54
|
-
PromptReference,
|
55
|
-
ReadResourceRequest,
|
56
|
-
ReadResourceRequestParams,
|
57
|
-
ReadResourceResult,
|
58
|
-
ResourceReference,
|
59
|
-
RootsCapability,
|
60
|
-
RootsListChangedNotification,
|
61
|
-
RunAgentRequest,
|
62
|
-
RunAgentRequestParams,
|
63
|
-
RunAgentResult,
|
64
|
-
ServerNotification,
|
65
|
-
ServerRequest,
|
66
|
-
SetLevelRequest,
|
67
|
-
SetLevelRequestParams,
|
68
|
-
SubscribeRequest,
|
69
|
-
SubscribeRequestParams,
|
70
|
-
UnsubscribeRequest,
|
71
|
-
UnsubscribeRequestParams,
|
72
|
-
)
|
73
|
-
|
74
|
-
|
75
|
-
class ClientSession(
|
76
|
-
BaseSession[
|
77
|
-
ClientRequest,
|
78
|
-
ClientNotification,
|
79
|
-
ClientResult,
|
80
|
-
ServerRequest,
|
81
|
-
ServerNotification,
|
82
|
-
]
|
83
|
-
):
|
84
|
-
def __init__(
|
85
|
-
self,
|
86
|
-
read_stream: MemoryObjectReceiveStream[JSONRPCMessage | Exception],
|
87
|
-
write_stream: MemoryObjectSendStream[JSONRPCMessage],
|
88
|
-
read_timeout_seconds: timedelta | None = None,
|
89
|
-
) -> None:
|
90
|
-
super().__init__(
|
91
|
-
read_stream,
|
92
|
-
write_stream,
|
93
|
-
ServerRequest,
|
94
|
-
ServerNotification,
|
95
|
-
read_timeout_seconds=read_timeout_seconds,
|
96
|
-
)
|
97
|
-
|
98
|
-
async def initialize(self) -> InitializeResult:
|
99
|
-
result = await self.send_request(
|
100
|
-
ClientRequest(
|
101
|
-
InitializeRequest(
|
102
|
-
method="initialize",
|
103
|
-
params=InitializeRequestParams(
|
104
|
-
protocolVersion=LATEST_PROTOCOL_VERSION,
|
105
|
-
capabilities=ClientCapabilities(
|
106
|
-
sampling=None,
|
107
|
-
experimental=None,
|
108
|
-
roots=RootsCapability(
|
109
|
-
# TODO: Should this be based on whether we
|
110
|
-
# _will_ send notifications, or only whether
|
111
|
-
# they're supported?
|
112
|
-
listChanged=True
|
113
|
-
),
|
114
|
-
),
|
115
|
-
clientInfo=Implementation(name="mcp", version="0.1.0"),
|
116
|
-
),
|
117
|
-
)
|
118
|
-
),
|
119
|
-
InitializeResult,
|
120
|
-
)
|
121
|
-
|
122
|
-
if result.protocolVersion not in SUPPORTED_PROTOCOL_VERSIONS:
|
123
|
-
raise RuntimeError(
|
124
|
-
"Unsupported protocol version from the server: "
|
125
|
-
f"{result.protocolVersion}"
|
126
|
-
)
|
127
|
-
|
128
|
-
await self.send_notification(
|
129
|
-
ClientNotification(
|
130
|
-
InitializedNotification(method="notifications/initialized")
|
131
|
-
)
|
132
|
-
)
|
133
|
-
|
134
|
-
return result
|
135
|
-
|
136
|
-
async def send_ping(self) -> EmptyResult:
|
137
|
-
"""Send a ping request."""
|
138
|
-
return await self.send_request(
|
139
|
-
ClientRequest(
|
140
|
-
PingRequest(
|
141
|
-
method="ping",
|
142
|
-
)
|
143
|
-
),
|
144
|
-
EmptyResult,
|
145
|
-
)
|
146
|
-
|
147
|
-
async def send_progress_notification(
|
148
|
-
self, progress_token: str | int, progress: float, total: float | None = None
|
149
|
-
) -> None:
|
150
|
-
"""Send a progress notification."""
|
151
|
-
await self.send_notification(
|
152
|
-
ClientNotification(
|
153
|
-
ProgressNotification(
|
154
|
-
method="notifications/progress",
|
155
|
-
params=ProgressNotificationParams(
|
156
|
-
progressToken=progress_token,
|
157
|
-
progress=progress,
|
158
|
-
total=total,
|
159
|
-
),
|
160
|
-
),
|
161
|
-
)
|
162
|
-
)
|
163
|
-
|
164
|
-
async def set_logging_level(self, level: LoggingLevel) -> EmptyResult:
|
165
|
-
"""Send a logging/setLevel request."""
|
166
|
-
return await self.send_request(
|
167
|
-
ClientRequest(
|
168
|
-
SetLevelRequest(
|
169
|
-
method="logging/setLevel",
|
170
|
-
params=SetLevelRequestParams(level=level),
|
171
|
-
)
|
172
|
-
),
|
173
|
-
EmptyResult,
|
174
|
-
)
|
175
|
-
|
176
|
-
async def list_resources(self) -> ListResourcesResult:
|
177
|
-
"""Send a resources/list request."""
|
178
|
-
return await self.send_request(
|
179
|
-
ClientRequest(
|
180
|
-
ListResourcesRequest(
|
181
|
-
method="resources/list",
|
182
|
-
)
|
183
|
-
),
|
184
|
-
ListResourcesResult,
|
185
|
-
)
|
186
|
-
|
187
|
-
async def list_resource_templates(self) -> ListResourceTemplatesResult:
|
188
|
-
"""Send a resources/templates/list request."""
|
189
|
-
return await self.send_request(
|
190
|
-
ClientRequest(
|
191
|
-
ListResourceTemplatesRequest(
|
192
|
-
method="resources/templates/list",
|
193
|
-
)
|
194
|
-
),
|
195
|
-
ListResourceTemplatesResult,
|
196
|
-
)
|
197
|
-
|
198
|
-
async def read_resource(self, uri: AnyUrl) -> ReadResourceResult:
|
199
|
-
"""Send a resources/read request."""
|
200
|
-
return await self.send_request(
|
201
|
-
ClientRequest(
|
202
|
-
ReadResourceRequest(
|
203
|
-
method="resources/read",
|
204
|
-
params=ReadResourceRequestParams(uri=uri),
|
205
|
-
)
|
206
|
-
),
|
207
|
-
ReadResourceResult,
|
208
|
-
)
|
209
|
-
|
210
|
-
async def subscribe_resource(self, uri: AnyUrl) -> EmptyResult:
|
211
|
-
"""Send a resources/subscribe request."""
|
212
|
-
return await self.send_request(
|
213
|
-
ClientRequest(
|
214
|
-
SubscribeRequest(
|
215
|
-
method="resources/subscribe",
|
216
|
-
params=SubscribeRequestParams(uri=uri),
|
217
|
-
)
|
218
|
-
),
|
219
|
-
EmptyResult,
|
220
|
-
)
|
221
|
-
|
222
|
-
async def unsubscribe_resource(self, uri: AnyUrl) -> EmptyResult:
|
223
|
-
"""Send a resources/unsubscribe request."""
|
224
|
-
return await self.send_request(
|
225
|
-
ClientRequest(
|
226
|
-
UnsubscribeRequest(
|
227
|
-
method="resources/unsubscribe",
|
228
|
-
params=UnsubscribeRequestParams(uri=uri),
|
229
|
-
)
|
230
|
-
),
|
231
|
-
EmptyResult,
|
232
|
-
)
|
233
|
-
|
234
|
-
async def call_tool(
|
235
|
-
self, name: str, arguments: dict | None = None
|
236
|
-
) -> CallToolResult:
|
237
|
-
"""Send a tools/call request."""
|
238
|
-
return await self.send_request(
|
239
|
-
ClientRequest(
|
240
|
-
CallToolRequest(
|
241
|
-
method="tools/call",
|
242
|
-
params=CallToolRequestParams(name=name, arguments=arguments),
|
243
|
-
)
|
244
|
-
),
|
245
|
-
CallToolResult,
|
246
|
-
)
|
247
|
-
|
248
|
-
async def list_prompts(self) -> ListPromptsResult:
|
249
|
-
"""Send a prompts/list request."""
|
250
|
-
return await self.send_request(
|
251
|
-
ClientRequest(
|
252
|
-
ListPromptsRequest(
|
253
|
-
method="prompts/list",
|
254
|
-
)
|
255
|
-
),
|
256
|
-
ListPromptsResult,
|
257
|
-
)
|
258
|
-
|
259
|
-
async def get_prompt(
|
260
|
-
self, name: str, arguments: dict[str, str] | None = None
|
261
|
-
) -> GetPromptResult:
|
262
|
-
"""Send a prompts/get request."""
|
263
|
-
return await self.send_request(
|
264
|
-
ClientRequest(
|
265
|
-
GetPromptRequest(
|
266
|
-
method="prompts/get",
|
267
|
-
params=GetPromptRequestParams(name=name, arguments=arguments),
|
268
|
-
)
|
269
|
-
),
|
270
|
-
GetPromptResult,
|
271
|
-
)
|
272
|
-
|
273
|
-
async def complete(
|
274
|
-
self, ref: ResourceReference | PromptReference, argument: dict
|
275
|
-
) -> CompleteResult:
|
276
|
-
"""Send a completion/complete request."""
|
277
|
-
return await self.send_request(
|
278
|
-
ClientRequest(
|
279
|
-
CompleteRequest(
|
280
|
-
method="completion/complete",
|
281
|
-
params=CompleteRequestParams(
|
282
|
-
ref=ref,
|
283
|
-
argument=CompletionArgument(**argument),
|
284
|
-
),
|
285
|
-
)
|
286
|
-
),
|
287
|
-
CompleteResult,
|
288
|
-
)
|
289
|
-
|
290
|
-
async def list_tools(self) -> ListToolsResult:
|
291
|
-
"""Send a tools/list request."""
|
292
|
-
return await self.send_request(
|
293
|
-
ClientRequest(
|
294
|
-
ListToolsRequest(
|
295
|
-
method="tools/list",
|
296
|
-
)
|
297
|
-
),
|
298
|
-
ListToolsResult,
|
299
|
-
)
|
300
|
-
|
301
|
-
async def send_roots_list_changed(self) -> None:
|
302
|
-
"""Send a roots/list_changed notification."""
|
303
|
-
await self.send_notification(
|
304
|
-
ClientNotification(
|
305
|
-
RootsListChangedNotification(
|
306
|
-
method="notifications/roots/list_changed",
|
307
|
-
)
|
308
|
-
)
|
309
|
-
)
|
310
|
-
|
311
|
-
async def list_agent_templates(self) -> ListAgentTemplatesResult:
|
312
|
-
"""Send a agents/templates/list request."""
|
313
|
-
return await self.send_request(
|
314
|
-
ClientRequest(
|
315
|
-
ListAgentTemplatesRequest(
|
316
|
-
method="agents/templates/list",
|
317
|
-
)
|
318
|
-
),
|
319
|
-
ListAgentTemplatesResult,
|
320
|
-
)
|
321
|
-
|
322
|
-
async def list_agents(self) -> ListAgentsResult:
|
323
|
-
"""Send a agents/list request."""
|
324
|
-
return await self.send_request(
|
325
|
-
ClientRequest(
|
326
|
-
ListAgentsRequest(
|
327
|
-
method="agents/list",
|
328
|
-
)
|
329
|
-
),
|
330
|
-
ListAgentsResult,
|
331
|
-
)
|
332
|
-
|
333
|
-
async def create_agent(
|
334
|
-
self, template_name: str, config: dict[str, Any]
|
335
|
-
) -> CreateAgentResult:
|
336
|
-
"""Send a agents/create request."""
|
337
|
-
return await self.send_request(
|
338
|
-
ClientRequest(
|
339
|
-
CreateAgentRequest(
|
340
|
-
method="agents/create",
|
341
|
-
params=CreateAgentRequestParams(
|
342
|
-
templateName=template_name,
|
343
|
-
config=config,
|
344
|
-
),
|
345
|
-
)
|
346
|
-
),
|
347
|
-
CreateAgentResult,
|
348
|
-
)
|
349
|
-
|
350
|
-
async def destroy_agent(self, name: str) -> DestroyAgentResult:
|
351
|
-
"""Send a agents/destroy request."""
|
352
|
-
return await self.send_request(
|
353
|
-
ClientRequest(
|
354
|
-
DestroyAgentRequest(
|
355
|
-
method="agents/destroy",
|
356
|
-
params=DestroyAgentRequestParams(name=name),
|
357
|
-
)
|
358
|
-
),
|
359
|
-
DestroyAgentResult,
|
360
|
-
)
|
361
|
-
|
362
|
-
async def run_agent(self, name: str, input: dict[str, Any]) -> RunAgentResult:
|
363
|
-
"""Send a agents/run request."""
|
364
|
-
return await self.send_request(
|
365
|
-
ClientRequest(
|
366
|
-
RunAgentRequest(
|
367
|
-
method="agents/run",
|
368
|
-
params=RunAgentRequestParams(name=name, input=input),
|
369
|
-
)
|
370
|
-
),
|
371
|
-
RunAgentResult,
|
372
|
-
)
|
acp/client/sse.py
DELETED
@@ -1,145 +0,0 @@
|
|
1
|
-
import logging
|
2
|
-
from contextlib import asynccontextmanager
|
3
|
-
from typing import Any
|
4
|
-
from urllib.parse import urljoin, urlparse
|
5
|
-
|
6
|
-
import anyio
|
7
|
-
import httpx
|
8
|
-
from anyio.abc import TaskStatus
|
9
|
-
from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
|
10
|
-
from httpx_sse import aconnect_sse
|
11
|
-
|
12
|
-
import acp.types as types
|
13
|
-
|
14
|
-
logger = logging.getLogger(__name__)
|
15
|
-
|
16
|
-
|
17
|
-
def remove_request_params(url: str) -> str:
|
18
|
-
return urljoin(url, urlparse(url).path)
|
19
|
-
|
20
|
-
|
21
|
-
@asynccontextmanager
|
22
|
-
async def sse_client(
|
23
|
-
url: str,
|
24
|
-
headers: dict[str, Any] | None = None,
|
25
|
-
timeout: float = 5,
|
26
|
-
sse_read_timeout: float = 60 * 5,
|
27
|
-
):
|
28
|
-
"""
|
29
|
-
Client transport for SSE.
|
30
|
-
|
31
|
-
`sse_read_timeout` determines how long (in seconds) the client will wait for a new
|
32
|
-
event before disconnecting. All other HTTP operations are controlled by `timeout`.
|
33
|
-
"""
|
34
|
-
read_stream: MemoryObjectReceiveStream[types.JSONRPCMessage | Exception]
|
35
|
-
read_stream_writer: MemoryObjectSendStream[types.JSONRPCMessage | Exception]
|
36
|
-
|
37
|
-
write_stream: MemoryObjectSendStream[types.JSONRPCMessage]
|
38
|
-
write_stream_reader: MemoryObjectReceiveStream[types.JSONRPCMessage]
|
39
|
-
|
40
|
-
read_stream_writer, read_stream = anyio.create_memory_object_stream(0)
|
41
|
-
write_stream, write_stream_reader = anyio.create_memory_object_stream(0)
|
42
|
-
|
43
|
-
async with anyio.create_task_group() as tg:
|
44
|
-
try:
|
45
|
-
logger.info(f"Connecting to SSE endpoint: {remove_request_params(url)}")
|
46
|
-
async with httpx.AsyncClient(headers=headers) as client:
|
47
|
-
async with aconnect_sse(
|
48
|
-
client,
|
49
|
-
"GET",
|
50
|
-
url,
|
51
|
-
timeout=httpx.Timeout(timeout, read=sse_read_timeout),
|
52
|
-
) as event_source:
|
53
|
-
event_source.response.raise_for_status()
|
54
|
-
logger.debug("SSE connection established")
|
55
|
-
|
56
|
-
async def sse_reader(
|
57
|
-
task_status: TaskStatus[str] = anyio.TASK_STATUS_IGNORED,
|
58
|
-
):
|
59
|
-
try:
|
60
|
-
async for sse in event_source.aiter_sse():
|
61
|
-
logger.debug(f"Received SSE event: {sse.event}")
|
62
|
-
match sse.event:
|
63
|
-
case "endpoint":
|
64
|
-
endpoint_url = urljoin(url, sse.data)
|
65
|
-
logger.info(
|
66
|
-
f"Received endpoint URL: {endpoint_url}"
|
67
|
-
)
|
68
|
-
|
69
|
-
url_parsed = urlparse(url)
|
70
|
-
endpoint_parsed = urlparse(endpoint_url)
|
71
|
-
if (
|
72
|
-
url_parsed.netloc != endpoint_parsed.netloc
|
73
|
-
or url_parsed.scheme
|
74
|
-
!= endpoint_parsed.scheme
|
75
|
-
):
|
76
|
-
error_msg = (
|
77
|
-
"Endpoint origin does not match "
|
78
|
-
f"connection origin: {endpoint_url}"
|
79
|
-
)
|
80
|
-
logger.error(error_msg)
|
81
|
-
raise ValueError(error_msg)
|
82
|
-
|
83
|
-
task_status.started(endpoint_url)
|
84
|
-
|
85
|
-
case "message":
|
86
|
-
try:
|
87
|
-
message = types.JSONRPCMessage.model_validate_json( # noqa: E501
|
88
|
-
sse.data
|
89
|
-
)
|
90
|
-
logger.debug(
|
91
|
-
f"Received server message: {message}"
|
92
|
-
)
|
93
|
-
except Exception as exc:
|
94
|
-
logger.error(
|
95
|
-
f"Error parsing server message: {exc}"
|
96
|
-
)
|
97
|
-
await read_stream_writer.send(exc)
|
98
|
-
continue
|
99
|
-
|
100
|
-
await read_stream_writer.send(message)
|
101
|
-
except Exception as exc:
|
102
|
-
logger.error(f"Error in sse_reader: {exc}")
|
103
|
-
await read_stream_writer.send(exc)
|
104
|
-
finally:
|
105
|
-
await read_stream_writer.aclose()
|
106
|
-
|
107
|
-
async def post_writer(endpoint_url: str):
|
108
|
-
try:
|
109
|
-
async with write_stream_reader:
|
110
|
-
async for message in write_stream_reader:
|
111
|
-
logger.debug(f"Sending client message: {message}")
|
112
|
-
response = await client.post(
|
113
|
-
endpoint_url,
|
114
|
-
json=message.model_dump(
|
115
|
-
by_alias=True,
|
116
|
-
mode="json",
|
117
|
-
exclude_none=True,
|
118
|
-
),
|
119
|
-
)
|
120
|
-
response.raise_for_status()
|
121
|
-
logger.debug(
|
122
|
-
"Client message sent successfully: "
|
123
|
-
f"{response.status_code}"
|
124
|
-
)
|
125
|
-
except Exception as exc:
|
126
|
-
logger.error(f"Error in post_writer: {exc}")
|
127
|
-
finally:
|
128
|
-
await write_stream.aclose()
|
129
|
-
|
130
|
-
endpoint_url = await tg.start(sse_reader)
|
131
|
-
logger.info(
|
132
|
-
f"Starting post writer with endpoint URL: {endpoint_url}"
|
133
|
-
)
|
134
|
-
tg.start_soon(post_writer, endpoint_url)
|
135
|
-
|
136
|
-
try:
|
137
|
-
yield read_stream, write_stream
|
138
|
-
finally:
|
139
|
-
tg.cancel_scope.cancel()
|
140
|
-
except Exception as ex:
|
141
|
-
logger.error(f"Error in post_writer: {ex}")
|
142
|
-
raise
|
143
|
-
finally:
|
144
|
-
await read_stream_writer.aclose()
|
145
|
-
await write_stream.aclose()
|
acp/client/stdio.py
DELETED
@@ -1,153 +0,0 @@
|
|
1
|
-
import os
|
2
|
-
import sys
|
3
|
-
from contextlib import asynccontextmanager
|
4
|
-
from typing import Literal
|
5
|
-
|
6
|
-
import anyio
|
7
|
-
import anyio.lowlevel
|
8
|
-
from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
|
9
|
-
from anyio.streams.text import TextReceiveStream
|
10
|
-
from pydantic import BaseModel, Field
|
11
|
-
|
12
|
-
import acp.types as types
|
13
|
-
|
14
|
-
# Environment variables to inherit by default
|
15
|
-
DEFAULT_INHERITED_ENV_VARS = (
|
16
|
-
[
|
17
|
-
"APPDATA",
|
18
|
-
"HOMEDRIVE",
|
19
|
-
"HOMEPATH",
|
20
|
-
"LOCALAPPDATA",
|
21
|
-
"PATH",
|
22
|
-
"PROCESSOR_ARCHITECTURE",
|
23
|
-
"SYSTEMDRIVE",
|
24
|
-
"SYSTEMROOT",
|
25
|
-
"TEMP",
|
26
|
-
"USERNAME",
|
27
|
-
"USERPROFILE",
|
28
|
-
]
|
29
|
-
if sys.platform == "win32"
|
30
|
-
else ["HOME", "LOGNAME", "PATH", "SHELL", "TERM", "USER"]
|
31
|
-
)
|
32
|
-
|
33
|
-
|
34
|
-
def get_default_environment() -> dict[str, str]:
|
35
|
-
"""
|
36
|
-
Returns a default environment object including only environment variables deemed
|
37
|
-
safe to inherit.
|
38
|
-
"""
|
39
|
-
env: dict[str, str] = {}
|
40
|
-
|
41
|
-
for key in DEFAULT_INHERITED_ENV_VARS:
|
42
|
-
value = os.environ.get(key)
|
43
|
-
if value is None:
|
44
|
-
continue
|
45
|
-
|
46
|
-
if value.startswith("()"):
|
47
|
-
# Skip functions, which are a security risk
|
48
|
-
continue
|
49
|
-
|
50
|
-
env[key] = value
|
51
|
-
|
52
|
-
return env
|
53
|
-
|
54
|
-
|
55
|
-
class StdioServerParameters(BaseModel):
|
56
|
-
command: str
|
57
|
-
"""The executable to run to start the server."""
|
58
|
-
|
59
|
-
args: list[str] = Field(default_factory=list)
|
60
|
-
"""Command line arguments to pass to the executable."""
|
61
|
-
|
62
|
-
env: dict[str, str] | None = None
|
63
|
-
"""
|
64
|
-
The environment to use when spawning the process.
|
65
|
-
|
66
|
-
If not specified, the result of get_default_environment() will be used.
|
67
|
-
"""
|
68
|
-
|
69
|
-
encoding: str = "utf-8"
|
70
|
-
"""
|
71
|
-
The text encoding used when sending/receiving messages to the server
|
72
|
-
|
73
|
-
defaults to utf-8
|
74
|
-
"""
|
75
|
-
|
76
|
-
encoding_error_handler: Literal["strict", "ignore", "replace"] = "strict"
|
77
|
-
"""
|
78
|
-
The text encoding error handler.
|
79
|
-
|
80
|
-
See https://docs.python.org/3/library/codecs.html#codec-base-classes for
|
81
|
-
explanations of possible values
|
82
|
-
"""
|
83
|
-
|
84
|
-
|
85
|
-
@asynccontextmanager
|
86
|
-
async def stdio_client(server: StdioServerParameters):
|
87
|
-
"""
|
88
|
-
Client transport for stdio: this will connect to a server by spawning a
|
89
|
-
process and communicating with it over stdin/stdout.
|
90
|
-
"""
|
91
|
-
read_stream: MemoryObjectReceiveStream[types.JSONRPCMessage | Exception]
|
92
|
-
read_stream_writer: MemoryObjectSendStream[types.JSONRPCMessage | Exception]
|
93
|
-
|
94
|
-
write_stream: MemoryObjectSendStream[types.JSONRPCMessage]
|
95
|
-
write_stream_reader: MemoryObjectReceiveStream[types.JSONRPCMessage]
|
96
|
-
|
97
|
-
read_stream_writer, read_stream = anyio.create_memory_object_stream(0)
|
98
|
-
write_stream, write_stream_reader = anyio.create_memory_object_stream(0)
|
99
|
-
|
100
|
-
process = await anyio.open_process(
|
101
|
-
[server.command, *server.args],
|
102
|
-
env=server.env if server.env is not None else get_default_environment(),
|
103
|
-
stderr=sys.stderr,
|
104
|
-
)
|
105
|
-
|
106
|
-
async def stdout_reader():
|
107
|
-
assert process.stdout, "Opened process is missing stdout"
|
108
|
-
|
109
|
-
try:
|
110
|
-
async with read_stream_writer:
|
111
|
-
buffer = ""
|
112
|
-
async for chunk in TextReceiveStream(
|
113
|
-
process.stdout,
|
114
|
-
encoding=server.encoding,
|
115
|
-
errors=server.encoding_error_handler,
|
116
|
-
):
|
117
|
-
lines = (buffer + chunk).split("\n")
|
118
|
-
buffer = lines.pop()
|
119
|
-
|
120
|
-
for line in lines:
|
121
|
-
try:
|
122
|
-
message = types.JSONRPCMessage.model_validate_json(line)
|
123
|
-
except Exception as exc:
|
124
|
-
await read_stream_writer.send(exc)
|
125
|
-
continue
|
126
|
-
|
127
|
-
await read_stream_writer.send(message)
|
128
|
-
except anyio.ClosedResourceError:
|
129
|
-
await anyio.lowlevel.checkpoint()
|
130
|
-
|
131
|
-
async def stdin_writer():
|
132
|
-
assert process.stdin, "Opened process is missing stdin"
|
133
|
-
|
134
|
-
try:
|
135
|
-
async with write_stream_reader:
|
136
|
-
async for message in write_stream_reader:
|
137
|
-
json = message.model_dump_json(by_alias=True, exclude_none=True)
|
138
|
-
await process.stdin.send(
|
139
|
-
(json + "\n").encode(
|
140
|
-
encoding=server.encoding,
|
141
|
-
errors=server.encoding_error_handler,
|
142
|
-
)
|
143
|
-
)
|
144
|
-
except anyio.ClosedResourceError:
|
145
|
-
await anyio.lowlevel.checkpoint()
|
146
|
-
|
147
|
-
async with (
|
148
|
-
anyio.create_task_group() as tg,
|
149
|
-
process,
|
150
|
-
):
|
151
|
-
tg.start_soon(stdout_reader)
|
152
|
-
tg.start_soon(stdin_writer)
|
153
|
-
yield read_stream, write_stream
|
acp/server/__init__.py
DELETED