acp-sdk 0.1.0rc7__py3-none-any.whl → 0.1.0rc8__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/client.py +49 -10
- acp_sdk/models/models.py +11 -1
- acp_sdk/models/schemas.py +1 -1
- acp_sdk/server/agent.py +6 -11
- acp_sdk/server/app.py +17 -15
- acp_sdk/server/bundle.py +28 -13
- acp_sdk/server/server.py +24 -2
- acp_sdk/server/session.py +21 -0
- acp_sdk/server/telemetry.py +7 -2
- acp_sdk/version.py +3 -0
- {acp_sdk-0.1.0rc7.dist-info → acp_sdk-0.1.0rc8.dist-info}/METADATA +5 -5
- acp_sdk-0.1.0rc8.dist-info/RECORD +24 -0
- acp_sdk-0.1.0rc7.dist-info/RECORD +0 -22
- {acp_sdk-0.1.0rc7.dist-info → acp_sdk-0.1.0rc8.dist-info}/WHEEL +0 -0
acp_sdk/__init__.py
CHANGED
acp_sdk/client/client.py
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
import uuid
|
2
|
+
from collections.abc import AsyncGenerator, AsyncIterator
|
3
|
+
from contextlib import asynccontextmanager
|
2
4
|
from types import TracebackType
|
3
5
|
from typing import Self
|
4
6
|
|
@@ -14,6 +16,7 @@ from acp_sdk.models import (
|
|
14
16
|
AgentReadResponse,
|
15
17
|
AgentsListResponse,
|
16
18
|
AwaitResume,
|
19
|
+
CreatedEvent,
|
17
20
|
Error,
|
18
21
|
Message,
|
19
22
|
Run,
|
@@ -25,12 +28,20 @@ from acp_sdk.models import (
|
|
25
28
|
RunMode,
|
26
29
|
RunResumeRequest,
|
27
30
|
RunResumeResponse,
|
31
|
+
SessionId,
|
28
32
|
)
|
29
33
|
|
30
34
|
|
31
35
|
class Client:
|
32
|
-
def __init__(
|
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:
|
33
43
|
self.base_url = base_url
|
44
|
+
self.session_id = session_id
|
34
45
|
|
35
46
|
self._client = self._init_client(client)
|
36
47
|
|
@@ -51,6 +62,10 @@ class Client:
|
|
51
62
|
) -> None:
|
52
63
|
await self._client.__aexit__(exc_type, exc_value, traceback)
|
53
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
|
+
|
54
69
|
async def agents(self) -> AsyncIterator[Agent]:
|
55
70
|
response = await self._client.get("/agents")
|
56
71
|
self._raise_error(response)
|
@@ -62,30 +77,51 @@ class Client:
|
|
62
77
|
self._raise_error(response)
|
63
78
|
return AgentReadResponse.model_validate(response.json())
|
64
79
|
|
65
|
-
async def run_sync(self, *, agent: AgentName,
|
80
|
+
async def run_sync(self, *, agent: AgentName, inputs: list[Message]) -> Run:
|
66
81
|
response = await self._client.post(
|
67
82
|
"/runs",
|
68
|
-
|
83
|
+
content=RunCreateRequest(
|
84
|
+
agent_name=agent,
|
85
|
+
inputs=inputs,
|
86
|
+
mode=RunMode.SYNC,
|
87
|
+
session_id=self.session_id,
|
88
|
+
).model_dump_json(),
|
69
89
|
)
|
70
90
|
self._raise_error(response)
|
71
|
-
|
91
|
+
response = RunCreateResponse.model_validate(response.json())
|
92
|
+
self._set_session(response)
|
93
|
+
return response
|
72
94
|
|
73
|
-
async def run_async(self, *, agent: AgentName,
|
95
|
+
async def run_async(self, *, agent: AgentName, inputs: list[Message]) -> Run:
|
74
96
|
response = await self._client.post(
|
75
97
|
"/runs",
|
76
|
-
|
98
|
+
content=RunCreateRequest(
|
99
|
+
agent_name=agent,
|
100
|
+
inputs=inputs,
|
101
|
+
mode=RunMode.ASYNC,
|
102
|
+
session_id=self.session_id,
|
103
|
+
).model_dump_json(),
|
77
104
|
)
|
78
105
|
self._raise_error(response)
|
79
|
-
|
106
|
+
response = RunCreateResponse.model_validate(response.json())
|
107
|
+
self._set_session(response)
|
108
|
+
return response
|
80
109
|
|
81
|
-
async def run_stream(self, *, agent: AgentName,
|
110
|
+
async def run_stream(self, *, agent: AgentName, inputs: list[Message]) -> AsyncIterator[RunEvent]:
|
82
111
|
async with aconnect_sse(
|
83
112
|
self._client,
|
84
113
|
"POST",
|
85
114
|
"/runs",
|
86
|
-
|
115
|
+
content=RunCreateRequest(
|
116
|
+
agent_name=agent,
|
117
|
+
inputs=inputs,
|
118
|
+
mode=RunMode.STREAM,
|
119
|
+
session_id=self.session_id,
|
120
|
+
).model_dump_json(),
|
87
121
|
) as event_source:
|
88
122
|
async for event in self._validate_stream(event_source):
|
123
|
+
if isinstance(event, CreatedEvent):
|
124
|
+
self._set_session(event.run)
|
89
125
|
yield event
|
90
126
|
|
91
127
|
async def run_status(self, *, run_id: RunId) -> Run:
|
@@ -137,3 +173,6 @@ class Client:
|
|
137
173
|
response.raise_for_status()
|
138
174
|
except httpx.HTTPError:
|
139
175
|
raise ACPError(Error.model_validate(response.json()))
|
176
|
+
|
177
|
+
def _set_session(self, run: Run) -> None:
|
178
|
+
self.session_id = run.session_id
|
acp_sdk/models/models.py
CHANGED
@@ -91,13 +91,18 @@ class AwaitResume(BaseModel):
|
|
91
91
|
pass
|
92
92
|
|
93
93
|
|
94
|
+
class Artifact(BaseModel):
|
95
|
+
pass
|
96
|
+
|
97
|
+
|
94
98
|
class Run(BaseModel):
|
95
99
|
run_id: RunId = Field(default_factory=uuid.uuid4)
|
96
100
|
agent_name: AgentName
|
97
101
|
session_id: SessionId | None = None
|
98
102
|
status: RunStatus = RunStatus.CREATED
|
99
103
|
await_: Await | None = Field(None, alias="await")
|
100
|
-
|
104
|
+
outputs: list[Message] = []
|
105
|
+
artifacts: list[Artifact] = []
|
101
106
|
error: Error | None = None
|
102
107
|
|
103
108
|
model_config = ConfigDict(populate_by_name=True)
|
@@ -117,6 +122,11 @@ class MessageEvent(BaseModel):
|
|
117
122
|
message: Message
|
118
123
|
|
119
124
|
|
125
|
+
class ArtifactEvent(BaseModel):
|
126
|
+
type: Literal["artifact"] = "artifact"
|
127
|
+
artifact: Artifact
|
128
|
+
|
129
|
+
|
120
130
|
class AwaitEvent(BaseModel):
|
121
131
|
type: Literal["await"] = "await"
|
122
132
|
await_: Await | None = Field(alias="await")
|
acp_sdk/models/schemas.py
CHANGED
acp_sdk/server/agent.py
CHANGED
@@ -31,19 +31,14 @@ class Agent(abc.ABC):
|
|
31
31
|
|
32
32
|
@abc.abstractmethod
|
33
33
|
def run(
|
34
|
-
self,
|
34
|
+
self, inputs: list[Message], context: Context
|
35
35
|
) -> (
|
36
36
|
AsyncGenerator[RunYield, RunYieldResume] | Generator[RunYield, RunYieldResume] | Coroutine[RunYield] | RunYield
|
37
37
|
):
|
38
38
|
pass
|
39
39
|
|
40
|
-
async def session(self, session_id: SessionId | None) -> SessionId | None:
|
41
|
-
if session_id:
|
42
|
-
raise NotImplementedError()
|
43
|
-
return None
|
44
|
-
|
45
40
|
async def execute(
|
46
|
-
self,
|
41
|
+
self, inputs: list[Message], session_id: SessionId | None, executor: ThreadPoolExecutor
|
47
42
|
) -> AsyncGenerator[RunYield, RunYieldResume]:
|
48
43
|
yield_queue: janus.Queue[RunYield] = janus.Queue()
|
49
44
|
yield_resume_queue: janus.Queue[RunYieldResume] = janus.Queue()
|
@@ -53,13 +48,13 @@ class Agent(abc.ABC):
|
|
53
48
|
)
|
54
49
|
|
55
50
|
if inspect.isasyncgenfunction(self.run):
|
56
|
-
run = asyncio.create_task(self._run_async_gen(
|
51
|
+
run = asyncio.create_task(self._run_async_gen(inputs, context))
|
57
52
|
elif inspect.iscoroutinefunction(self.run):
|
58
|
-
run = asyncio.create_task(self._run_coro(
|
53
|
+
run = asyncio.create_task(self._run_coro(inputs, context))
|
59
54
|
elif inspect.isgeneratorfunction(self.run):
|
60
|
-
run = asyncio.get_running_loop().run_in_executor(executor, self._run_gen,
|
55
|
+
run = asyncio.get_running_loop().run_in_executor(executor, self._run_gen, inputs, context)
|
61
56
|
else:
|
62
|
-
run = asyncio.get_running_loop().run_in_executor(executor, self._run_func,
|
57
|
+
run = asyncio.get_running_loop().run_in_executor(executor, self._run_func, inputs, context)
|
63
58
|
|
64
59
|
try:
|
65
60
|
while True:
|
acp_sdk/server/app.py
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
import asyncio
|
2
1
|
from collections.abc import AsyncGenerator
|
3
2
|
from concurrent.futures import ThreadPoolExecutor
|
4
3
|
from contextlib import asynccontextmanager
|
@@ -25,7 +24,7 @@ from acp_sdk.models import (
|
|
25
24
|
RunReadResponse,
|
26
25
|
RunResumeRequest,
|
27
26
|
RunResumeResponse,
|
28
|
-
|
27
|
+
SessionId,
|
29
28
|
)
|
30
29
|
from acp_sdk.models.errors import ACPError
|
31
30
|
from acp_sdk.server.agent import Agent
|
@@ -38,6 +37,7 @@ from acp_sdk.server.errors import (
|
|
38
37
|
http_exception_handler,
|
39
38
|
validation_exception_handler,
|
40
39
|
)
|
40
|
+
from acp_sdk.server.session import Session
|
41
41
|
from acp_sdk.server.utils import stream_sse
|
42
42
|
|
43
43
|
|
@@ -51,7 +51,7 @@ def create_app(*agents: Agent) -> FastAPI:
|
|
51
51
|
@asynccontextmanager
|
52
52
|
async def lifespan(app: FastAPI) -> AsyncGenerator[None]:
|
53
53
|
nonlocal executor
|
54
|
-
with ThreadPoolExecutor(
|
54
|
+
with ThreadPoolExecutor() as exec:
|
55
55
|
executor = exec
|
56
56
|
yield
|
57
57
|
|
@@ -61,6 +61,7 @@ def create_app(*agents: Agent) -> FastAPI:
|
|
61
61
|
|
62
62
|
agents: dict[AgentName, Agent] = {agent.name: agent for agent in agents}
|
63
63
|
runs: dict[RunId, RunBundle] = {}
|
64
|
+
sessions: dict[SessionId, Session] = {}
|
64
65
|
|
65
66
|
app.exception_handler(ACPError)(acp_error_handler)
|
66
67
|
app.exception_handler(StarletteHTTPException)(http_exception_handler)
|
@@ -96,17 +97,20 @@ def create_app(*agents: Agent) -> FastAPI:
|
|
96
97
|
@app.post("/runs")
|
97
98
|
async def create_run(request: RunCreateRequest) -> RunCreateResponse:
|
98
99
|
agent = find_agent(request.agent_name)
|
100
|
+
|
101
|
+
session = sessions.get(request.session_id, Session()) if request.session_id else Session()
|
102
|
+
nonlocal executor
|
99
103
|
bundle = RunBundle(
|
100
104
|
agent=agent,
|
101
|
-
run=Run(
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
+
run=Run(agent_name=agent.name, session_id=session.id),
|
106
|
+
inputs=request.inputs,
|
107
|
+
history=list(session.history()),
|
108
|
+
executor=executor,
|
105
109
|
)
|
110
|
+
session.append(bundle)
|
106
111
|
|
107
|
-
nonlocal executor
|
108
|
-
bundle.task = asyncio.create_task(bundle.execute(request.input, executor=executor))
|
109
112
|
runs[bundle.run.run_id] = bundle
|
113
|
+
sessions[session.id] = session
|
110
114
|
|
111
115
|
headers = {Headers.RUN_ID: str(bundle.run.run_id)}
|
112
116
|
|
@@ -140,8 +144,7 @@ def create_app(*agents: Agent) -> FastAPI:
|
|
140
144
|
@app.post("/runs/{run_id}")
|
141
145
|
async def resume_run(run_id: RunId, request: RunResumeRequest) -> RunResumeResponse:
|
142
146
|
bundle = find_run_bundle(run_id)
|
143
|
-
bundle.
|
144
|
-
await bundle.await_queue.put(request.await_)
|
147
|
+
await bundle.resume(request.await_)
|
145
148
|
match request.mode:
|
146
149
|
case RunMode.STREAM:
|
147
150
|
return StreamingResponse(
|
@@ -164,11 +167,10 @@ def create_app(*agents: Agent) -> FastAPI:
|
|
164
167
|
bundle = find_run_bundle(run_id)
|
165
168
|
if bundle.run.status.is_terminal:
|
166
169
|
raise HTTPException(
|
167
|
-
status_code=
|
168
|
-
detail=f"Run
|
170
|
+
status_code=status.HTTP_403_FORBIDDEN,
|
171
|
+
detail=f"Run in terminal status {bundle.run.status} can't be cancelled",
|
169
172
|
)
|
170
|
-
bundle.
|
171
|
-
bundle.run.status = RunStatus.CANCELLING
|
173
|
+
await bundle.cancel()
|
172
174
|
return JSONResponse(status_code=status.HTTP_202_ACCEPTED, content=jsonable_encoder(bundle.run))
|
173
175
|
|
174
176
|
return app
|
acp_sdk/server/bundle.py
CHANGED
@@ -3,11 +3,12 @@ import logging
|
|
3
3
|
from collections.abc import AsyncGenerator
|
4
4
|
from concurrent.futures import ThreadPoolExecutor
|
5
5
|
|
6
|
-
from opentelemetry import trace
|
7
6
|
from pydantic import ValidationError
|
8
7
|
|
9
8
|
from acp_sdk.models import (
|
10
9
|
AnyModel,
|
10
|
+
Artifact,
|
11
|
+
ArtifactEvent,
|
11
12
|
Await,
|
12
13
|
AwaitEvent,
|
13
14
|
AwaitResume,
|
@@ -27,20 +28,25 @@ from acp_sdk.models import (
|
|
27
28
|
from acp_sdk.models.errors import ErrorCode
|
28
29
|
from acp_sdk.server.agent import Agent
|
29
30
|
from acp_sdk.server.logging import logger
|
31
|
+
from acp_sdk.server.telemetry import get_tracer
|
30
32
|
|
31
33
|
|
32
34
|
class RunBundle:
|
33
|
-
def __init__(
|
35
|
+
def __init__(
|
36
|
+
self, *, agent: Agent, run: Run, inputs: list[Message], history: list[Message], executor: ThreadPoolExecutor
|
37
|
+
) -> None:
|
34
38
|
self.agent = agent
|
35
39
|
self.run = run
|
36
|
-
self.
|
40
|
+
self.inputs = inputs
|
41
|
+
self.history = history
|
37
42
|
|
38
43
|
self.stream_queue: asyncio.Queue[RunEvent] = asyncio.Queue()
|
39
|
-
self.composed_message = Message()
|
40
44
|
|
41
45
|
self.await_queue: asyncio.Queue[AwaitResume] = asyncio.Queue(maxsize=1)
|
42
46
|
self.await_or_terminate_event = asyncio.Event()
|
43
47
|
|
48
|
+
self.task = asyncio.create_task(self._execute(inputs, executor=executor))
|
49
|
+
|
44
50
|
async def stream(self) -> AsyncGenerator[RunEvent]:
|
45
51
|
while True:
|
46
52
|
event = await self.stream_queue.get()
|
@@ -64,20 +70,27 @@ class RunBundle:
|
|
64
70
|
async def resume(self, resume: AwaitResume) -> None:
|
65
71
|
self.stream_queue = asyncio.Queue()
|
66
72
|
await self.await_queue.put(resume)
|
73
|
+
self.run.status = RunStatus.IN_PROGRESS
|
74
|
+
self.run.await_ = None
|
75
|
+
|
76
|
+
async def cancel(self) -> None:
|
77
|
+
self.task.cancel()
|
78
|
+
self.run.status = RunStatus.CANCELLING
|
79
|
+
self.run.await_ = None
|
67
80
|
|
68
81
|
async def join(self) -> None:
|
69
82
|
await self.await_or_terminate_event.wait()
|
70
83
|
|
71
|
-
async def
|
72
|
-
with
|
84
|
+
async def _execute(self, inputs: list[Message], *, executor: ThreadPoolExecutor) -> None:
|
85
|
+
with get_tracer().start_as_current_span("run"):
|
73
86
|
run_logger = logging.LoggerAdapter(logger, {"run_id": str(self.run.run_id)})
|
74
87
|
|
75
|
-
await self.emit(CreatedEvent(run=self.run))
|
76
88
|
try:
|
77
|
-
|
78
|
-
run_logger.info("Session loaded")
|
89
|
+
await self.emit(CreatedEvent(run=self.run))
|
79
90
|
|
80
|
-
generator = self.agent.execute(
|
91
|
+
generator = self.agent.execute(
|
92
|
+
inputs=self.history + inputs, session_id=self.run.session_id, executor=executor
|
93
|
+
)
|
81
94
|
run_logger.info("Run started")
|
82
95
|
|
83
96
|
self.run.status = RunStatus.IN_PROGRESS
|
@@ -87,8 +100,11 @@ class RunBundle:
|
|
87
100
|
while True:
|
88
101
|
next = await generator.asend(await_resume)
|
89
102
|
if isinstance(next, Message):
|
90
|
-
self.
|
103
|
+
self.run.outputs.append(next)
|
91
104
|
await self.emit(MessageEvent(message=next))
|
105
|
+
elif isinstance(next, Artifact):
|
106
|
+
self.run.artifacts.append(next)
|
107
|
+
await self.emit(ArtifactEvent(artifact=next))
|
92
108
|
elif isinstance(next, Await):
|
93
109
|
self.run.await_ = next
|
94
110
|
self.run.status = RunStatus.AWAITING
|
@@ -103,7 +119,6 @@ class RunBundle:
|
|
103
119
|
)
|
104
120
|
run_logger.info("Run awaited")
|
105
121
|
await_resume = await self.await_()
|
106
|
-
self.run.status = RunStatus.IN_PROGRESS
|
107
122
|
await self.emit(InProgressEvent(run=self.run))
|
108
123
|
run_logger.info("Run resumed")
|
109
124
|
else:
|
@@ -113,7 +128,6 @@ class RunBundle:
|
|
113
128
|
except ValidationError:
|
114
129
|
raise TypeError("Invalid yield")
|
115
130
|
except StopAsyncIteration:
|
116
|
-
self.run.output = self.composed_message
|
117
131
|
self.run.status = RunStatus.COMPLETED
|
118
132
|
await self.emit(CompletedEvent(run=self.run))
|
119
133
|
run_logger.info("Run completed")
|
@@ -126,6 +140,7 @@ class RunBundle:
|
|
126
140
|
self.run.status = RunStatus.FAILED
|
127
141
|
await self.emit(FailedEvent(run=self.run))
|
128
142
|
run_logger.exception("Run failed")
|
143
|
+
raise
|
129
144
|
finally:
|
130
145
|
self.await_or_terminate_event.set()
|
131
146
|
await self.stream_queue.put(None)
|
acp_sdk/server/server.py
CHANGED
@@ -7,7 +7,7 @@ from typing import Any, Callable
|
|
7
7
|
import uvicorn
|
8
8
|
import uvicorn.config
|
9
9
|
|
10
|
-
from acp_sdk.models import Message
|
10
|
+
from acp_sdk.models import Message, Metadata
|
11
11
|
from acp_sdk.server.agent import Agent
|
12
12
|
from acp_sdk.server.app import create_app
|
13
13
|
from acp_sdk.server.context import Context
|
@@ -21,7 +21,13 @@ class Server:
|
|
21
21
|
self._agents: list[Agent] = []
|
22
22
|
self._server: uvicorn.Server | None = None
|
23
23
|
|
24
|
-
def agent(
|
24
|
+
def agent(
|
25
|
+
self,
|
26
|
+
name: str | None = None,
|
27
|
+
description: str | None = None,
|
28
|
+
*,
|
29
|
+
metadata: Metadata | None = None,
|
30
|
+
) -> Callable:
|
25
31
|
"""Decorator to register an agent."""
|
26
32
|
|
27
33
|
def decorator(fn: Callable) -> Callable:
|
@@ -49,6 +55,10 @@ class Server:
|
|
49
55
|
def description(self) -> str:
|
50
56
|
return description or fn.__doc__ or ""
|
51
57
|
|
58
|
+
@property
|
59
|
+
def metadata(self) -> Metadata:
|
60
|
+
return metadata or Metadata()
|
61
|
+
|
52
62
|
async def run(self, input: Message, context: Context) -> AsyncGenerator[RunYield, RunYieldResume]:
|
53
63
|
try:
|
54
64
|
gen: AsyncGenerator[RunYield, RunYieldResume] = (
|
@@ -72,6 +82,10 @@ class Server:
|
|
72
82
|
def description(self) -> str:
|
73
83
|
return description or fn.__doc__ or ""
|
74
84
|
|
85
|
+
@property
|
86
|
+
def metadata(self) -> Metadata:
|
87
|
+
return metadata or Metadata()
|
88
|
+
|
75
89
|
async def run(self, input: Message, context: Context) -> Coroutine[RunYield]:
|
76
90
|
return await (fn(input, context) if has_context_param else fn(input))
|
77
91
|
|
@@ -87,6 +101,10 @@ class Server:
|
|
87
101
|
def description(self) -> str:
|
88
102
|
return description or fn.__doc__ or ""
|
89
103
|
|
104
|
+
@property
|
105
|
+
def metadata(self) -> Metadata:
|
106
|
+
return metadata or Metadata()
|
107
|
+
|
90
108
|
def run(self, input: Message, context: Context) -> Generator[RunYield, RunYieldResume]:
|
91
109
|
yield from (fn(input, context) if has_context_param else fn(input))
|
92
110
|
|
@@ -102,6 +120,10 @@ class Server:
|
|
102
120
|
def description(self) -> str:
|
103
121
|
return description or fn.__doc__ or ""
|
104
122
|
|
123
|
+
@property
|
124
|
+
def metadata(self) -> Metadata:
|
125
|
+
return metadata or Metadata()
|
126
|
+
|
105
127
|
def run(self, input: Message, context: Context) -> RunYield:
|
106
128
|
return fn(input, context) if has_context_param else fn(input)
|
107
129
|
|
@@ -0,0 +1,21 @@
|
|
1
|
+
import uuid
|
2
|
+
from collections.abc import Iterator
|
3
|
+
|
4
|
+
from acp_sdk.models import Message, SessionId
|
5
|
+
from acp_sdk.models.models import RunStatus
|
6
|
+
from acp_sdk.server.bundle import RunBundle
|
7
|
+
|
8
|
+
|
9
|
+
class Session:
|
10
|
+
def __init__(self) -> None:
|
11
|
+
self.id: SessionId = uuid.uuid4()
|
12
|
+
self.bundles: list[RunBundle] = []
|
13
|
+
|
14
|
+
def append(self, bundle: RunBundle) -> None:
|
15
|
+
self.bundles.append(bundle)
|
16
|
+
|
17
|
+
def history(self) -> Iterator[Message]:
|
18
|
+
for bundle in self.bundles:
|
19
|
+
if bundle.run.status == RunStatus.COMPLETED:
|
20
|
+
yield from bundle.inputs
|
21
|
+
yield from bundle.run.outputs
|
acp_sdk/server/telemetry.py
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
import logging
|
2
|
-
from importlib.metadata import version
|
3
2
|
|
4
3
|
from opentelemetry import metrics, trace
|
5
4
|
from opentelemetry.exporter.otlp.proto.http._log_exporter import OTLPLogExporter
|
@@ -18,6 +17,8 @@ from opentelemetry.sdk.resources import (
|
|
18
17
|
from opentelemetry.sdk.trace import TracerProvider
|
19
18
|
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
20
19
|
|
20
|
+
from acp_sdk.version import __version__
|
21
|
+
|
21
22
|
root_logger = logging.getLogger()
|
22
23
|
|
23
24
|
|
@@ -28,7 +29,7 @@ def configure_telemetry() -> None:
|
|
28
29
|
attributes={
|
29
30
|
SERVICE_NAME: "acp-server",
|
30
31
|
SERVICE_NAMESPACE: "acp",
|
31
|
-
SERVICE_VERSION:
|
32
|
+
SERVICE_VERSION: __version__,
|
32
33
|
}
|
33
34
|
)
|
34
35
|
|
@@ -50,3 +51,7 @@ def configure_telemetry() -> None:
|
|
50
51
|
processor = BatchLogRecordProcessor(OTLPLogExporter())
|
51
52
|
logger_provider.add_log_record_processor(processor)
|
52
53
|
root_logger.addHandler(LoggingHandler(logger_provider=logger_provider))
|
54
|
+
|
55
|
+
|
56
|
+
def get_tracer() -> trace.Tracer:
|
57
|
+
return trace.get_tracer("acp-sdk", __version__)
|
acp_sdk/version.py
ADDED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: acp-sdk
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.0rc8
|
4
4
|
Summary: Agent Communication Protocol SDK
|
5
5
|
Requires-Python: <4.0,>=3.11
|
6
6
|
Requires-Dist: opentelemetry-api>=1.31.1
|
@@ -47,7 +47,7 @@ The `client` submodule exposes [httpx]() based client with simple methods for co
|
|
47
47
|
|
48
48
|
```python
|
49
49
|
async with Client(base_url="http://localhost:8000") as client:
|
50
|
-
run = await client.run_sync(agent="echo",
|
50
|
+
run = await client.run_sync(agent="echo", inputs=[Message(TextMessagePart(content="Howdy!"))])
|
51
51
|
print(run.output)
|
52
52
|
```
|
53
53
|
|
@@ -59,13 +59,13 @@ The `server` submodule exposes [fastapi] application factory that makes it easy
|
|
59
59
|
server = Server()
|
60
60
|
|
61
61
|
@server.agent()
|
62
|
-
async def echo(
|
62
|
+
async def echo(inputs: list[Message], context: Context) -> AsyncGenerator[RunYield, RunYieldResume]:
|
63
63
|
"""Echoes everything"""
|
64
|
-
for
|
64
|
+
for message in inputs:
|
65
65
|
await asyncio.sleep(0.5)
|
66
66
|
yield {"thought": "I should echo everyting"}
|
67
67
|
await asyncio.sleep(0.5)
|
68
|
-
yield
|
68
|
+
yield message
|
69
69
|
|
70
70
|
|
71
71
|
server.run()
|
@@ -0,0 +1,24 @@
|
|
1
|
+
acp_sdk/__init__.py,sha256=tXdAUM9zcmdSKCAkVrOCrGcXcuVS-yuvQUoQwTe9pek,98
|
2
|
+
acp_sdk/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
|
+
acp_sdk/version.py,sha256=Niy83rgvigB4hL_rR-O4ySvI7dj6xnqkyOe_JTymi9s,73
|
4
|
+
acp_sdk/client/__init__.py,sha256=Bca1DORrswxzZsrR2aUFpATuNG2xNSmYvF1Z2WJaVbc,51
|
5
|
+
acp_sdk/client/client.py,sha256=phsFdVMdVmTcfmzUQUgqzzvq4zNvmEUgW2eL9cFJY2g,6140
|
6
|
+
acp_sdk/models/__init__.py,sha256=numSDBDT1QHx7n_Y3Deb5VOvKWcUBxbOEaMwQBSRHxc,151
|
7
|
+
acp_sdk/models/errors.py,sha256=rEyaMVvQuBi7fwWe_d0PGGySYsD3FZTluQ-SkC0yhAs,444
|
8
|
+
acp_sdk/models/models.py,sha256=LrOZIWwn_6SenSwzz2JeK7Q555vzqjBKFElCifxQgnk,4107
|
9
|
+
acp_sdk/models/schemas.py,sha256=LNs3oN1pQ4GD0ceV3-k6X6R39eu33nsAfnW6uEgOP0c,735
|
10
|
+
acp_sdk/server/__init__.py,sha256=CowMcwN_WSsnO_-ZoqWQKtNVfa21MW3X3trZ9haJjaA,329
|
11
|
+
acp_sdk/server/agent.py,sha256=wMwyB3Ouz361DX-RDPFD61nymoUOTvBIJt3_JU9KmOw,3297
|
12
|
+
acp_sdk/server/app.py,sha256=Ys5EN4MzmrwrpBGvycEP5dKEIYkDZmeBMMV1Aq58AU0,5897
|
13
|
+
acp_sdk/server/bundle.py,sha256=VUOqzBhwsADkoyPWqXSZ2KG59S3q4LMDfpFVAssnGSM,5386
|
14
|
+
acp_sdk/server/context.py,sha256=MgnLV6qcDIhc_0BjW7r4Jj1tHts4ZuwpdTGIBnz2Mgo,1036
|
15
|
+
acp_sdk/server/errors.py,sha256=fWlgVsQ5hs_AXwzc-wvy6QgoDWEMRUBlSrfJfhHHMyE,2085
|
16
|
+
acp_sdk/server/logging.py,sha256=Oc8yZigCsuDnHHPsarRzu0RX3NKaLEgpELM2yovGKDI,411
|
17
|
+
acp_sdk/server/server.py,sha256=6iCJZrMfIDtqEmxqFREpPbyAiuxVxbQD73QHiFJ6KpA,9307
|
18
|
+
acp_sdk/server/session.py,sha256=0cDr924HC5x2bBNbK9NSKVHAt5A_mi5dK8P4jP_ugq0,629
|
19
|
+
acp_sdk/server/telemetry.py,sha256=WIEHK8syOTG9SyWi3Y-cos7CsCF5-IHGiyL9bCaUN0E,1921
|
20
|
+
acp_sdk/server/types.py,sha256=2yJPkfUzjVIhHmc0SegGTMqDROe2uFgycb-7CATvYVw,161
|
21
|
+
acp_sdk/server/utils.py,sha256=EfrF9VCyVk3AM_ao-BIB9EzGbfTrh4V2Bz-VFr6f6Sg,351
|
22
|
+
acp_sdk-0.1.0rc8.dist-info/METADATA,sha256=hrIiPuTocgUWerNeGvolLPMY1DgqFqpTshMswZr7sQw,2041
|
23
|
+
acp_sdk-0.1.0rc8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
24
|
+
acp_sdk-0.1.0rc8.dist-info/RECORD,,
|
@@ -1,22 +0,0 @@
|
|
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=JhjI_M9GO-1lMrpZJo0IChDVrzUfnDyH5R5hFcqkXEE,3936
|
8
|
-
acp_sdk/models/schemas.py,sha256=TM_0aRsBj_9G__zzWPYDx4wAXsGs2RUH57IOTi4--Ek,728
|
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=rf8I2qcaaXoM-36Za8vs3Re7gygcTSMs4MXbMUBJENU,5803
|
12
|
-
acp_sdk/server/bundle.py,sha256=LZzinifpbKxEBivoxOSdxUmzEcu3XNAkCiIJc2Ivveg,4987
|
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=B1XGptw2kTgaB52klI4aNqDAlmyT2rNpMtKDSCxyVc4,8665
|
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.0rc7.dist-info/METADATA,sha256=IoDvdN2USySSWbm7I05iIuuRz-7vCzaBtsnilmgW5IQ,2033
|
21
|
-
acp_sdk-0.1.0rc7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
22
|
-
acp_sdk-0.1.0rc7.dist-info/RECORD,,
|
File without changes
|