acp-sdk 0.0.6__tar.gz → 0.1.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 (184) hide show
  1. {acp_sdk-0.0.6 → acp_sdk-0.1.0}/.gitignore +39 -3
  2. acp_sdk-0.1.0/.python-version +1 -0
  3. acp_sdk-0.1.0/PKG-INFO +113 -0
  4. acp_sdk-0.1.0/README.md +91 -0
  5. acp_sdk-0.1.0/examples/clients/advanced.py +21 -0
  6. acp_sdk-0.1.0/examples/clients/session.py +18 -0
  7. acp_sdk-0.1.0/examples/clients/simple.py +19 -0
  8. acp_sdk-0.1.0/examples/clients/stream.py +16 -0
  9. acp_sdk-0.1.0/examples/servers/awaiting.py +23 -0
  10. acp_sdk-0.1.0/examples/servers/echo.py +22 -0
  11. acp_sdk-0.1.0/examples/servers/standalone.py +25 -0
  12. acp_sdk-0.1.0/pyproject.toml +40 -0
  13. acp_sdk-0.1.0/pytest.ini +5 -0
  14. acp_sdk-0.1.0/src/acp_sdk/__init__.py +2 -0
  15. acp_sdk-0.1.0/src/acp_sdk/client/__init__.py +1 -0
  16. acp_sdk-0.1.0/src/acp_sdk/client/client.py +178 -0
  17. acp_sdk-0.1.0/src/acp_sdk/models/__init__.py +3 -0
  18. acp_sdk-0.1.0/src/acp_sdk/models/errors.py +23 -0
  19. acp_sdk-0.1.0/src/acp_sdk/models/models.py +192 -0
  20. acp_sdk-0.1.0/src/acp_sdk/models/schemas.py +41 -0
  21. acp_sdk-0.1.0/src/acp_sdk/server/__init__.py +7 -0
  22. acp_sdk-0.1.0/src/acp_sdk/server/agent.py +178 -0
  23. acp_sdk-0.1.0/src/acp_sdk/server/app.py +176 -0
  24. acp_sdk-0.1.0/src/acp_sdk/server/bundle.py +146 -0
  25. acp_sdk-0.1.0/src/acp_sdk/server/context.py +33 -0
  26. acp_sdk-0.1.0/src/acp_sdk/server/errors.py +54 -0
  27. acp_sdk-0.1.0/src/acp_sdk/server/logging.py +16 -0
  28. acp_sdk-0.1.0/src/acp_sdk/server/server.py +166 -0
  29. acp_sdk-0.1.0/src/acp_sdk/server/session.py +21 -0
  30. acp_sdk-0.1.0/src/acp_sdk/server/telemetry.py +57 -0
  31. acp_sdk-0.1.0/src/acp_sdk/server/types.py +6 -0
  32. acp_sdk-0.1.0/src/acp_sdk/server/utils.py +14 -0
  33. acp_sdk-0.1.0/src/acp_sdk/version.py +3 -0
  34. acp_sdk-0.1.0/tests/conftest.py +4 -0
  35. acp_sdk-0.1.0/tests/e2e/config.py +2 -0
  36. acp_sdk-0.1.0/tests/e2e/fixtures/client.py +12 -0
  37. acp_sdk-0.1.0/tests/e2e/fixtures/server.py +87 -0
  38. acp_sdk-0.1.0/tests/e2e/test_suites/test_runs.py +195 -0
  39. acp_sdk-0.0.6/.github/ISSUE_TEMPLATE/bug_report.md +0 -38
  40. acp_sdk-0.0.6/.github/ISSUE_TEMPLATE/feature_request.md +0 -20
  41. acp_sdk-0.0.6/.github/workflows/check-lock.yml +0 -25
  42. acp_sdk-0.0.6/.github/workflows/main-checks.yml +0 -13
  43. acp_sdk-0.0.6/.github/workflows/publish-pypi.yml +0 -55
  44. acp_sdk-0.0.6/.github/workflows/pull-request-checks.yml +0 -8
  45. acp_sdk-0.0.6/.github/workflows/shared.yml +0 -69
  46. acp_sdk-0.0.6/.pre-commit-config.yaml +0 -24
  47. acp_sdk-0.0.6/CLAUDE.md +0 -114
  48. acp_sdk-0.0.6/CODE_OF_CONDUCT.md +0 -128
  49. acp_sdk-0.0.6/CONTRIBUTING.md +0 -66
  50. acp_sdk-0.0.6/LICENSE +0 -22
  51. acp_sdk-0.0.6/PKG-INFO +0 -46
  52. acp_sdk-0.0.6/README.md +0 -9
  53. acp_sdk-0.0.6/RELEASE.md +0 -15
  54. acp_sdk-0.0.6/SECURITY.md +0 -14
  55. acp_sdk-0.0.6/examples/README.md +0 -5
  56. acp_sdk-0.0.6/examples/clients/simple-chatbot/.python-version +0 -1
  57. acp_sdk-0.0.6/examples/clients/simple-chatbot/README.MD +0 -110
  58. acp_sdk-0.0.6/examples/clients/simple-chatbot/mcp_simple_chatbot/.env.example +0 -1
  59. acp_sdk-0.0.6/examples/clients/simple-chatbot/mcp_simple_chatbot/main.py +0 -429
  60. acp_sdk-0.0.6/examples/clients/simple-chatbot/mcp_simple_chatbot/requirements.txt +0 -4
  61. acp_sdk-0.0.6/examples/clients/simple-chatbot/mcp_simple_chatbot/servers_config.json +0 -12
  62. acp_sdk-0.0.6/examples/clients/simple-chatbot/mcp_simple_chatbot/test.db +0 -0
  63. acp_sdk-0.0.6/examples/clients/simple-chatbot/pyproject.toml +0 -48
  64. acp_sdk-0.0.6/examples/clients/simple-chatbot/uv.lock +0 -555
  65. acp_sdk-0.0.6/examples/highlevel/agents.py +0 -26
  66. acp_sdk-0.0.6/examples/highlevel/complex_inputs.py +0 -30
  67. acp_sdk-0.0.6/examples/highlevel/desktop.py +0 -25
  68. acp_sdk-0.0.6/examples/highlevel/echo.py +0 -30
  69. acp_sdk-0.0.6/examples/highlevel/memory.py +0 -349
  70. acp_sdk-0.0.6/examples/highlevel/parameter_descriptions.py +0 -21
  71. acp_sdk-0.0.6/examples/highlevel/readme-quickstart.py +0 -18
  72. acp_sdk-0.0.6/examples/highlevel/screenshot.py +0 -29
  73. acp_sdk-0.0.6/examples/highlevel/simple_echo.py +0 -14
  74. acp_sdk-0.0.6/examples/highlevel/text_me.py +0 -72
  75. acp_sdk-0.0.6/examples/highlevel/unicode_example.py +0 -64
  76. acp_sdk-0.0.6/examples/servers/simple-prompt/.python-version +0 -1
  77. acp_sdk-0.0.6/examples/servers/simple-prompt/README.md +0 -55
  78. acp_sdk-0.0.6/examples/servers/simple-prompt/mcp_simple_prompt/__init__.py +0 -1
  79. acp_sdk-0.0.6/examples/servers/simple-prompt/mcp_simple_prompt/__main__.py +0 -5
  80. acp_sdk-0.0.6/examples/servers/simple-prompt/mcp_simple_prompt/server.py +0 -127
  81. acp_sdk-0.0.6/examples/servers/simple-prompt/pyproject.toml +0 -47
  82. acp_sdk-0.0.6/examples/servers/simple-resource/.python-version +0 -1
  83. acp_sdk-0.0.6/examples/servers/simple-resource/README.md +0 -48
  84. acp_sdk-0.0.6/examples/servers/simple-resource/mcp_simple_resource/__init__.py +0 -1
  85. acp_sdk-0.0.6/examples/servers/simple-resource/mcp_simple_resource/__main__.py +0 -5
  86. acp_sdk-0.0.6/examples/servers/simple-resource/mcp_simple_resource/server.py +0 -84
  87. acp_sdk-0.0.6/examples/servers/simple-resource/pyproject.toml +0 -47
  88. acp_sdk-0.0.6/examples/servers/simple-tool/.python-version +0 -1
  89. acp_sdk-0.0.6/examples/servers/simple-tool/README.md +0 -48
  90. acp_sdk-0.0.6/examples/servers/simple-tool/mcp_simple_tool/__init__.py +0 -1
  91. acp_sdk-0.0.6/examples/servers/simple-tool/mcp_simple_tool/__main__.py +0 -5
  92. acp_sdk-0.0.6/examples/servers/simple-tool/mcp_simple_tool/server.py +0 -97
  93. acp_sdk-0.0.6/examples/servers/simple-tool/pyproject.toml +0 -47
  94. acp_sdk-0.0.6/pyproject.toml +0 -76
  95. acp_sdk-0.0.6/src/acp/__init__.py +0 -138
  96. acp_sdk-0.0.6/src/acp/cli/__init__.py +0 -6
  97. acp_sdk-0.0.6/src/acp/cli/claude.py +0 -139
  98. acp_sdk-0.0.6/src/acp/cli/cli.py +0 -471
  99. acp_sdk-0.0.6/src/acp/client/__main__.py +0 -79
  100. acp_sdk-0.0.6/src/acp/client/session.py +0 -372
  101. acp_sdk-0.0.6/src/acp/client/sse.py +0 -145
  102. acp_sdk-0.0.6/src/acp/client/stdio.py +0 -153
  103. acp_sdk-0.0.6/src/acp/server/__init__.py +0 -3
  104. acp_sdk-0.0.6/src/acp/server/__main__.py +0 -50
  105. acp_sdk-0.0.6/src/acp/server/highlevel/__init__.py +0 -9
  106. acp_sdk-0.0.6/src/acp/server/highlevel/agents/__init__.py +0 -5
  107. acp_sdk-0.0.6/src/acp/server/highlevel/agents/agent_manager.py +0 -110
  108. acp_sdk-0.0.6/src/acp/server/highlevel/agents/base.py +0 -20
  109. acp_sdk-0.0.6/src/acp/server/highlevel/agents/templates.py +0 -21
  110. acp_sdk-0.0.6/src/acp/server/highlevel/context.py +0 -185
  111. acp_sdk-0.0.6/src/acp/server/highlevel/exceptions.py +0 -25
  112. acp_sdk-0.0.6/src/acp/server/highlevel/prompts/__init__.py +0 -4
  113. acp_sdk-0.0.6/src/acp/server/highlevel/prompts/base.py +0 -167
  114. acp_sdk-0.0.6/src/acp/server/highlevel/prompts/manager.py +0 -50
  115. acp_sdk-0.0.6/src/acp/server/highlevel/prompts/prompt_manager.py +0 -33
  116. acp_sdk-0.0.6/src/acp/server/highlevel/resources/__init__.py +0 -23
  117. acp_sdk-0.0.6/src/acp/server/highlevel/resources/base.py +0 -48
  118. acp_sdk-0.0.6/src/acp/server/highlevel/resources/resource_manager.py +0 -94
  119. acp_sdk-0.0.6/src/acp/server/highlevel/resources/templates.py +0 -80
  120. acp_sdk-0.0.6/src/acp/server/highlevel/resources/types.py +0 -185
  121. acp_sdk-0.0.6/src/acp/server/highlevel/server.py +0 -705
  122. acp_sdk-0.0.6/src/acp/server/highlevel/tools/__init__.py +0 -4
  123. acp_sdk-0.0.6/src/acp/server/highlevel/tools/base.py +0 -83
  124. acp_sdk-0.0.6/src/acp/server/highlevel/tools/tool_manager.py +0 -53
  125. acp_sdk-0.0.6/src/acp/server/highlevel/utilities/__init__.py +0 -1
  126. acp_sdk-0.0.6/src/acp/server/highlevel/utilities/func_metadata.py +0 -210
  127. acp_sdk-0.0.6/src/acp/server/highlevel/utilities/logging.py +0 -43
  128. acp_sdk-0.0.6/src/acp/server/highlevel/utilities/types.py +0 -54
  129. acp_sdk-0.0.6/src/acp/server/lowlevel/__init__.py +0 -3
  130. acp_sdk-0.0.6/src/acp/server/lowlevel/helper_types.py +0 -9
  131. acp_sdk-0.0.6/src/acp/server/lowlevel/server.py +0 -643
  132. acp_sdk-0.0.6/src/acp/server/models.py +0 -17
  133. acp_sdk-0.0.6/src/acp/server/session.py +0 -315
  134. acp_sdk-0.0.6/src/acp/server/sse.py +0 -175
  135. acp_sdk-0.0.6/src/acp/server/stdio.py +0 -83
  136. acp_sdk-0.0.6/src/acp/server/websocket.py +0 -61
  137. acp_sdk-0.0.6/src/acp/shared/context.py +0 -14
  138. acp_sdk-0.0.6/src/acp/shared/exceptions.py +0 -14
  139. acp_sdk-0.0.6/src/acp/shared/memory.py +0 -87
  140. acp_sdk-0.0.6/src/acp/shared/progress.py +0 -40
  141. acp_sdk-0.0.6/src/acp/shared/session.py +0 -413
  142. acp_sdk-0.0.6/src/acp/shared/version.py +0 -3
  143. acp_sdk-0.0.6/src/acp/types.py +0 -1258
  144. acp_sdk-0.0.6/tasks.toml +0 -67
  145. acp_sdk-0.0.6/tests/client/__init__.py +0 -0
  146. acp_sdk-0.0.6/tests/client/test_config.py +0 -52
  147. acp_sdk-0.0.6/tests/client/test_session.py +0 -100
  148. acp_sdk-0.0.6/tests/client/test_stdio.py +0 -38
  149. acp_sdk-0.0.6/tests/conftest.py +0 -6
  150. acp_sdk-0.0.6/tests/issues/test_100_tool_listing.py +0 -35
  151. acp_sdk-0.0.6/tests/issues/test_129_resource_templates.py +0 -44
  152. acp_sdk-0.0.6/tests/issues/test_141_resource_templates.py +0 -118
  153. acp_sdk-0.0.6/tests/issues/test_152_resource_mime_type.py +0 -146
  154. acp_sdk-0.0.6/tests/issues/test_176_progress_token.py +0 -46
  155. acp_sdk-0.0.6/tests/issues/test_88_random_error.py +0 -111
  156. acp_sdk-0.0.6/tests/server/__init__.py +0 -0
  157. acp_sdk-0.0.6/tests/server/fastmcp/__init__.py +0 -0
  158. acp_sdk-0.0.6/tests/server/fastmcp/prompts/__init__.py +0 -0
  159. acp_sdk-0.0.6/tests/server/fastmcp/prompts/test_base.py +0 -206
  160. acp_sdk-0.0.6/tests/server/fastmcp/prompts/test_manager.py +0 -112
  161. acp_sdk-0.0.6/tests/server/fastmcp/resources/__init__.py +0 -0
  162. acp_sdk-0.0.6/tests/server/fastmcp/resources/test_file_resources.py +0 -119
  163. acp_sdk-0.0.6/tests/server/fastmcp/resources/test_function_resources.py +0 -138
  164. acp_sdk-0.0.6/tests/server/fastmcp/resources/test_resource_manager.py +0 -141
  165. acp_sdk-0.0.6/tests/server/fastmcp/resources/test_resource_template.py +0 -188
  166. acp_sdk-0.0.6/tests/server/fastmcp/resources/test_resources.py +0 -101
  167. acp_sdk-0.0.6/tests/server/fastmcp/servers/__init__.py +0 -0
  168. acp_sdk-0.0.6/tests/server/fastmcp/servers/test_file_server.py +0 -123
  169. acp_sdk-0.0.6/tests/server/fastmcp/test_func_metadata.py +0 -401
  170. acp_sdk-0.0.6/tests/server/fastmcp/test_parameter_descriptions.py +0 -30
  171. acp_sdk-0.0.6/tests/server/fastmcp/test_server.py +0 -743
  172. acp_sdk-0.0.6/tests/server/fastmcp/test_tool_manager.py +0 -322
  173. acp_sdk-0.0.6/tests/server/test_read_resource.py +0 -109
  174. acp_sdk-0.0.6/tests/server/test_session.py +0 -101
  175. acp_sdk-0.0.6/tests/server/test_stdio.py +0 -68
  176. acp_sdk-0.0.6/tests/shared/test_memory.py +0 -47
  177. acp_sdk-0.0.6/tests/shared/test_session.py +0 -127
  178. acp_sdk-0.0.6/tests/shared/test_sse.py +0 -254
  179. acp_sdk-0.0.6/tests/test_examples.py +0 -72
  180. acp_sdk-0.0.6/tests/test_types.py +0 -32
  181. {acp_sdk-0.0.6/src/acp → acp_sdk-0.1.0/src/acp_sdk}/py.typed +0 -0
  182. {acp_sdk-0.0.6/src/acp/client → acp_sdk-0.1.0/tests/e2e}/__init__.py +0 -0
  183. {acp_sdk-0.0.6/src/acp/shared → acp_sdk-0.1.0/tests/e2e/fixtures}/__init__.py +0 -0
  184. {acp_sdk-0.0.6/tests → acp_sdk-0.1.0/tests/e2e/test_suites}/__init__.py +0 -0
@@ -1,5 +1,40 @@
1
+ ### NextJS template
2
+ # dependencies
3
+ node_modules
4
+ /.pnp
5
+ .pnp.js
6
+
7
+ # testing
8
+ /coverage
9
+
10
+ # next.js
11
+ /.next/
12
+ /out/
13
+
14
+ # production
15
+ /build
16
+
17
+ # misc
1
18
  .DS_Store
19
+ *.pem
20
+
21
+ # debug
22
+ npm-debug.log*
23
+ yarn-debug.log*
24
+ yarn-error.log*
25
+ .pnpm-debug.log*
26
+
27
+ # local env files
28
+ .env*.local
29
+
30
+ # vercel
31
+ .vercel
32
+
33
+ # typescript
34
+ *.tsbuildinfo
35
+ next-env.d.ts
2
36
 
37
+ ### Python template
3
38
  # Byte-compiled / optimized / DLL files
4
39
  __pycache__/
5
40
  *.py[cod]
@@ -13,6 +48,7 @@ __pycache__/
13
48
  build/
14
49
  develop-eggs/
15
50
  dist/
51
+ dist-py/
16
52
  downloads/
17
53
  eggs/
18
54
  .eggs/
@@ -161,7 +197,7 @@ cython_debug/
161
197
  # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
162
198
  # and can be added to the global gitignore or merged into this file. For a more nuclear
163
199
  # option (not recommended) you can uncomment the following to ignore the entire idea folder.
164
- #.idea/
200
+ .idea/
165
201
 
166
- # vscode
167
- .vscode/
202
+ # Mise
203
+ mise.local.toml
@@ -0,0 +1 @@
1
+ 3.11
acp_sdk-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,113 @@
1
+ Metadata-Version: 2.4
2
+ Name: acp-sdk
3
+ Version: 0.1.0
4
+ Summary: Agent Communication Protocol SDK
5
+ Author: IBM Corp.
6
+ Maintainer-email: Tomas Pilar <thomas7pilar@gmail.com>
7
+ License-Expression: Apache-2.0
8
+ Requires-Python: <4.0,>=3.11
9
+ Requires-Dist: opentelemetry-api>=1.31.1
10
+ Requires-Dist: pydantic>=2.11.1
11
+ Provides-Extra: client
12
+ Requires-Dist: httpx-sse>=0.4.0; extra == 'client'
13
+ Requires-Dist: httpx>=0.28.1; extra == 'client'
14
+ Requires-Dist: opentelemetry-instrumentation-httpx>=0.52b1; extra == 'client'
15
+ Provides-Extra: server
16
+ Requires-Dist: fastapi[standard]>=0.115.8; extra == 'server'
17
+ Requires-Dist: janus>=2.0.0; extra == 'server'
18
+ Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.31.1; extra == 'server'
19
+ Requires-Dist: opentelemetry-instrumentation-fastapi>=0.52b1; extra == 'server'
20
+ Requires-Dist: opentelemetry-sdk>=1.31.1; extra == 'server'
21
+ Description-Content-Type: text/markdown
22
+
23
+ # Agent Communication Protocol SDK for Python
24
+
25
+ Agent Communication Protocol SDK for Python provides allows developers to serve and consume agents over the Agent Communication Protocol.
26
+
27
+ ## Prerequisites
28
+
29
+ ✅ Python >= 3.11
30
+
31
+ ## Installation
32
+
33
+ Install to use client:
34
+
35
+ ```shell
36
+ pip install acp-sdk[client]
37
+ ```
38
+
39
+ Install to use server:
40
+
41
+ ```shell
42
+ pip install acp-sdk[server]
43
+ ```
44
+
45
+ Install to use models only:
46
+
47
+ ```shell
48
+ pip install acp-sdk
49
+ ```
50
+
51
+ ## Overview
52
+
53
+ ### Core
54
+
55
+ The core of the SDK exposes [pydantic](https://docs.pydantic.dev/) data models corresponding to REST API requests, responses, resources, events and errors.
56
+
57
+
58
+ ### Client
59
+
60
+ The `client` submodule exposes [httpx](https://www.python-httpx.org/) based client with simple methods for communication over ACP.
61
+
62
+ ```python
63
+ async with Client(base_url="http://localhost:8000") as client:
64
+ run = await client.run_sync(agent="echo", inputs=[Message(parts=[MessagePart(content="Howdy!")])])
65
+ print(run)
66
+
67
+ ```
68
+
69
+ ### Server
70
+
71
+ The `server` submodule exposes `Agent` class and `agent` decorator together with [fastapi](https://fastapi.tiangolo.com/) application factory, making it easy to expose agents over ACP. Additionaly, it exposes [uvicorn](https://www.uvicorn.org/) based server to serve agents with set up logging, [opentelemetry](https://opentelemetry.io/) and more.
72
+
73
+ ```python
74
+ server = Server()
75
+
76
+ @server.agent()
77
+ async def echo(inputs: list[Message], context: Context) -> AsyncGenerator[RunYield, RunYieldResume]:
78
+ """Echoes everything"""
79
+ for message in inputs:
80
+ yield {"thought": "I should echo everyting"}
81
+ await asyncio.sleep(0.5)
82
+ yield message
83
+
84
+
85
+ server.run()
86
+ ```
87
+
88
+ ➡️ Explore more in our [examples library](/python/examples).
89
+
90
+ ## Architecture
91
+
92
+ The architecture of the SDK is outlined in the following segment. It focuses on central parts of the SDK without going into much detail.
93
+
94
+ ### Models
95
+
96
+ The core of the SDK contains pydantic models for requests, responses, resources, events and errors. Users of the SDK are meant to use these models directly or indirectly.
97
+
98
+ ### Server
99
+
100
+ The server module consists of 3 parts:
101
+
102
+ 1. Agent interface
103
+ 2. FastAPI application factory
104
+ 3. Uvicorn based server
105
+
106
+ Each part builds on top of the previous one. Not all parts need to be used, e.g. users are advised to bring their own ASGI server for production deployments.
107
+
108
+ ### Client
109
+
110
+ The client module consists of httpx based client with session support. The client is meant to be thin and mimic the REST API. Exception is session management which has been abstracted into a context manager.
111
+
112
+
113
+
@@ -0,0 +1,91 @@
1
+ # Agent Communication Protocol SDK for Python
2
+
3
+ Agent Communication Protocol SDK for Python provides allows developers to serve and consume agents over the Agent Communication Protocol.
4
+
5
+ ## Prerequisites
6
+
7
+ ✅ Python >= 3.11
8
+
9
+ ## Installation
10
+
11
+ Install to use client:
12
+
13
+ ```shell
14
+ pip install acp-sdk[client]
15
+ ```
16
+
17
+ Install to use server:
18
+
19
+ ```shell
20
+ pip install acp-sdk[server]
21
+ ```
22
+
23
+ Install to use models only:
24
+
25
+ ```shell
26
+ pip install acp-sdk
27
+ ```
28
+
29
+ ## Overview
30
+
31
+ ### Core
32
+
33
+ The core of the SDK exposes [pydantic](https://docs.pydantic.dev/) data models corresponding to REST API requests, responses, resources, events and errors.
34
+
35
+
36
+ ### Client
37
+
38
+ The `client` submodule exposes [httpx](https://www.python-httpx.org/) based client with simple methods for communication over ACP.
39
+
40
+ ```python
41
+ async with Client(base_url="http://localhost:8000") as client:
42
+ run = await client.run_sync(agent="echo", inputs=[Message(parts=[MessagePart(content="Howdy!")])])
43
+ print(run)
44
+
45
+ ```
46
+
47
+ ### Server
48
+
49
+ The `server` submodule exposes `Agent` class and `agent` decorator together with [fastapi](https://fastapi.tiangolo.com/) application factory, making it easy to expose agents over ACP. Additionaly, it exposes [uvicorn](https://www.uvicorn.org/) based server to serve agents with set up logging, [opentelemetry](https://opentelemetry.io/) and more.
50
+
51
+ ```python
52
+ server = Server()
53
+
54
+ @server.agent()
55
+ async def echo(inputs: list[Message], context: Context) -> AsyncGenerator[RunYield, RunYieldResume]:
56
+ """Echoes everything"""
57
+ for message in inputs:
58
+ yield {"thought": "I should echo everyting"}
59
+ await asyncio.sleep(0.5)
60
+ yield message
61
+
62
+
63
+ server.run()
64
+ ```
65
+
66
+ ➡️ Explore more in our [examples library](/python/examples).
67
+
68
+ ## Architecture
69
+
70
+ The architecture of the SDK is outlined in the following segment. It focuses on central parts of the SDK without going into much detail.
71
+
72
+ ### Models
73
+
74
+ The core of the SDK contains pydantic models for requests, responses, resources, events and errors. Users of the SDK are meant to use these models directly or indirectly.
75
+
76
+ ### Server
77
+
78
+ The server module consists of 3 parts:
79
+
80
+ 1. Agent interface
81
+ 2. FastAPI application factory
82
+ 3. Uvicorn based server
83
+
84
+ Each part builds on top of the previous one. Not all parts need to be used, e.g. users are advised to bring their own ASGI server for production deployments.
85
+
86
+ ### Client
87
+
88
+ The client module consists of httpx based client with session support. The client is meant to be thin and mimic the REST API. Exception is session management which has been abstracted into a context manager.
89
+
90
+
91
+
@@ -0,0 +1,21 @@
1
+ import asyncio
2
+
3
+ import httpx
4
+ from acp_sdk.client.client import Client
5
+ from acp_sdk.models import Message, MessagePart
6
+
7
+
8
+ async def example() -> None:
9
+ async with Client(
10
+ client=httpx.AsyncClient(
11
+ base_url="http://localhost:8000",
12
+ auth=httpx.BasicAuth(username="username", password="password"),
13
+ # Additional client configuration
14
+ )
15
+ ) as client:
16
+ run = await client.run_sync(agent="echo", inputs=[Message(parts=[MessagePart(content="Howdy!")])])
17
+ print(run)
18
+
19
+
20
+ if __name__ == "__main__":
21
+ asyncio.run(example())
@@ -0,0 +1,18 @@
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())
@@ -0,0 +1,19 @@
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:
12
+ run = await client.run_sync(
13
+ agent="echo", inputs=[Message(parts=[MessagePart(content="Howdy!", content_type="text/plain")])]
14
+ )
15
+ print(run.outputs)
16
+
17
+
18
+ if __name__ == "__main__":
19
+ asyncio.run(example())
@@ -0,0 +1,16 @@
1
+ import asyncio
2
+
3
+ from acp_sdk.client import Client
4
+ from acp_sdk.models import Message, MessagePart
5
+
6
+
7
+ async def example() -> None:
8
+ async with Client(base_url="http://localhost:8000") as client:
9
+ async for event in client.run_stream(
10
+ agent="echo", inputs=[Message(parts=[MessagePart(content="Howdy!", content_type="text/plain")])]
11
+ ):
12
+ print(event)
13
+
14
+
15
+ if __name__ == "__main__":
16
+ asyncio.run(example())
@@ -0,0 +1,23 @@
1
+ from collections.abc import AsyncGenerator
2
+ from typing import Any
3
+
4
+ from acp_sdk.models import (
5
+ Await,
6
+ AwaitResume,
7
+ Message,
8
+ MessagePart,
9
+ )
10
+ from acp_sdk.server import Context, Server
11
+
12
+ server = Server()
13
+
14
+
15
+ @server.agent()
16
+ async def awaiting(inputs: list[Message], context: Context) -> AsyncGenerator[Message | Await | Any, AwaitResume]:
17
+ """Greets and awaits for more data"""
18
+ yield Message(parts=[MessagePart(content="Hello!", content_type="text/plain")])
19
+ data = yield Await()
20
+ yield Message(parts=[MessagePart(content=f"Thanks for {data}", content_type="text/plain")])
21
+
22
+
23
+ server.run()
@@ -0,0 +1,22 @@
1
+ import asyncio
2
+ from collections.abc import AsyncGenerator
3
+
4
+ from acp_sdk.models import (
5
+ Message,
6
+ )
7
+ from acp_sdk.server import Context, RunYield, RunYieldResume, Server
8
+
9
+ server = Server()
10
+
11
+
12
+ @server.agent()
13
+ async def echo(inputs: list[Message], context: Context) -> AsyncGenerator[RunYield, RunYieldResume]:
14
+ """Echoes everything"""
15
+ for message in inputs:
16
+ await asyncio.sleep(0.5)
17
+ yield {"thought": "I should echo everyting"}
18
+ await asyncio.sleep(0.5)
19
+ yield message
20
+
21
+
22
+ server.run()
@@ -0,0 +1,25 @@
1
+ from collections.abc import AsyncGenerator
2
+
3
+ from acp_sdk.models import (
4
+ Message,
5
+ )
6
+ from acp_sdk.server import RunYield, RunYieldResume, agent, create_app
7
+
8
+ # This example demonstrates how to serve agents with you own server
9
+
10
+
11
+ @agent()
12
+ async def echo(inputs: list[Message]) -> AsyncGenerator[RunYield, RunYieldResume]:
13
+ """Echoes everything"""
14
+ for message in inputs:
15
+ yield message
16
+
17
+
18
+ app = create_app(echo)
19
+
20
+ # The app can now be used with any ASGI server
21
+
22
+ # Run with
23
+ # 1. fastapi run examples/servers/standalone.py
24
+ # 2. uvicorn examples.servers.standalone:app
25
+ # ...
@@ -0,0 +1,40 @@
1
+ [project]
2
+ name = "acp-sdk"
3
+ version = "0.1.0"
4
+ description = "Agent Communication Protocol SDK"
5
+ license = "Apache-2.0"
6
+ readme = "README.md"
7
+ authors = [{ name = "IBM Corp." }]
8
+ maintainers = [{ name = "Tomas Pilar", email = "thomas7pilar@gmail.com" }]
9
+ requires-python = ">=3.11, <4.0"
10
+ dependencies = ["opentelemetry-api>=1.31.1", "pydantic>=2.11.1"]
11
+
12
+ [project.optional-dependencies]
13
+ client = [
14
+ "httpx>=0.28.1",
15
+ "httpx-sse>=0.4.0",
16
+ "opentelemetry-instrumentation-httpx>=0.52b1",
17
+ ]
18
+ server = [
19
+ "fastapi[standard]>=0.115.8",
20
+ "opentelemetry-exporter-otlp-proto-http>=1.31.1",
21
+ "opentelemetry-instrumentation-fastapi>=0.52b1",
22
+ "opentelemetry-sdk>=1.31.1",
23
+ "janus>=2.0.0",
24
+ ]
25
+
26
+ [tool.uv]
27
+ dev-dependencies = [
28
+ "httpx>=0.28.1",
29
+ "httpx-sse>=0.4.0",
30
+ "opentelemetry-instrumentation-httpx>=0.52b1",
31
+ "fastapi[standard]>=0.115.8",
32
+ "opentelemetry-exporter-otlp-proto-http>=1.31.1",
33
+ "opentelemetry-instrumentation-fastapi>=0.52b1",
34
+ "opentelemetry-sdk>=1.31.1",
35
+ "janus>=2.0.0",
36
+ ]
37
+
38
+ [build-system]
39
+ requires = ["hatchling"]
40
+ build-backend = "hatchling.build"
@@ -0,0 +1,5 @@
1
+ [pytest]
2
+ testpaths = tests/e2e
3
+ python_files = test_*.py
4
+ python_functions = test_*
5
+ addopts = -v --strict-markers
@@ -0,0 +1,2 @@
1
+ from acp_sdk.models import * # noqa: F403
2
+ from acp_sdk.version import __version__ as __version__
@@ -0,0 +1 @@
1
+ from acp_sdk.client.client import Client as Client
@@ -0,0 +1,178 @@
1
+ import uuid
2
+ from collections.abc import AsyncGenerator, AsyncIterator
3
+ from contextlib import asynccontextmanager
4
+ from types import TracebackType
5
+ from typing import Self
6
+
7
+ import httpx
8
+ from httpx_sse import EventSource, aconnect_sse
9
+ from opentelemetry.instrumentation.httpx import HTTPXClientInstrumentor
10
+ from pydantic import TypeAdapter
11
+
12
+ from acp_sdk.models import (
13
+ ACPError,
14
+ Agent,
15
+ AgentName,
16
+ AgentReadResponse,
17
+ AgentsListResponse,
18
+ AwaitResume,
19
+ CreatedEvent,
20
+ Error,
21
+ Message,
22
+ Run,
23
+ RunCancelResponse,
24
+ RunCreateRequest,
25
+ RunCreateResponse,
26
+ RunEvent,
27
+ RunId,
28
+ RunMode,
29
+ RunResumeRequest,
30
+ RunResumeResponse,
31
+ SessionId,
32
+ )
33
+
34
+
35
+ class Client:
36
+ def __init__(
37
+ self,
38
+ *,
39
+ base_url: httpx.URL | str = "",
40
+ session_id: SessionId | None = None,
41
+ client: httpx.AsyncClient | None = None,
42
+ ) -> None:
43
+ self.base_url = base_url
44
+ self.session_id = session_id
45
+
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
52
+
53
+ async def __aenter__(self) -> Self:
54
+ await self._client.__aenter__()
55
+ return self
56
+
57
+ async def __aexit__(
58
+ self,
59
+ exc_type: type[BaseException] | None = None,
60
+ exc_value: BaseException | None = None,
61
+ traceback: TracebackType | None = None,
62
+ ) -> None:
63
+ await self._client.__aexit__(exc_type, exc_value, traceback)
64
+
65
+ @asynccontextmanager
66
+ async def session(self, session_id: SessionId | None = None) -> AsyncGenerator[Self]:
67
+ yield Client(client=self._client, session_id=session_id or uuid.uuid4())
68
+
69
+ async def agents(self) -> AsyncIterator[Agent]:
70
+ response = await self._client.get("/agents")
71
+ self._raise_error(response)
72
+ for agent in AgentsListResponse.model_validate(response.json()).agents:
73
+ yield agent
74
+
75
+ async def agent(self, *, name: AgentName) -> Agent:
76
+ response = await self._client.get(f"/agents/{name}")
77
+ self._raise_error(response)
78
+ return AgentReadResponse.model_validate(response.json())
79
+
80
+ async def run_sync(self, *, agent: AgentName, inputs: list[Message]) -> Run:
81
+ response = await self._client.post(
82
+ "/runs",
83
+ content=RunCreateRequest(
84
+ agent_name=agent,
85
+ inputs=inputs,
86
+ mode=RunMode.SYNC,
87
+ session_id=self.session_id,
88
+ ).model_dump_json(),
89
+ )
90
+ self._raise_error(response)
91
+ response = RunCreateResponse.model_validate(response.json())
92
+ self._set_session(response)
93
+ return response
94
+
95
+ async def run_async(self, *, agent: AgentName, inputs: list[Message]) -> Run:
96
+ response = await self._client.post(
97
+ "/runs",
98
+ content=RunCreateRequest(
99
+ agent_name=agent,
100
+ inputs=inputs,
101
+ mode=RunMode.ASYNC,
102
+ session_id=self.session_id,
103
+ ).model_dump_json(),
104
+ )
105
+ self._raise_error(response)
106
+ response = RunCreateResponse.model_validate(response.json())
107
+ self._set_session(response)
108
+ return response
109
+
110
+ async def run_stream(self, *, agent: AgentName, inputs: list[Message]) -> AsyncIterator[RunEvent]:
111
+ async with aconnect_sse(
112
+ self._client,
113
+ "POST",
114
+ "/runs",
115
+ content=RunCreateRequest(
116
+ agent_name=agent,
117
+ inputs=inputs,
118
+ mode=RunMode.STREAM,
119
+ session_id=self.session_id,
120
+ ).model_dump_json(),
121
+ ) as event_source:
122
+ async for event in self._validate_stream(event_source):
123
+ if isinstance(event, CreatedEvent):
124
+ self._set_session(event.run)
125
+ yield event
126
+
127
+ async def run_status(self, *, run_id: RunId) -> Run:
128
+ response = await self._client.get(f"/runs/{run_id}")
129
+ self._raise_error(response)
130
+ return Run.model_validate(response.json())
131
+
132
+ async def run_cancel(self, *, run_id: RunId) -> Run:
133
+ response = await self._client.post(f"/runs/{run_id}/cancel")
134
+ self._raise_error(response)
135
+ return RunCancelResponse.model_validate(response.json())
136
+
137
+ async def run_resume_sync(self, *, run_id: RunId, await_: AwaitResume) -> Run:
138
+ response = await self._client.post(
139
+ f"/runs/{run_id}",
140
+ json=RunResumeRequest(await_=await_, mode=RunMode.SYNC).model_dump(),
141
+ )
142
+ self._raise_error(response)
143
+ return RunResumeResponse.model_validate(response.json())
144
+
145
+ async def run_resume_async(self, *, run_id: RunId, await_: AwaitResume) -> Run:
146
+ response = await self._client.post(
147
+ f"/runs/{run_id}",
148
+ json=RunResumeRequest(await_=await_, mode=RunMode.ASYNC).model_dump(),
149
+ )
150
+ self._raise_error(response)
151
+ return RunResumeResponse.model_validate(response.json())
152
+
153
+ async def run_resume_stream(self, *, run_id: RunId, await_: AwaitResume) -> AsyncIterator[RunEvent]:
154
+ async with aconnect_sse(
155
+ self._client,
156
+ "POST",
157
+ f"/runs/{run_id}",
158
+ json=RunResumeRequest(await_=await_, mode=RunMode.STREAM).model_dump(),
159
+ ) as event_source:
160
+ async for event in self._validate_stream(event_source):
161
+ yield event
162
+
163
+ async def _validate_stream(
164
+ self,
165
+ event_source: EventSource,
166
+ ) -> AsyncIterator[RunEvent]:
167
+ async for event in event_source.aiter_sse():
168
+ event = TypeAdapter(RunEvent).validate_json(event.data)
169
+ yield event
170
+
171
+ def _raise_error(self, response: httpx.Response) -> None:
172
+ try:
173
+ response.raise_for_status()
174
+ except httpx.HTTPError:
175
+ raise ACPError(Error.model_validate(response.json()))
176
+
177
+ def _set_session(self, run: Run) -> None:
178
+ self.session_id = run.session_id
@@ -0,0 +1,3 @@
1
+ from acp_sdk.models.errors import * # noqa: F403
2
+ from acp_sdk.models.models import * # noqa: F403
3
+ from acp_sdk.models.schemas import * # noqa: F403
@@ -0,0 +1,23 @@
1
+ from enum import Enum
2
+
3
+ from pydantic import BaseModel
4
+
5
+
6
+ class ErrorCode(str, Enum):
7
+ SERVER_ERROR = "server_error"
8
+ INVALID_INPUT = "invalid_input"
9
+ NOT_FOUND = "not_found"
10
+
11
+
12
+ class Error(BaseModel):
13
+ code: ErrorCode
14
+ message: str
15
+
16
+
17
+ class ACPError(Exception):
18
+ def __init__(self, error: Error) -> None:
19
+ super().__init__()
20
+ self.error = error
21
+
22
+ def __str__(self) -> str:
23
+ return str(self.error.message)