acp-sdk 0.2.1__tar.gz → 0.2.3__tar.gz

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 (43) hide show
  1. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/PKG-INFO +1 -1
  2. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/examples/clients/advanced.py +3 -1
  3. acp_sdk-0.2.3/examples/clients/session.py +22 -0
  4. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/examples/clients/simple.py +1 -1
  5. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/pyproject.toml +1 -1
  6. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/src/acp_sdk/client/client.py +11 -13
  7. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/src/acp_sdk/models/models.py +4 -4
  8. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/src/acp_sdk/server/bundle.py +10 -13
  9. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/tests/e2e/fixtures/server.py +2 -2
  10. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/tests/e2e/test_suites/test_runs.py +6 -3
  11. acp_sdk-0.2.1/examples/clients/session.py +0 -18
  12. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/.gitignore +0 -0
  13. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/.python-version +0 -0
  14. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/README.md +0 -0
  15. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/examples/clients/stream.py +0 -0
  16. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/examples/servers/awaiting.py +0 -0
  17. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/examples/servers/echo.py +0 -0
  18. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/examples/servers/standalone.py +0 -0
  19. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/pytest.ini +0 -0
  20. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/src/acp_sdk/__init__.py +0 -0
  21. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/src/acp_sdk/client/__init__.py +0 -0
  22. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/src/acp_sdk/models/__init__.py +0 -0
  23. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/src/acp_sdk/models/errors.py +0 -0
  24. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/src/acp_sdk/models/schemas.py +0 -0
  25. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/src/acp_sdk/py.typed +0 -0
  26. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/src/acp_sdk/server/__init__.py +0 -0
  27. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/src/acp_sdk/server/agent.py +0 -0
  28. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/src/acp_sdk/server/app.py +0 -0
  29. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/src/acp_sdk/server/context.py +0 -0
  30. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/src/acp_sdk/server/errors.py +0 -0
  31. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/src/acp_sdk/server/logging.py +0 -0
  32. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/src/acp_sdk/server/server.py +0 -0
  33. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/src/acp_sdk/server/session.py +0 -0
  34. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/src/acp_sdk/server/telemetry.py +0 -0
  35. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/src/acp_sdk/server/types.py +0 -0
  36. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/src/acp_sdk/server/utils.py +0 -0
  37. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/src/acp_sdk/version.py +0 -0
  38. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/tests/conftest.py +0 -0
  39. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/tests/e2e/__init__.py +0 -0
  40. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/tests/e2e/config.py +0 -0
  41. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/tests/e2e/fixtures/__init__.py +0 -0
  42. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/tests/e2e/fixtures/client.py +0 -0
  43. {acp_sdk-0.2.1 → acp_sdk-0.2.3}/tests/e2e/test_suites/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: acp-sdk
3
- Version: 0.2.1
3
+ Version: 0.2.3
4
4
  Summary: Agent Communication Protocol SDK
5
5
  Author: IBM Corp.
6
6
  Maintainer-email: Tomas Pilar <thomas7pilar@gmail.com>
@@ -13,7 +13,9 @@ async def example() -> None:
13
13
  # Additional client configuration
14
14
  )
15
15
  ) as client:
16
- run = await client.run_sync(agent="echo", inputs=[Message(parts=[MessagePart(content="Howdy!")])])
16
+ run = await client.run_sync(
17
+ agent="echo", inputs=[Message(parts=[MessagePart(content="Howdy!", content_type="text/plain")])]
18
+ )
17
19
  print(run)
18
20
 
19
21
 
@@ -0,0 +1,22 @@
1
+ import asyncio
2
+
3
+ from acp_sdk.client import Client
4
+ from acp_sdk.models import (
5
+ Message,
6
+ MessagePart,
7
+ )
8
+
9
+
10
+ async def example() -> None:
11
+ async with Client(base_url="http://localhost:8000") as client, client.session() as session:
12
+ run = await session.run_sync(
13
+ agent="echo", inputs=[Message(parts=[MessagePart(content="Howdy!", content_type="text/plain")])]
14
+ )
15
+ run = await session.run_sync(
16
+ agent="echo", inputs=[Message(parts=[MessagePart(content="Howdy again!", content_type="text/plain")])]
17
+ )
18
+ print(run)
19
+
20
+
21
+ if __name__ == "__main__":
22
+ asyncio.run(example())
@@ -12,7 +12,7 @@ async def example() -> None:
12
12
  run = await client.run_sync(
13
13
  agent="echo", inputs=[Message(parts=[MessagePart(content="Howdy!", content_type="text/plain")])]
14
14
  )
15
- print(run.outputs)
15
+ print(run)
16
16
 
17
17
 
18
18
  if __name__ == "__main__":
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "acp-sdk"
3
- version = "0.2.1"
3
+ version = "0.2.3"
4
4
  description = "Agent Communication Protocol SDK"
5
5
  license = "Apache-2.0"
6
6
  readme = "README.md"
@@ -39,16 +39,14 @@ class Client:
39
39
  base_url: httpx.URL | str = "",
40
40
  session_id: SessionId | None = None,
41
41
  client: httpx.AsyncClient | None = None,
42
+ instrument: bool = True,
42
43
  ) -> None:
43
44
  self.base_url = base_url
44
45
  self.session_id = session_id
45
46
 
46
- self._client = self._init_client(client)
47
-
48
- def _init_client(self, client: httpx.AsyncClient | None = None) -> httpx.AsyncClient:
49
- client = client or httpx.AsyncClient(base_url=self.base_url)
50
- HTTPXClientInstrumentor.instrument_client(client)
51
- return client
47
+ self._client = client or httpx.AsyncClient(base_url=self.base_url)
48
+ if instrument:
49
+ HTTPXClientInstrumentor.instrument_client(self._client)
52
50
 
53
51
  async def __aenter__(self) -> Self:
54
52
  await self._client.__aenter__()
@@ -64,7 +62,7 @@ class Client:
64
62
 
65
63
  @asynccontextmanager
66
64
  async def session(self, session_id: SessionId | None = None) -> AsyncGenerator[Self]:
67
- yield Client(client=self._client, session_id=session_id or uuid.uuid4())
65
+ yield Client(client=self._client, session_id=session_id or uuid.uuid4(), instrument=False)
68
66
 
69
67
  async def agents(self) -> AsyncIterator[Agent]:
70
68
  response = await self._client.get("/agents")
@@ -134,28 +132,28 @@ class Client:
134
132
  self._raise_error(response)
135
133
  return RunCancelResponse.model_validate(response.json())
136
134
 
137
- async def run_resume_sync(self, *, run_id: RunId, await_: AwaitResume) -> Run:
135
+ async def run_resume_sync(self, *, run_id: RunId, await_resume: AwaitResume) -> Run:
138
136
  response = await self._client.post(
139
137
  f"/runs/{run_id}",
140
- json=RunResumeRequest(await_resume=await_, mode=RunMode.SYNC).model_dump(),
138
+ json=RunResumeRequest(await_resume=await_resume, mode=RunMode.SYNC).model_dump(),
141
139
  )
142
140
  self._raise_error(response)
143
141
  return RunResumeResponse.model_validate(response.json())
144
142
 
145
- async def run_resume_async(self, *, run_id: RunId, await_: AwaitResume) -> Run:
143
+ async def run_resume_async(self, *, run_id: RunId, await_resume: AwaitResume) -> Run:
146
144
  response = await self._client.post(
147
145
  f"/runs/{run_id}",
148
- json=RunResumeRequest(await_resume=await_, mode=RunMode.ASYNC).model_dump(),
146
+ json=RunResumeRequest(await_resume=await_resume, mode=RunMode.ASYNC).model_dump(),
149
147
  )
150
148
  self._raise_error(response)
151
149
  return RunResumeResponse.model_validate(response.json())
152
150
 
153
- async def run_resume_stream(self, *, run_id: RunId, await_: AwaitResume) -> AsyncIterator[Event]:
151
+ async def run_resume_stream(self, *, run_id: RunId, await_resume: AwaitResume) -> AsyncIterator[Event]:
154
152
  async with aconnect_sse(
155
153
  self._client,
156
154
  "POST",
157
155
  f"/runs/{run_id}",
158
- json=RunResumeRequest(await_resume=await_, mode=RunMode.STREAM).model_dump(),
156
+ json=RunResumeRequest(await_resume=await_resume, mode=RunMode.STREAM).model_dump(),
159
157
  ) as event_source:
160
158
  async for event in self._validate_stream(event_source):
161
159
  yield event
@@ -113,9 +113,9 @@ class MessageCompletedEvent(BaseModel):
113
113
  message: Message
114
114
 
115
115
 
116
- class AwaitEvent(BaseModel):
117
- type: Literal["await"] = "await"
118
- await_request: AwaitRequest | None = None
116
+ class RunAwaitingEvent(BaseModel):
117
+ type: Literal["run.awaiting"] = "run.awaiting"
118
+ run: Run
119
119
 
120
120
 
121
121
  class GenericEvent(BaseModel):
@@ -155,7 +155,7 @@ Event = Union[
155
155
  ArtifactEvent,
156
156
  MessagePartEvent,
157
157
  MessageCompletedEvent,
158
- AwaitEvent,
158
+ RunAwaitingEvent,
159
159
  GenericEvent,
160
160
  RunCancelledEvent,
161
161
  RunFailedEvent,
@@ -6,17 +6,21 @@ from concurrent.futures import ThreadPoolExecutor
6
6
  from pydantic import ValidationError
7
7
 
8
8
  from acp_sdk.models import (
9
+ ACPError,
9
10
  AnyModel,
10
- AwaitEvent,
11
11
  AwaitRequest,
12
12
  AwaitResume,
13
13
  Error,
14
+ ErrorCode,
14
15
  Event,
15
16
  GenericEvent,
16
17
  Message,
18
+ MessageCompletedEvent,
17
19
  MessageCreatedEvent,
20
+ MessagePart,
18
21
  MessagePartEvent,
19
22
  Run,
23
+ RunAwaitingEvent,
20
24
  RunCancelledEvent,
21
25
  RunCompletedEvent,
22
26
  RunCreatedEvent,
@@ -24,8 +28,6 @@ from acp_sdk.models import (
24
28
  RunInProgressEvent,
25
29
  RunStatus,
26
30
  )
27
- from acp_sdk.models.errors import ErrorCode
28
- from acp_sdk.models.models import MessageCompletedEvent, MessagePart
29
31
  from acp_sdk.server.agent import Agent
30
32
  from acp_sdk.server.logging import logger
31
33
  from acp_sdk.server.telemetry import get_tracer
@@ -120,15 +122,7 @@ class RunBundle:
120
122
  elif isinstance(next, AwaitRequest):
121
123
  self.run.await_request = next
122
124
  self.run.status = RunStatus.AWAITING
123
- await self.emit(
124
- AwaitEvent.model_validate(
125
- {
126
- "run_id": self.run.run_id,
127
- "type": "await",
128
- "await": next,
129
- }
130
- )
131
- )
125
+ await self.emit(RunAwaitingEvent(run=self.run))
132
126
  run_logger.info("Run awaited")
133
127
  await_resume = await self.await_()
134
128
  await self.emit(RunInProgressEvent(run=self.run))
@@ -150,7 +144,10 @@ class RunBundle:
150
144
  await self.emit(RunCancelledEvent(run=self.run))
151
145
  run_logger.info("Run cancelled")
152
146
  except Exception as e:
153
- self.run.error = Error(code=ErrorCode.SERVER_ERROR, message=str(e))
147
+ if isinstance(e, ACPError):
148
+ self.run.error = e.error
149
+ else:
150
+ self.run.error = Error(code=ErrorCode.SERVER_ERROR, message=str(e))
154
151
  self.run.status = RunStatus.FAILED
155
152
  await self.emit(RunFailedEvent(run=self.run))
156
153
  run_logger.exception("Run failed")
@@ -4,7 +4,7 @@ from collections.abc import AsyncGenerator, AsyncIterator, Generator
4
4
  from threading import Thread
5
5
 
6
6
  import pytest
7
- from acp_sdk.models import Artifact, AwaitRequest, AwaitResume, Message, MessagePart
7
+ from acp_sdk.models import ACPError, Artifact, AwaitRequest, AwaitResume, Error, ErrorCode, Message, MessagePart
8
8
  from acp_sdk.server import Context, Server
9
9
 
10
10
  from e2e.config import Config
@@ -26,7 +26,7 @@ def server() -> Generator[None]:
26
26
 
27
27
  @server.agent()
28
28
  async def failer(inputs: list[Message], context: Context) -> AsyncIterator[Message]:
29
- raise RuntimeError("Whoops")
29
+ raise ACPError(Error(code=ErrorCode.INVALID_INPUT, message="Wrong question buddy!"))
30
30
 
31
31
  @server.agent()
32
32
  async def sessioner(inputs: list[Message], context: Context) -> AsyncIterator[Message]:
@@ -5,6 +5,7 @@ from acp_sdk.client import Client
5
5
  from acp_sdk.models import (
6
6
  ArtifactEvent,
7
7
  AwaitResume,
8
+ ErrorCode,
8
9
  Message,
9
10
  MessageCreatedEvent,
10
11
  MessagePart,
@@ -50,6 +51,8 @@ async def test_run_status(server: Server, client: Client) -> None:
50
51
  async def test_failure(server: Server, client: Client) -> None:
51
52
  run = await client.run_sync(agent="failer", inputs=inputs)
52
53
  assert run.status == RunStatus.FAILED
54
+ assert run.error is not None
55
+ assert run.error.code == ErrorCode.INVALID_INPUT
53
56
 
54
57
 
55
58
  @pytest.mark.asyncio
@@ -66,7 +69,7 @@ async def test_run_resume_sync(server: Server, client: Client) -> None:
66
69
  assert run.status == RunStatus.AWAITING
67
70
  assert run.await_request is not None
68
71
 
69
- run = await client.run_resume_sync(run_id=run.run_id, await_=AwaitResume())
72
+ run = await client.run_resume_sync(run_id=run.run_id, await_resume=AwaitResume())
70
73
  assert run.status == RunStatus.COMPLETED
71
74
 
72
75
 
@@ -76,7 +79,7 @@ async def test_run_resume_async(server: Server, client: Client) -> None:
76
79
  assert run.status == RunStatus.AWAITING
77
80
  assert run.await_request is not None
78
81
 
79
- run = await client.run_resume_async(run_id=run.run_id, await_=AwaitResume())
82
+ run = await client.run_resume_async(run_id=run.run_id, await_resume=AwaitResume())
80
83
  assert run.status == RunStatus.IN_PROGRESS
81
84
 
82
85
 
@@ -86,7 +89,7 @@ async def test_run_resume_stream(server: Server, client: Client) -> None:
86
89
  assert run.status == RunStatus.AWAITING
87
90
  assert run.await_request is not None
88
91
 
89
- event_stream = [event async for event in client.run_resume_stream(run_id=run.run_id, await_=AwaitResume())]
92
+ event_stream = [event async for event in client.run_resume_stream(run_id=run.run_id, await_resume=AwaitResume())]
90
93
  assert isinstance(event_stream[0], RunInProgressEvent)
91
94
  assert isinstance(event_stream[-1], RunCompletedEvent)
92
95
 
@@ -1,18 +0,0 @@
1
- import asyncio
2
-
3
- from acp_sdk.client import Client
4
- from acp_sdk.models import (
5
- Message,
6
- MessagePart,
7
- )
8
-
9
-
10
- async def example() -> None:
11
- async with Client(base_url="http://localhost:8000") as client, client.session() as session:
12
- run = await session.run_sync(agent="echo", inputs=[Message(parts=[MessagePart(content="Howdy!")])])
13
- run = await session.run_sync(agent="echo", inputs=[Message(parts=[MessagePart(content="Howdy again!")])])
14
- print(run)
15
-
16
-
17
- if __name__ == "__main__":
18
- asyncio.run(example())
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes