acp-sdk 0.0.6__py3-none-any.whl → 0.1.0rc5__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/__init__.py +1 -0
- acp_sdk/client/__init__.py +1 -0
- acp_sdk/client/client.py +139 -0
- acp_sdk/models/__init__.py +3 -0
- acp_sdk/models/errors.py +23 -0
- acp_sdk/models/models.py +181 -0
- acp_sdk/models/schemas.py +39 -0
- acp_sdk/server/__init__.py +6 -0
- acp_sdk/server/agent.py +105 -0
- acp_sdk/server/app.py +161 -0
- acp_sdk/server/bundle.py +131 -0
- acp_sdk/server/context.py +33 -0
- acp_sdk/server/errors.py +54 -0
- acp_sdk/server/logging.py +16 -0
- acp_sdk/server/server.py +120 -0
- acp_sdk/server/telemetry.py +52 -0
- acp_sdk/server/types.py +6 -0
- acp_sdk/server/utils.py +14 -0
- acp_sdk-0.1.0rc5.dist-info/METADATA +78 -0
- acp_sdk-0.1.0rc5.dist-info/RECORD +22 -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/__init__.py +0 -0
- 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 → acp_sdk}/py.typed +0 -0
- {acp_sdk-0.0.6.dist-info → acp_sdk-0.1.0rc5.dist-info}/WHEEL +0 -0
acp_sdk/server/bundle.py
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
import asyncio
|
2
|
+
import logging
|
3
|
+
from collections.abc import AsyncGenerator
|
4
|
+
from concurrent.futures import ThreadPoolExecutor
|
5
|
+
|
6
|
+
from opentelemetry import trace
|
7
|
+
from pydantic import ValidationError
|
8
|
+
|
9
|
+
from acp_sdk.models import (
|
10
|
+
AnyModel,
|
11
|
+
Await,
|
12
|
+
AwaitEvent,
|
13
|
+
AwaitResume,
|
14
|
+
CancelledEvent,
|
15
|
+
CompletedEvent,
|
16
|
+
CreatedEvent,
|
17
|
+
Error,
|
18
|
+
FailedEvent,
|
19
|
+
GenericEvent,
|
20
|
+
InProgressEvent,
|
21
|
+
Message,
|
22
|
+
MessageEvent,
|
23
|
+
Run,
|
24
|
+
RunEvent,
|
25
|
+
RunStatus,
|
26
|
+
)
|
27
|
+
from acp_sdk.models.errors import ErrorCode
|
28
|
+
from acp_sdk.server.agent import Agent
|
29
|
+
from acp_sdk.server.logging import logger
|
30
|
+
|
31
|
+
|
32
|
+
class RunBundle:
|
33
|
+
def __init__(self, *, agent: Agent, run: Run, task: asyncio.Task | None = None) -> None:
|
34
|
+
self.agent = agent
|
35
|
+
self.run = run
|
36
|
+
self.task = task
|
37
|
+
|
38
|
+
self.stream_queue: asyncio.Queue[RunEvent] = asyncio.Queue()
|
39
|
+
self.composed_message = Message()
|
40
|
+
|
41
|
+
self.await_queue: asyncio.Queue[AwaitResume] = asyncio.Queue(maxsize=1)
|
42
|
+
self.await_or_terminate_event = asyncio.Event()
|
43
|
+
|
44
|
+
async def stream(self) -> AsyncGenerator[RunEvent]:
|
45
|
+
while True:
|
46
|
+
event = await self.stream_queue.get()
|
47
|
+
if event is None:
|
48
|
+
break
|
49
|
+
yield event
|
50
|
+
self.stream_queue.task_done()
|
51
|
+
|
52
|
+
async def emit(self, event: RunEvent) -> None:
|
53
|
+
await self.stream_queue.put(event)
|
54
|
+
|
55
|
+
async def await_(self) -> AwaitResume:
|
56
|
+
await self.stream_queue.put(None)
|
57
|
+
self.await_queue.empty()
|
58
|
+
self.await_or_terminate_event.set()
|
59
|
+
self.await_or_terminate_event.clear()
|
60
|
+
resume = await self.await_queue.get()
|
61
|
+
self.await_queue.task_done()
|
62
|
+
return resume
|
63
|
+
|
64
|
+
async def resume(self, resume: AwaitResume) -> None:
|
65
|
+
self.stream_queue = asyncio.Queue()
|
66
|
+
await self.await_queue.put(resume)
|
67
|
+
|
68
|
+
async def join(self) -> None:
|
69
|
+
await self.await_or_terminate_event.wait()
|
70
|
+
|
71
|
+
async def execute(self, input: Message, *, executor: ThreadPoolExecutor) -> None:
|
72
|
+
with trace.get_tracer(__name__).start_as_current_span("execute"):
|
73
|
+
run_logger = logging.LoggerAdapter(logger, {"run_id": self.run.run_id})
|
74
|
+
|
75
|
+
await self.emit(CreatedEvent(run=self.run))
|
76
|
+
try:
|
77
|
+
self.run.session_id = await self.agent.session(self.run.session_id)
|
78
|
+
run_logger.info("Session loaded")
|
79
|
+
|
80
|
+
generator = self.agent.execute(input=input, session_id=self.run.session_id, executor=executor)
|
81
|
+
run_logger.info("Run started")
|
82
|
+
|
83
|
+
self.run.status = RunStatus.IN_PROGRESS
|
84
|
+
await self.emit(InProgressEvent(run=self.run))
|
85
|
+
|
86
|
+
await_resume = None
|
87
|
+
while True:
|
88
|
+
next = await generator.asend(await_resume)
|
89
|
+
if isinstance(next, Message):
|
90
|
+
self.composed_message += next
|
91
|
+
await self.emit(MessageEvent(message=next))
|
92
|
+
elif isinstance(next, Await):
|
93
|
+
self.run.await_ = next
|
94
|
+
self.run.status = RunStatus.AWAITING
|
95
|
+
await self.emit(
|
96
|
+
AwaitEvent.model_validate(
|
97
|
+
{
|
98
|
+
"run_id": self.run.run_id,
|
99
|
+
"type": "await",
|
100
|
+
"await": next,
|
101
|
+
}
|
102
|
+
)
|
103
|
+
)
|
104
|
+
run_logger.info("Run awaited")
|
105
|
+
await_resume = await self.await_()
|
106
|
+
self.run.status = RunStatus.IN_PROGRESS
|
107
|
+
await self.emit(InProgressEvent(run=self.run))
|
108
|
+
run_logger.info("Run resumed")
|
109
|
+
else:
|
110
|
+
try:
|
111
|
+
generic = AnyModel.model_validate(next)
|
112
|
+
await self.emit(GenericEvent(generic=generic))
|
113
|
+
except ValidationError:
|
114
|
+
raise TypeError("Invalid yield")
|
115
|
+
except StopAsyncIteration:
|
116
|
+
self.run.output = self.composed_message
|
117
|
+
self.run.status = RunStatus.COMPLETED
|
118
|
+
await self.emit(CompletedEvent(run=self.run))
|
119
|
+
run_logger.info("Run completed")
|
120
|
+
except asyncio.CancelledError:
|
121
|
+
self.run.status = RunStatus.CANCELLED
|
122
|
+
await self.emit(CancelledEvent(run=self.run))
|
123
|
+
run_logger.info("Run cancelled")
|
124
|
+
except Exception as e:
|
125
|
+
self.run.error = Error(code=ErrorCode.SERVER_ERROR, message=str(e))
|
126
|
+
self.run.status = RunStatus.FAILED
|
127
|
+
await self.emit(FailedEvent(run=self.run))
|
128
|
+
run_logger.exception("Run failed")
|
129
|
+
finally:
|
130
|
+
self.await_or_terminate_event.set()
|
131
|
+
await self.stream_queue.put(None)
|
@@ -0,0 +1,33 @@
|
|
1
|
+
from concurrent.futures import ThreadPoolExecutor
|
2
|
+
|
3
|
+
import janus
|
4
|
+
|
5
|
+
from acp_sdk.models import SessionId
|
6
|
+
from acp_sdk.server.types import RunYield, RunYieldResume
|
7
|
+
|
8
|
+
|
9
|
+
class Context:
|
10
|
+
def __init__(
|
11
|
+
self,
|
12
|
+
*,
|
13
|
+
session_id: SessionId | None = None,
|
14
|
+
executor: ThreadPoolExecutor,
|
15
|
+
yield_queue: janus.Queue[RunYield],
|
16
|
+
yield_resume_queue: janus.Queue[RunYieldResume],
|
17
|
+
) -> None:
|
18
|
+
self.session_id = session_id
|
19
|
+
self.executor = executor
|
20
|
+
self._yield_queue = yield_queue
|
21
|
+
self._yield_resume_queue = yield_resume_queue
|
22
|
+
|
23
|
+
def yield_sync(self, value: RunYield) -> RunYieldResume:
|
24
|
+
self._yield_queue.sync_q.put(value)
|
25
|
+
return self._yield_resume_queue.sync_q.get()
|
26
|
+
|
27
|
+
async def yield_async(self, value: RunYield) -> RunYieldResume:
|
28
|
+
await self._yield_queue.async_q.put(value)
|
29
|
+
return await self._yield_resume_queue.async_q.get()
|
30
|
+
|
31
|
+
def shutdown(self) -> None:
|
32
|
+
self._yield_queue.shutdown()
|
33
|
+
self._yield_resume_queue.shutdown()
|
acp_sdk/server/errors.py
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
from fastapi import Request, status
|
2
|
+
from fastapi.exceptions import RequestValidationError
|
3
|
+
from fastapi.responses import JSONResponse
|
4
|
+
from starlette.exceptions import HTTPException as StarletteHTTPException
|
5
|
+
|
6
|
+
from acp_sdk.models import Error, ErrorCode
|
7
|
+
from acp_sdk.models.errors import ACPError
|
8
|
+
from acp_sdk.server.logging import logger
|
9
|
+
|
10
|
+
|
11
|
+
def error_code_to_status_code(error_code: ErrorCode) -> int:
|
12
|
+
match error_code:
|
13
|
+
case ErrorCode.NOT_FOUND:
|
14
|
+
return status.HTTP_404_NOT_FOUND
|
15
|
+
case ErrorCode.INVALID_INPUT:
|
16
|
+
return status.HTTP_422_UNPROCESSABLE_ENTITY
|
17
|
+
case _:
|
18
|
+
return status.HTTP_500_INTERNAL_SERVER_ERROR
|
19
|
+
|
20
|
+
|
21
|
+
def status_code_to_error_code(status_code: int) -> ErrorCode:
|
22
|
+
match status_code:
|
23
|
+
case status.HTTP_400_BAD_REQUEST:
|
24
|
+
return ErrorCode.INVALID_INPUT
|
25
|
+
case status.HTTP_404_NOT_FOUND:
|
26
|
+
return ErrorCode.NOT_FOUND
|
27
|
+
case status.HTTP_422_UNPROCESSABLE_ENTITY:
|
28
|
+
return ErrorCode.INVALID_INPUT
|
29
|
+
case _:
|
30
|
+
return ErrorCode.SERVER_ERROR
|
31
|
+
|
32
|
+
|
33
|
+
async def acp_error_handler(request: Request, exc: ACPError, *, status_code: int | None = None) -> JSONResponse:
|
34
|
+
error = exc.error
|
35
|
+
return JSONResponse(status_code=status_code or error_code_to_status_code(error.code), content=error.model_dump())
|
36
|
+
|
37
|
+
|
38
|
+
async def http_exception_handler(request: Request, exc: StarletteHTTPException) -> JSONResponse:
|
39
|
+
return await acp_error_handler(
|
40
|
+
request,
|
41
|
+
ACPError(Error(code=status_code_to_error_code(exc.status_code), message=exc.detail)),
|
42
|
+
status_code=exc.status_code,
|
43
|
+
)
|
44
|
+
|
45
|
+
|
46
|
+
async def validation_exception_handler(request: Request, exc: RequestValidationError) -> JSONResponse:
|
47
|
+
return await acp_error_handler(request, ACPError(Error(code=ErrorCode.INVALID_INPUT, message=str(exc))))
|
48
|
+
|
49
|
+
|
50
|
+
async def catch_all_exception_handler(request: Request, exc: Exception) -> JSONResponse:
|
51
|
+
logger.error(exc)
|
52
|
+
return await acp_error_handler(
|
53
|
+
request, ACPError(Error(code=ErrorCode.SERVER_ERROR, message="An unexpected error occurred"))
|
54
|
+
)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
import logging
|
2
|
+
|
3
|
+
from uvicorn.logging import DefaultFormatter
|
4
|
+
|
5
|
+
logger = logging.getLogger("acp")
|
6
|
+
|
7
|
+
|
8
|
+
def configure_logger() -> None:
|
9
|
+
"""Utility that configures the root logger"""
|
10
|
+
root_logger = logging.getLogger()
|
11
|
+
|
12
|
+
handler = logging.StreamHandler()
|
13
|
+
handler.setFormatter(DefaultFormatter(fmt="%(levelprefix)s %(message)s"))
|
14
|
+
|
15
|
+
root_logger.addHandler(handler)
|
16
|
+
root_logger.setLevel(logging.INFO)
|
acp_sdk/server/server.py
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
import inspect
|
2
|
+
from collections.abc import AsyncGenerator, Coroutine, Generator
|
3
|
+
from typing import Any, Callable
|
4
|
+
|
5
|
+
from acp_sdk.models import Message
|
6
|
+
from acp_sdk.server.agent import Agent
|
7
|
+
from acp_sdk.server.app import create_app
|
8
|
+
from acp_sdk.server.context import Context
|
9
|
+
from acp_sdk.server.logging import configure_logger as configure_logger_func
|
10
|
+
from acp_sdk.server.telemetry import configure_telemetry as configure_telemetry_func
|
11
|
+
from acp_sdk.server.types import RunYield, RunYieldResume
|
12
|
+
|
13
|
+
|
14
|
+
class Server:
|
15
|
+
def __init__(self) -> None:
|
16
|
+
self.agents: list[Agent] = []
|
17
|
+
|
18
|
+
def agent(self, name: str | None = None, description: str | None = None) -> Callable:
|
19
|
+
"""Decorator to register an agent."""
|
20
|
+
|
21
|
+
def decorator(fn: Callable) -> Callable:
|
22
|
+
# check agent's function signature
|
23
|
+
signature = inspect.signature(fn)
|
24
|
+
parameters = list(signature.parameters.values())
|
25
|
+
|
26
|
+
# validate agent's function
|
27
|
+
if inspect.isasyncgenfunction(fn):
|
28
|
+
if len(parameters) != 2:
|
29
|
+
raise TypeError(
|
30
|
+
"The agent generator function must have one 'input' argument and one 'context' argument"
|
31
|
+
)
|
32
|
+
else:
|
33
|
+
if len(parameters) != 2:
|
34
|
+
raise TypeError("The agent function must have one 'input' argument and one 'context' argument")
|
35
|
+
|
36
|
+
agent: Agent
|
37
|
+
if inspect.isasyncgenfunction(fn):
|
38
|
+
|
39
|
+
class DecoratedAgent(Agent):
|
40
|
+
@property
|
41
|
+
def name(self) -> str:
|
42
|
+
return name or fn.__name__
|
43
|
+
|
44
|
+
@property
|
45
|
+
def description(self) -> str:
|
46
|
+
return description or fn.__doc__ or ""
|
47
|
+
|
48
|
+
async def run(self, input: Message, context: Context) -> AsyncGenerator[RunYield, RunYieldResume]:
|
49
|
+
try:
|
50
|
+
gen: AsyncGenerator[RunYield, RunYieldResume] = fn(input, context)
|
51
|
+
value = None
|
52
|
+
while True:
|
53
|
+
value = yield await gen.asend(value)
|
54
|
+
except StopAsyncIteration:
|
55
|
+
pass
|
56
|
+
|
57
|
+
agent = DecoratedAgent()
|
58
|
+
elif inspect.iscoroutinefunction(fn):
|
59
|
+
|
60
|
+
class DecoratedAgent(Agent):
|
61
|
+
@property
|
62
|
+
def name(self) -> str:
|
63
|
+
return name or fn.__name__
|
64
|
+
|
65
|
+
@property
|
66
|
+
def description(self) -> str:
|
67
|
+
return description or fn.__doc__ or ""
|
68
|
+
|
69
|
+
async def run(self, input: Message, context: Context) -> Coroutine[RunYield]:
|
70
|
+
return await fn(input, context)
|
71
|
+
|
72
|
+
agent = DecoratedAgent()
|
73
|
+
elif inspect.isgeneratorfunction(fn):
|
74
|
+
|
75
|
+
class DecoratedAgent(Agent):
|
76
|
+
@property
|
77
|
+
def name(self) -> str:
|
78
|
+
return name or fn.__name__
|
79
|
+
|
80
|
+
@property
|
81
|
+
def description(self) -> str:
|
82
|
+
return description or fn.__doc__ or ""
|
83
|
+
|
84
|
+
def run(self, input: Message, context: Context) -> Generator[RunYield, RunYieldResume]:
|
85
|
+
yield from fn(input, context)
|
86
|
+
|
87
|
+
agent = DecoratedAgent()
|
88
|
+
else:
|
89
|
+
|
90
|
+
class DecoratedAgent(Agent):
|
91
|
+
@property
|
92
|
+
def name(self) -> str:
|
93
|
+
return name or fn.__name__
|
94
|
+
|
95
|
+
@property
|
96
|
+
def description(self) -> str:
|
97
|
+
return description or fn.__doc__ or ""
|
98
|
+
|
99
|
+
def run(self, input: Message, context: Context) -> RunYield:
|
100
|
+
return fn(input, context)
|
101
|
+
|
102
|
+
agent = DecoratedAgent()
|
103
|
+
|
104
|
+
self.register(agent)
|
105
|
+
return fn
|
106
|
+
|
107
|
+
return decorator
|
108
|
+
|
109
|
+
def register(self, *agents: Agent) -> None:
|
110
|
+
self.agents.extend(agents)
|
111
|
+
|
112
|
+
def run(self, configure_logger: bool = True, configure_telemetry: bool = False, **kwargs: dict[str, Any]) -> None:
|
113
|
+
import uvicorn
|
114
|
+
|
115
|
+
if configure_logger:
|
116
|
+
configure_logger_func()
|
117
|
+
if configure_telemetry:
|
118
|
+
configure_telemetry_func()
|
119
|
+
|
120
|
+
uvicorn.run(create_app(*self.agents), **kwargs)
|
@@ -0,0 +1,52 @@
|
|
1
|
+
import logging
|
2
|
+
from importlib.metadata import version
|
3
|
+
|
4
|
+
from opentelemetry import metrics, trace
|
5
|
+
from opentelemetry.exporter.otlp.proto.http._log_exporter import OTLPLogExporter
|
6
|
+
from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter
|
7
|
+
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
|
8
|
+
from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
|
9
|
+
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
|
10
|
+
from opentelemetry.sdk.metrics import MeterProvider
|
11
|
+
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
|
12
|
+
from opentelemetry.sdk.resources import (
|
13
|
+
SERVICE_NAME,
|
14
|
+
SERVICE_NAMESPACE,
|
15
|
+
SERVICE_VERSION,
|
16
|
+
Resource,
|
17
|
+
)
|
18
|
+
from opentelemetry.sdk.trace import TracerProvider
|
19
|
+
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
20
|
+
|
21
|
+
root_logger = logging.getLogger()
|
22
|
+
|
23
|
+
|
24
|
+
def configure_telemetry() -> None:
|
25
|
+
"""Utility that configures opentelemetry with OTLP exporter"""
|
26
|
+
|
27
|
+
resource = Resource(
|
28
|
+
attributes={
|
29
|
+
SERVICE_NAME: "acp-server",
|
30
|
+
SERVICE_NAMESPACE: "acp",
|
31
|
+
SERVICE_VERSION: version("acp-sdk"),
|
32
|
+
}
|
33
|
+
)
|
34
|
+
|
35
|
+
# Traces
|
36
|
+
provider = TracerProvider(resource=resource)
|
37
|
+
processor = BatchSpanProcessor(OTLPSpanExporter())
|
38
|
+
provider.add_span_processor(processor)
|
39
|
+
trace.set_tracer_provider(provider)
|
40
|
+
|
41
|
+
# Metrics
|
42
|
+
meter_provider = MeterProvider(
|
43
|
+
resource=resource,
|
44
|
+
metric_readers=[PeriodicExportingMetricReader(OTLPMetricExporter())],
|
45
|
+
)
|
46
|
+
metrics.set_meter_provider(meter_provider)
|
47
|
+
|
48
|
+
# Logs
|
49
|
+
logger_provider = LoggerProvider(resource=resource)
|
50
|
+
processor = BatchLogRecordProcessor(OTLPLogExporter())
|
51
|
+
logger_provider.add_log_record_processor(processor)
|
52
|
+
root_logger.addHandler(LoggingHandler(logger_provider=logger_provider))
|
acp_sdk/server/types.py
ADDED
acp_sdk/server/utils.py
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
from collections.abc import AsyncGenerator
|
2
|
+
|
3
|
+
from pydantic import BaseModel
|
4
|
+
|
5
|
+
from acp_sdk.server.bundle import RunBundle
|
6
|
+
|
7
|
+
|
8
|
+
def encode_sse(model: BaseModel) -> str:
|
9
|
+
return f"data: {model.model_dump_json()}\n\n"
|
10
|
+
|
11
|
+
|
12
|
+
async def stream_sse(bundle: RunBundle) -> AsyncGenerator[str]:
|
13
|
+
async for event in bundle.stream():
|
14
|
+
yield encode_sse(event)
|
@@ -0,0 +1,78 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: acp-sdk
|
3
|
+
Version: 0.1.0rc5
|
4
|
+
Summary: Agent Communication Protocol SDK
|
5
|
+
Requires-Python: <4.0,>=3.11
|
6
|
+
Requires-Dist: opentelemetry-api>=1.31.1
|
7
|
+
Requires-Dist: pydantic>=2.11.1
|
8
|
+
Provides-Extra: client
|
9
|
+
Requires-Dist: httpx-sse>=0.4.0; extra == 'client'
|
10
|
+
Requires-Dist: httpx>=0.28.1; extra == 'client'
|
11
|
+
Requires-Dist: opentelemetry-instrumentation-httpx>=0.52b1; extra == 'client'
|
12
|
+
Provides-Extra: server
|
13
|
+
Requires-Dist: fastapi[standard]>=0.115.8; extra == 'server'
|
14
|
+
Requires-Dist: janus>=2.0.0; extra == 'server'
|
15
|
+
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.31.1; extra == 'server'
|
16
|
+
Requires-Dist: opentelemetry-instrumentation-fastapi>=0.52b1; extra == 'server'
|
17
|
+
Requires-Dist: opentelemetry-sdk>=1.31.1; extra == 'server'
|
18
|
+
Description-Content-Type: text/markdown
|
19
|
+
|
20
|
+
# Agent Communication Protocol SDK for Python
|
21
|
+
|
22
|
+
Agent Communication Protocol SDK for Python provides allows developers to serve and consume agents over the Agent Communication Protocol.
|
23
|
+
|
24
|
+
## Prerequisites
|
25
|
+
|
26
|
+
✅ Python >= 3.11
|
27
|
+
|
28
|
+
## Installation
|
29
|
+
|
30
|
+
Install to use client:
|
31
|
+
|
32
|
+
```shell
|
33
|
+
pip install acp-sdk[client]
|
34
|
+
```
|
35
|
+
|
36
|
+
Install to use server:
|
37
|
+
|
38
|
+
```shell
|
39
|
+
pip install acp-sdk[server]
|
40
|
+
```
|
41
|
+
|
42
|
+
## Overview
|
43
|
+
|
44
|
+
### Client
|
45
|
+
|
46
|
+
The `client` submodule exposes [httpx]() based client with simple methods for communication over ACP.
|
47
|
+
|
48
|
+
```python
|
49
|
+
async with Client(base_url="http://localhost:8000") as client:
|
50
|
+
run = await client.run_sync(agent="echo", input=Message(TextMessagePart(content="Howdy!")))
|
51
|
+
print(run.output)
|
52
|
+
```
|
53
|
+
|
54
|
+
### Server
|
55
|
+
|
56
|
+
The `server` submodule exposes [fastapi] application factory that makes it easy to expose any agent over ACP.
|
57
|
+
|
58
|
+
```python
|
59
|
+
class EchoAgent(Agent):
|
60
|
+
@property
|
61
|
+
def name(self) -> str:
|
62
|
+
return "echo"
|
63
|
+
|
64
|
+
@property
|
65
|
+
def description(self) -> str:
|
66
|
+
return "Echoes everything"
|
67
|
+
|
68
|
+
async def run(self, input: Message, *, context: Context) -> AsyncGenerator[Message | Await, AwaitResume]:
|
69
|
+
for part in input:
|
70
|
+
await asyncio.sleep(0.5)
|
71
|
+
yield {"thought": "I should echo everyting"}
|
72
|
+
yield Message(part)
|
73
|
+
|
74
|
+
|
75
|
+
serve(EchoAgent())
|
76
|
+
```
|
77
|
+
|
78
|
+
➡️ Explore more in our [examples library](/python/examples).
|
@@ -0,0 +1,22 @@
|
|
1
|
+
acp_sdk/__init__.py,sha256=XlcAbmvJfvJK5tmOFdxXP19aDwxxdG_jygsTCvXtjTk,43
|
2
|
+
acp_sdk/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
|
+
acp_sdk/client/__init__.py,sha256=Bca1DORrswxzZsrR2aUFpATuNG2xNSmYvF1Z2WJaVbc,51
|
4
|
+
acp_sdk/client/client.py,sha256=W6iXxH65UUODnvt3tnvgdqT_pP1meZUF2HwJpWCM0BM,5029
|
5
|
+
acp_sdk/models/__init__.py,sha256=numSDBDT1QHx7n_Y3Deb5VOvKWcUBxbOEaMwQBSRHxc,151
|
6
|
+
acp_sdk/models/errors.py,sha256=rEyaMVvQuBi7fwWe_d0PGGySYsD3FZTluQ-SkC0yhAs,444
|
7
|
+
acp_sdk/models/models.py,sha256=ACZPQp-wm43tdlx83hrX7QmdtBRWVXvU5bsotUOidHM,3908
|
8
|
+
acp_sdk/models/schemas.py,sha256=a0T29vfy93uVIce3ikbd3GSTdKBokMmfXWNVRJD3Eb8,662
|
9
|
+
acp_sdk/server/__init__.py,sha256=CowMcwN_WSsnO_-ZoqWQKtNVfa21MW3X3trZ9haJjaA,329
|
10
|
+
acp_sdk/server/agent.py,sha256=SZfJK634d4s8fRh0u1Tc56l58XCRHAh3hq5Hcabh6tI,3442
|
11
|
+
acp_sdk/server/app.py,sha256=7alacNo9y_f0-BjZI0dm_rzSweW-n5YiuY0hrVtEIR8,5420
|
12
|
+
acp_sdk/server/bundle.py,sha256=ZVkIOwWfYgi-VyGYSMKr9WrG_WzYbej-LD-YPUWQdeg,4982
|
13
|
+
acp_sdk/server/context.py,sha256=MgnLV6qcDIhc_0BjW7r4Jj1tHts4ZuwpdTGIBnz2Mgo,1036
|
14
|
+
acp_sdk/server/errors.py,sha256=fWlgVsQ5hs_AXwzc-wvy6QgoDWEMRUBlSrfJfhHHMyE,2085
|
15
|
+
acp_sdk/server/logging.py,sha256=Oc8yZigCsuDnHHPsarRzu0RX3NKaLEgpELM2yovGKDI,411
|
16
|
+
acp_sdk/server/server.py,sha256=xSS_TjxQ5xi_X9RJDNjFsU0JEytLlCCSqbp6sBVXXhY,4442
|
17
|
+
acp_sdk/server/telemetry.py,sha256=EwmtUrWMYid7XHiX76V1J6CigJPa2NrzEPOX0fBoY3o,1838
|
18
|
+
acp_sdk/server/types.py,sha256=2yJPkfUzjVIhHmc0SegGTMqDROe2uFgycb-7CATvYVw,161
|
19
|
+
acp_sdk/server/utils.py,sha256=EfrF9VCyVk3AM_ao-BIB9EzGbfTrh4V2Bz-VFr6f6Sg,351
|
20
|
+
acp_sdk-0.1.0rc5.dist-info/METADATA,sha256=laguwvWACy9Yslwj_Wh2p25rfEciRu217HEGGXTIOKg,2147
|
21
|
+
acp_sdk-0.1.0rc5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
22
|
+
acp_sdk-0.1.0rc5.dist-info/RECORD,,
|
acp/__init__.py
DELETED
@@ -1,138 +0,0 @@
|
|
1
|
-
from .client.session import ClientSession
|
2
|
-
from .client.stdio import StdioServerParameters, stdio_client
|
3
|
-
from .server.session import ServerSession
|
4
|
-
from .server.stdio import stdio_server
|
5
|
-
from .shared.exceptions import McpError
|
6
|
-
from .types import (
|
7
|
-
Agent,
|
8
|
-
AgentTemplate,
|
9
|
-
CallToolRequest,
|
10
|
-
ClientCapabilities,
|
11
|
-
ClientNotification,
|
12
|
-
ClientRequest,
|
13
|
-
ClientResult,
|
14
|
-
CompleteRequest,
|
15
|
-
CreateAgentRequest,
|
16
|
-
CreateAgentResult,
|
17
|
-
CreateMessageRequest,
|
18
|
-
CreateMessageResult,
|
19
|
-
DestroyAgentRequest,
|
20
|
-
DestroyAgentResult,
|
21
|
-
ErrorData,
|
22
|
-
GetPromptRequest,
|
23
|
-
GetPromptResult,
|
24
|
-
Implementation,
|
25
|
-
IncludeContext,
|
26
|
-
InitializedNotification,
|
27
|
-
InitializeRequest,
|
28
|
-
InitializeResult,
|
29
|
-
JSONRPCError,
|
30
|
-
JSONRPCRequest,
|
31
|
-
JSONRPCResponse,
|
32
|
-
ListAgentsRequest,
|
33
|
-
ListAgentsResult,
|
34
|
-
ListAgentTemplatesRequest,
|
35
|
-
ListAgentTemplatesResult,
|
36
|
-
ListPromptsRequest,
|
37
|
-
ListPromptsResult,
|
38
|
-
ListResourcesRequest,
|
39
|
-
ListResourcesResult,
|
40
|
-
ListToolsResult,
|
41
|
-
LoggingLevel,
|
42
|
-
LoggingMessageNotification,
|
43
|
-
Notification,
|
44
|
-
PingRequest,
|
45
|
-
ProgressNotification,
|
46
|
-
PromptsCapability,
|
47
|
-
ReadResourceRequest,
|
48
|
-
ReadResourceResult,
|
49
|
-
Resource,
|
50
|
-
ResourcesCapability,
|
51
|
-
ResourceUpdatedNotification,
|
52
|
-
RootsCapability,
|
53
|
-
RunAgentRequest,
|
54
|
-
RunAgentResult,
|
55
|
-
SamplingMessage,
|
56
|
-
ServerCapabilities,
|
57
|
-
ServerNotification,
|
58
|
-
ServerRequest,
|
59
|
-
ServerResult,
|
60
|
-
SetLevelRequest,
|
61
|
-
StopReason,
|
62
|
-
SubscribeRequest,
|
63
|
-
Tool,
|
64
|
-
ToolsCapability,
|
65
|
-
UnsubscribeRequest,
|
66
|
-
)
|
67
|
-
from .types import (
|
68
|
-
Role as SamplingRole,
|
69
|
-
)
|
70
|
-
|
71
|
-
__all__ = [
|
72
|
-
"CallToolRequest",
|
73
|
-
"ClientCapabilities",
|
74
|
-
"ClientNotification",
|
75
|
-
"ClientRequest",
|
76
|
-
"ClientResult",
|
77
|
-
"ClientSession",
|
78
|
-
"CreateMessageRequest",
|
79
|
-
"CreateMessageResult",
|
80
|
-
"ErrorData",
|
81
|
-
"GetPromptRequest",
|
82
|
-
"GetPromptResult",
|
83
|
-
"Implementation",
|
84
|
-
"IncludeContext",
|
85
|
-
"InitializeRequest",
|
86
|
-
"InitializeResult",
|
87
|
-
"InitializedNotification",
|
88
|
-
"JSONRPCError",
|
89
|
-
"JSONRPCRequest",
|
90
|
-
"ListPromptsRequest",
|
91
|
-
"ListPromptsResult",
|
92
|
-
"ListResourcesRequest",
|
93
|
-
"ListResourcesResult",
|
94
|
-
"ListToolsResult",
|
95
|
-
"LoggingLevel",
|
96
|
-
"LoggingMessageNotification",
|
97
|
-
"McpError",
|
98
|
-
"Notification",
|
99
|
-
"PingRequest",
|
100
|
-
"ProgressNotification",
|
101
|
-
"PromptsCapability",
|
102
|
-
"ReadResourceRequest",
|
103
|
-
"ReadResourceResult",
|
104
|
-
"ResourcesCapability",
|
105
|
-
"ResourceUpdatedNotification",
|
106
|
-
"Resource",
|
107
|
-
"RootsCapability",
|
108
|
-
"SamplingMessage",
|
109
|
-
"SamplingRole",
|
110
|
-
"ServerCapabilities",
|
111
|
-
"ServerNotification",
|
112
|
-
"ServerRequest",
|
113
|
-
"ServerResult",
|
114
|
-
"ServerSession",
|
115
|
-
"SetLevelRequest",
|
116
|
-
"StdioServerParameters",
|
117
|
-
"StopReason",
|
118
|
-
"SubscribeRequest",
|
119
|
-
"Tool",
|
120
|
-
"ToolsCapability",
|
121
|
-
"UnsubscribeRequest",
|
122
|
-
"stdio_client",
|
123
|
-
"stdio_server",
|
124
|
-
"CompleteRequest",
|
125
|
-
"JSONRPCResponse",
|
126
|
-
"AgentTemplate",
|
127
|
-
"ListAgentTemplatesRequest",
|
128
|
-
"ListAgentTemplatesResult",
|
129
|
-
"Agent",
|
130
|
-
"ListAgentsRequest",
|
131
|
-
"ListAgentsResult",
|
132
|
-
"CreateAgentRequest",
|
133
|
-
"CreateAgentResult",
|
134
|
-
"DestroyAgentRequest",
|
135
|
-
"DestroyAgentResult",
|
136
|
-
"RunAgentRequest",
|
137
|
-
"RunAgentResult",
|
138
|
-
]
|