acp-sdk 0.12.2__tar.gz → 0.13.0__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 (61) hide show
  1. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/PKG-INFO +1 -1
  2. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/pyproject.toml +1 -1
  3. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/src/acp_sdk/client/client.py +5 -12
  4. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/src/acp_sdk/models/models.py +28 -1
  5. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/src/acp_sdk/server/server.py +7 -7
  6. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/tests/e2e/test_suites/test_sessions.py +15 -0
  7. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/tests/unit/client/test_client.py +10 -5
  8. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/.gitignore +0 -0
  9. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/.python-version +0 -0
  10. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/README.md +0 -0
  11. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/docs/.gitignore +0 -0
  12. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/docs/Makefile +0 -0
  13. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/docs/conf.py +0 -0
  14. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/docs/index.rst +0 -0
  15. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/docs/make.bat +0 -0
  16. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/pytest.ini +0 -0
  17. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/src/acp_sdk/__init__.py +0 -0
  18. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/src/acp_sdk/client/__init__.py +0 -0
  19. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/src/acp_sdk/client/types.py +0 -0
  20. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/src/acp_sdk/client/utils.py +0 -0
  21. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/src/acp_sdk/instrumentation.py +0 -0
  22. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/src/acp_sdk/models/__init__.py +0 -0
  23. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/src/acp_sdk/models/errors.py +0 -0
  24. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/src/acp_sdk/models/platform.py +0 -0
  25. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/src/acp_sdk/models/schemas.py +0 -0
  26. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/src/acp_sdk/models/types.py +0 -0
  27. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/src/acp_sdk/py.typed +0 -0
  28. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/src/acp_sdk/server/__init__.py +0 -0
  29. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/src/acp_sdk/server/agent.py +0 -0
  30. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/src/acp_sdk/server/app.py +0 -0
  31. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/src/acp_sdk/server/context.py +0 -0
  32. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/src/acp_sdk/server/errors.py +0 -0
  33. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/src/acp_sdk/server/executor.py +0 -0
  34. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/src/acp_sdk/server/logging.py +0 -0
  35. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/src/acp_sdk/server/resources.py +0 -0
  36. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/src/acp_sdk/server/store/__init__.py +0 -0
  37. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/src/acp_sdk/server/store/memory_store.py +0 -0
  38. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/src/acp_sdk/server/store/postgresql_store.py +0 -0
  39. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/src/acp_sdk/server/store/redis_store.py +0 -0
  40. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/src/acp_sdk/server/store/store.py +0 -0
  41. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/src/acp_sdk/server/store/utils.py +0 -0
  42. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/src/acp_sdk/server/telemetry.py +0 -0
  43. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/src/acp_sdk/server/types.py +0 -0
  44. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/src/acp_sdk/server/utils.py +0 -0
  45. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/src/acp_sdk/shared/__init__.py +0 -0
  46. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/src/acp_sdk/shared/resources.py +0 -0
  47. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/src/acp_sdk/version.py +0 -0
  48. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/tests/conftest.py +0 -0
  49. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/tests/e2e/__init__.py +0 -0
  50. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/tests/e2e/config.py +0 -0
  51. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/tests/e2e/fixtures/__init__.py +0 -0
  52. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/tests/e2e/fixtures/client.py +0 -0
  53. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/tests/e2e/fixtures/server.py +0 -0
  54. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/tests/e2e/test_suites/__init__.py +0 -0
  55. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/tests/e2e/test_suites/test_discovery.py +0 -0
  56. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/tests/e2e/test_suites/test_runs.py +0 -0
  57. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/tests/unit/client/test_utils.py +0 -0
  58. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/tests/unit/models/__init__.py +0 -0
  59. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/tests/unit/models/test_models.py +0 -0
  60. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/tests/unit/server/__init__.py +0 -0
  61. {acp_sdk-0.12.2 → acp_sdk-0.13.0}/tests/unit/server/test_server.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: acp-sdk
3
- Version: 0.12.2
3
+ Version: 0.13.0
4
4
  Summary: Agent Communication Protocol SDK
5
5
  Author: IBM Corp.
6
6
  Maintainer-email: Tomas Pilar <thomas7pilar@gmail.com>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "acp-sdk"
3
- version = "0.12.2"
3
+ version = "0.13.0"
4
4
  description = "Agent Communication Protocol SDK"
5
5
  license = "Apache-2.0"
6
6
  readme = "README.md"
@@ -252,15 +252,10 @@ class Client:
252
252
  base_url=base_url or self._session_last_refresh_base_url,
253
253
  )
254
254
 
255
- try:
256
- response = await self._client.get(url, timeout=timeout)
257
- response = SessionReadResponse.model_validate(response.json())
258
- self._session = Session(**response.model_dump())
259
- except ACPError as e:
260
- if e.error.code == ErrorCode.NOT_FOUND:
261
- pass
262
- raise e
263
-
255
+ response = await self._client.get(url, timeout=timeout)
256
+ self._raise_error(response)
257
+ response = SessionReadResponse.model_validate(response.json())
258
+ self._session = Session(**response.model_dump())
264
259
  return self._session
265
260
 
266
261
  async def _validate_stream(
@@ -304,13 +299,11 @@ class Client:
304
299
 
305
300
  target_base_url = self._create_base_url(base_url=base_url)
306
301
  try:
307
- if not self._session_last_refresh_base_url:
308
- return {"session": self._session}
309
302
  if self._session_last_refresh_base_url == target_base_url:
310
303
  # Same server, no need to forward session
311
304
  return {"session_id": self._session.id}
312
305
 
313
- session = await self.refresh_session()
306
+ session = await self.refresh_session(base_url=self._session_last_refresh_base_url or target_base_url)
314
307
  return {"session": session}
315
308
  except ACPError as e:
316
309
  if e.error.code == ErrorCode.NOT_FOUND:
@@ -118,6 +118,33 @@ class CitationMetadata(BaseModel):
118
118
  description: Optional[str]
119
119
 
120
120
 
121
+ class TrajectoryMetadata(BaseModel):
122
+ """
123
+ Represents trajectory information for an agent's reasoning or tool execution
124
+ steps. This metadata helps track the agent's decision-making process and
125
+ provides transparency into how the agent arrived at its response.
126
+
127
+ TrajectoryMetadata can capture either:
128
+ 1. A reasoning step with a message
129
+ 2. A tool execution with tool name, input, and output
130
+
131
+ This information can be used for debugging, audit trails, and providing
132
+ users with insight into the agent's thought process.
133
+
134
+ Properties:
135
+ - message: A reasoning step or thought in the agent's decision process.
136
+ - tool_name: Name of the tool that was executed.
137
+ - tool_input: Input parameters passed to the tool.
138
+ - tool_output: Output or result returned by the tool.
139
+ """
140
+
141
+ kind: Literal["trajectory"] = "trajectory"
142
+ message: Optional[str] = None
143
+ tool_name: Optional[str] = None
144
+ tool_input: Optional[AnyModel] = None
145
+ tool_output: Optional[AnyModel] = None
146
+
147
+
121
148
  class MessagePart(BaseModel):
122
149
  name: Optional[str] = None
123
150
  content_type: Optional[str] = "text/plain"
@@ -127,7 +154,7 @@ class MessagePart(BaseModel):
127
154
 
128
155
  model_config = ConfigDict(extra="allow")
129
156
 
130
- metadata: Optional[CitationMetadata] = Field(discriminator="kind", default=None)
157
+ metadata: Optional[CitationMetadata | TrajectoryMetadata] = Field(discriminator="kind", default=None)
131
158
 
132
159
  def model_post_init(self, __context: Any) -> None:
133
160
  if self.content is not None and self.content_url is not None:
@@ -331,14 +331,14 @@ class Server:
331
331
  request_data = {
332
332
  "location": f"http://{host}:{self.server.config.port}",
333
333
  }
334
- await async_request_with_retry(lambda client, data=request_data: client.get(f"{url}/api/v1/providers"))
335
334
  try:
335
+ await async_request_with_retry(lambda client, data=request_data: client.get(f"{url}/api/v1/providers"))
336
336
  await async_request_with_retry(
337
337
  lambda client, data=request_data: client.post(
338
338
  f"{url}/api/v1/providers", json=data, params={"auto_remove": True}
339
339
  )
340
340
  )
341
- logger.info("Agent registered to the beeai server.")
341
+ logger.debug("Agent registered to the beeai server.")
342
342
 
343
343
  # check missing env keyes
344
344
  envs_request = await async_request_with_retry(lambda client: client.get(f"{url}/api/v1/variables"))
@@ -361,17 +361,17 @@ class Server:
361
361
  elif env.get("required"):
362
362
  missing_keyes.append(env)
363
363
  if len(missing_keyes):
364
- logger.error(f"Can not run agent, missing required env variables: {missing_keyes}")
364
+ logger.debug(f"Can not run agent, missing required env variables: {missing_keyes}")
365
365
  raise Exception("Missing env variables")
366
366
 
367
367
  except requests.exceptions.ConnectionError as e:
368
- logger.warning(f"Can not reach server, check if running on {url} : {e}")
368
+ logger.debug(f"Can not reach server, check if running on {url} : {e}")
369
369
  except (requests.exceptions.HTTPError, Exception) as e:
370
370
  try:
371
371
  error_message = e.response.json().get("detail")
372
372
  if error_message:
373
- logger.warning(f"Agent can not be registered to beeai server: {error_message}")
373
+ logger.debug(f"Agent can not be registered to beeai server: {error_message}")
374
374
  else:
375
- logger.warning(f"Agent can not be registered to beeai server: {e}")
375
+ logger.debug(f"Agent can not be registered to beeai server: {e}")
376
376
  except Exception:
377
- logger.warning(f"Agent can not be registered to beeai server: {e}")
377
+ logger.debug(f"Agent can not be registered to beeai server: {e}")
@@ -6,6 +6,7 @@ from acp_sdk.models import (
6
6
  Message,
7
7
  MessagePart,
8
8
  )
9
+ from acp_sdk.models.models import Session
9
10
  from acp_sdk.server import Server
10
11
 
11
12
  agent = "history_echo"
@@ -31,6 +32,20 @@ async def test_session_refresh(server: Server, client: Client) -> None:
31
32
  assert len(sess.history) == len(input) * 2
32
33
 
33
34
 
35
+ @pytest.mark.asyncio
36
+ async def test_session_multi_client(server: Server, client: Client) -> None:
37
+ session = Session()
38
+
39
+ async with client.session(session) as session_client:
40
+ run = await session_client.run_sync(input, agent=agent)
41
+ assert run.output == output
42
+ print(run.session_id)
43
+
44
+ async with client.session(session) as session_client:
45
+ run = await session_client.run_sync(input, agent=agent)
46
+ assert run.output == output * 3
47
+
48
+
34
49
  @pytest.mark.asyncio
35
50
  async def test_distributed_session(multi_server: tuple[Server, Server]) -> None:
36
51
  one, two = multi_server
@@ -176,23 +176,28 @@ async def test_run_events(httpx_mock: HTTPXMock) -> None:
176
176
  @pytest.mark.asyncio
177
177
  async def test_session(httpx_mock: HTTPXMock) -> None:
178
178
  httpx_mock.add_response(url="http://test/runs", method="POST", content=mock_run.model_dump_json(), is_reusable=True)
179
+ httpx_mock.add_response(
180
+ url=f"http://test/sessions/{mock_session.id}",
181
+ method="GET",
182
+ content=mock_session.model_dump_json(),
183
+ is_reusable=True,
184
+ )
179
185
 
180
- async with Client(base_url="http://test") as client, client.session(Session(id=mock_run.session_id)) as session:
181
- assert session._session.id == mock_run.session_id
186
+ async with Client(base_url="http://test") as client, client.session(mock_session) as session:
182
187
  await session.run_sync("Howdy!", agent=mock_run.agent_name)
183
188
  await session.run_sync("Howdy!", agent=mock_run.agent_name)
184
189
  await client.run_sync("Howdy!", agent=mock_run.agent_name)
185
190
 
186
191
  requests = httpx_mock.get_requests()
187
- body = json.loads(requests[0].content)
192
+ body = json.loads(requests[1].content)
188
193
  # First request gets full session
189
194
  assert body["session"]["id"] == str(mock_run.session_id)
190
195
 
191
- body = json.loads(requests[1].content)
196
+ body = json.loads(requests[2].content)
192
197
  # Second sends just the ID
193
198
  assert body["session_id"] == str(mock_run.session_id)
194
199
 
195
- body = json.loads(requests[2].content)
200
+ body = json.loads(requests[3].content)
196
201
  assert body["session_id"] is None
197
202
  assert body["session"] is None
198
203
 
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
File without changes
File without changes
File without changes