acp-sdk 0.7.3__py3-none-any.whl → 0.8.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.
acp_sdk/client/client.py CHANGED
@@ -23,10 +23,12 @@ from acp_sdk.models import (
23
23
  AwaitResume,
24
24
  Error,
25
25
  Event,
26
+ PingResponse,
26
27
  Run,
27
28
  RunCancelResponse,
28
29
  RunCreateRequest,
29
30
  RunCreateResponse,
31
+ RunEventsListResponse,
30
32
  RunId,
31
33
  RunMode,
32
34
  RunResumeRequest,
@@ -120,9 +122,10 @@ class Client:
120
122
  return Agent(**response.model_dump())
121
123
 
122
124
  async def ping(self) -> bool:
123
- response = await self._client.get("/healthcheck")
125
+ response = await self._client.get("/ping")
124
126
  self._raise_error(response)
125
- return response.json() == "OK"
127
+ PingResponse.model_validate(response.json())
128
+ return
126
129
 
127
130
  async def run_sync(self, input: Input, *, agent: AgentName) -> Run:
128
131
  response = await self._client.post(
@@ -172,6 +175,13 @@ class Client:
172
175
  self._raise_error(response)
173
176
  return Run.model_validate(response.json())
174
177
 
178
+ async def run_events(self, *, run_id: RunId) -> AsyncIterator[Event]:
179
+ response = await self._client.get(f"/runs/{run_id}/events")
180
+ self._raise_error(response)
181
+ response = RunEventsListResponse.model_validate(response.json())
182
+ for event in response.events:
183
+ yield event
184
+
175
185
  async def run_cancel(self, *, run_id: RunId) -> Run:
176
186
  response = await self._client.post(f"/runs/{run_id}/cancel")
177
187
  self._raise_error(response)
acp_sdk/models/models.py CHANGED
@@ -1,5 +1,5 @@
1
1
  import uuid
2
- from datetime import datetime
2
+ from datetime import datetime, timezone
3
3
  from enum import Enum
4
4
  from typing import Any, Literal, Optional, Union
5
5
 
@@ -95,11 +95,17 @@ class Artifact(MessagePart):
95
95
 
96
96
  class Message(BaseModel):
97
97
  parts: list[MessagePart]
98
+ created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
99
+ completed_at: datetime | None = Field(default_factory=lambda: datetime.now(timezone.utc))
98
100
 
99
101
  def __add__(self, other: "Message") -> "Message":
100
102
  if not isinstance(other, Message):
101
103
  raise TypeError(f"Cannot concatenate Message with {type(other).__name__}")
102
- return Message(parts=self.parts + other.parts)
104
+ return Message(
105
+ parts=self.parts + other.parts,
106
+ created_at=min(self.created_at, other.created_at),
107
+ completed_at=max(self.completed_at, other.completed_at),
108
+ )
103
109
 
104
110
  def __str__(self) -> str:
105
111
  return "".join(
@@ -134,7 +140,7 @@ class Message(BaseModel):
134
140
  parts[-1] = join(parts[-1], part)
135
141
  else:
136
142
  parts.append(part)
137
- return Message(parts=parts)
143
+ return Message(parts=parts, created_at=self.created_at, completed_at=self.completed_at)
138
144
 
139
145
 
140
146
  AgentName = str
@@ -185,6 +191,8 @@ class Run(BaseModel):
185
191
  await_request: AwaitRequest | None = None
186
192
  output: list[Message] = []
187
193
  error: Error | None = None
194
+ created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
195
+ finished_at: datetime | None = None
188
196
 
189
197
 
190
198
  class MessageCreatedEvent(BaseModel):
acp_sdk/models/schemas.py CHANGED
@@ -1,6 +1,10 @@
1
1
  from pydantic import BaseModel
2
2
 
3
- from acp_sdk.models.models import Agent, AgentName, AwaitResume, Message, Run, RunMode, SessionId
3
+ from acp_sdk.models.models import Agent, AgentName, AwaitResume, Event, Message, Run, RunMode, SessionId
4
+
5
+
6
+ class PingResponse(BaseModel):
7
+ pass
4
8
 
5
9
 
6
10
  class AgentsListResponse(BaseModel):
@@ -37,3 +41,7 @@ class RunReadResponse(Run):
37
41
 
38
42
  class RunCancelResponse(Run):
39
43
  pass
44
+
45
+
46
+ class RunEventsListResponse(BaseModel):
47
+ events: list[Event]
acp_sdk/server/app.py CHANGED
@@ -21,6 +21,7 @@ from acp_sdk.models import (
21
21
  RunCancelResponse,
22
22
  RunCreateRequest,
23
23
  RunCreateResponse,
24
+ RunEventsListResponse,
24
25
  RunId,
25
26
  RunMode,
26
27
  RunReadResponse,
@@ -29,6 +30,7 @@ from acp_sdk.models import (
29
30
  SessionId,
30
31
  )
31
32
  from acp_sdk.models.errors import ACPError
33
+ from acp_sdk.models.schemas import PingResponse
32
34
  from acp_sdk.server.agent import Agent
33
35
  from acp_sdk.server.bundle import RunBundle
34
36
  from acp_sdk.server.errors import (
@@ -104,9 +106,9 @@ def create_app(
104
106
  agent = find_agent(name)
105
107
  return AgentModel(name=agent.name, description=agent.description, metadata=agent.metadata)
106
108
 
107
- @app.get("/healthcheck")
108
- async def healthcheck() -> str:
109
- return "OK"
109
+ @app.get("/ping")
110
+ async def ping() -> PingResponse:
111
+ return PingResponse()
110
112
 
111
113
  @app.post("/runs")
112
114
  async def create_run(request: RunCreateRequest) -> RunCreateResponse:
@@ -155,6 +157,11 @@ def create_app(
155
157
  bundle = find_run_bundle(run_id)
156
158
  return bundle.run
157
159
 
160
+ @app.get("/runs/{run_id}/events")
161
+ async def list_run_events(run_id: RunId) -> RunEventsListResponse:
162
+ bundle = find_run_bundle(run_id)
163
+ return RunEventsListResponse(events=bundle.events)
164
+
158
165
  @app.post("/runs/{run_id}")
159
166
  async def resume_run(run_id: RunId, request: RunResumeRequest) -> RunResumeResponse:
160
167
  bundle = find_run_bundle(run_id)
acp_sdk/server/bundle.py CHANGED
@@ -2,6 +2,7 @@ import asyncio
2
2
  import logging
3
3
  from collections.abc import AsyncGenerator
4
4
  from concurrent.futures import ThreadPoolExecutor
5
+ from datetime import datetime, timezone
5
6
 
6
7
  from pydantic import BaseModel, ValidationError
7
8
 
@@ -43,6 +44,7 @@ class RunBundle:
43
44
  self.history = history
44
45
 
45
46
  self.stream_queue: asyncio.Queue[Event] = asyncio.Queue()
47
+ self.events: list[Event] = []
46
48
 
47
49
  self.await_queue: asyncio.Queue[AwaitResume] = asyncio.Queue(maxsize=1)
48
50
  self.await_or_terminate_event = asyncio.Event()
@@ -58,7 +60,9 @@ class RunBundle:
58
60
  self.stream_queue.task_done()
59
61
 
60
62
  async def emit(self, event: Event) -> None:
61
- await self.stream_queue.put(event)
63
+ freeze = event.model_copy(deep=True)
64
+ self.events.append(freeze)
65
+ await self.stream_queue.put(freeze)
62
66
 
63
67
  async def await_(self) -> AwaitResume:
64
68
  await self.stream_queue.put(None)
@@ -92,7 +96,9 @@ class RunBundle:
92
96
  async def flush_message() -> None:
93
97
  nonlocal in_message
94
98
  if in_message:
95
- await self.emit(MessageCompletedEvent(message=self.run.output[-1]))
99
+ message = self.run.output[-1]
100
+ message.completed_at = datetime.now(timezone.utc)
101
+ await self.emit(MessageCompletedEvent(message=message))
96
102
  in_message = False
97
103
 
98
104
  try:
@@ -114,7 +120,7 @@ class RunBundle:
114
120
  if isinstance(next, str):
115
121
  next = MessagePart(content=next)
116
122
  if not in_message:
117
- self.run.output.append(Message(parts=[]))
123
+ self.run.output.append(Message(parts=[], completed_at=None))
118
124
  in_message = True
119
125
  await self.emit(MessageCreatedEvent(message=self.run.output[-1]))
120
126
  self.run.output[-1].parts.append(next)
@@ -149,10 +155,12 @@ class RunBundle:
149
155
  except StopAsyncIteration:
150
156
  await flush_message()
151
157
  self.run.status = RunStatus.COMPLETED
158
+ self.run.finished_at = datetime.now(timezone.utc)
152
159
  await self.emit(RunCompletedEvent(run=self.run))
153
160
  run_logger.info("Run completed")
154
161
  except asyncio.CancelledError:
155
162
  self.run.status = RunStatus.CANCELLED
163
+ self.run.finished_at = datetime.now(timezone.utc)
156
164
  await self.emit(RunCancelledEvent(run=self.run))
157
165
  run_logger.info("Run cancelled")
158
166
  except Exception as e:
@@ -161,6 +169,7 @@ class RunBundle:
161
169
  else:
162
170
  self.run.error = Error(code=ErrorCode.SERVER_ERROR, message=str(e))
163
171
  self.run.status = RunStatus.FAILED
172
+ self.run.finished_at = datetime.now(timezone.utc)
164
173
  await self.emit(RunFailedEvent(run=self.run))
165
174
  run_logger.exception("Run failed")
166
175
  raise
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: acp-sdk
3
- Version: 0.7.3
3
+ Version: 0.8.0
4
4
  Summary: Agent Communication Protocol SDK
5
5
  Author: IBM Corp.
6
6
  Maintainer-email: Tomas Pilar <thomas7pilar@gmail.com>
@@ -3,17 +3,17 @@ acp_sdk/instrumentation.py,sha256=JqSyvILN3sGAfOZrmckQq4-M_4_5alyPn95DK0o5lfA,16
3
3
  acp_sdk/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  acp_sdk/version.py,sha256=Niy83rgvigB4hL_rR-O4ySvI7dj6xnqkyOe_JTymi9s,73
5
5
  acp_sdk/client/__init__.py,sha256=Bca1DORrswxzZsrR2aUFpATuNG2xNSmYvF1Z2WJaVbc,51
6
- acp_sdk/client/client.py,sha256=O5i7FW5L_u3OCtJD2_muqSMwVhymlTtxNdk2elVkcO8,8218
6
+ acp_sdk/client/client.py,sha256=GnkbhCoEghI9A0_Otmzb18V4ot1uNz0hEXNjIAPNHOE,8599
7
7
  acp_sdk/client/types.py,sha256=_H6zYt-2OHOOYRtssRnbDIiwmgsl2-KIXc9lb-mJLFA,133
8
8
  acp_sdk/client/utils.py,sha256=2jhJyrPJmVFRoDJh0q_JMqOMlC3IxCh-6HXed-PIZS8,924
9
9
  acp_sdk/models/__init__.py,sha256=numSDBDT1QHx7n_Y3Deb5VOvKWcUBxbOEaMwQBSRHxc,151
10
10
  acp_sdk/models/errors.py,sha256=rEyaMVvQuBi7fwWe_d0PGGySYsD3FZTluQ-SkC0yhAs,444
11
- acp_sdk/models/models.py,sha256=aA_ylU-Z0ZAlqPB8j-JfqJ2q516pgvTIyZ0wbfY8-UU,6654
12
- acp_sdk/models/schemas.py,sha256=OQ2bEfwFFZ1D9mhpgzfPBCcnPNvbl4qRfCWChH2wCro,644
11
+ acp_sdk/models/models.py,sha256=6OEkTMdMsl3_IbiFmFG2ChEqY_KYTf3Bi_MW5p168GQ,7183
12
+ acp_sdk/models/schemas.py,sha256=_ah7_zHsQJGxDXvnzsBvASdRsQHVphFQ7Sum6A04iRw,759
13
13
  acp_sdk/server/__init__.py,sha256=mxBBBFaZuMEUENRMLwp1XZkuLeT9QghcFmNvjnqvAAU,377
14
14
  acp_sdk/server/agent.py,sha256=wvwpi83osmW7zQWxVnzyVMXIvzOswAfhKWHscVIldhA,6245
15
- acp_sdk/server/app.py,sha256=QEkyM68oc9oxVl0ex5E4IMvyAToSiSIaBcUvwELWG4c,6827
16
- acp_sdk/server/bundle.py,sha256=P3DfmzQOewsK8jr8yG8_RsGSrXQI4PsZCQbspVxKlus,6552
15
+ acp_sdk/server/app.py,sha256=1S1mxECioL5NeGjGo5C8u94x7Ybvj79L_Aauu2352vA,7117
16
+ acp_sdk/server/bundle.py,sha256=5Rq6E4WgmuwPQ4u-sViaHeu5loCLEUgojWav9LAghX4,7036
17
17
  acp_sdk/server/context.py,sha256=MgnLV6qcDIhc_0BjW7r4Jj1tHts4ZuwpdTGIBnz2Mgo,1036
18
18
  acp_sdk/server/errors.py,sha256=GSO8yYIqEeX8Y4Lz86ks35dMTHiQiXuOrLYYx0eXsbI,2110
19
19
  acp_sdk/server/logging.py,sha256=Oc8yZigCsuDnHHPsarRzu0RX3NKaLEgpELM2yovGKDI,411
@@ -22,6 +22,6 @@ acp_sdk/server/session.py,sha256=ekz1o6Sy1tQZlpaoS_VgbvFuUQh2qpiHG71mvBdvhgc,662
22
22
  acp_sdk/server/telemetry.py,sha256=1BUxNg-xL_Vqgs27PDWNc3HikrQW2lidAtT_FKlp_Qk,1833
23
23
  acp_sdk/server/types.py,sha256=teBNRWSks8XP1SCQKGEtbNWQahVD3RAOPnysTxcQPxI,292
24
24
  acp_sdk/server/utils.py,sha256=y-DDWv_QI25OJJYP5cni2FzfolKXtH3S2SYOm6OL_gc,1835
25
- acp_sdk-0.7.3.dist-info/METADATA,sha256=v9-DPCCuw6NbWTuFyUDtI9UoTqxkkt1C4iS0wS1ATkw,1651
26
- acp_sdk-0.7.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
27
- acp_sdk-0.7.3.dist-info/RECORD,,
25
+ acp_sdk-0.8.0.dist-info/METADATA,sha256=Inaa0idYypuECgh9JWdUVxIwJDRhslH7-YwAW6lupMg,1651
26
+ acp_sdk-0.8.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
27
+ acp_sdk-0.8.0.dist-info/RECORD,,