agent-framework-hosting 1.0.0a260709__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.
- agent_framework_hosting/__init__.py +29 -0
- agent_framework_hosting/_state.py +338 -0
- agent_framework_hosting-1.0.0a260709.dist-info/METADATA +118 -0
- agent_framework_hosting-1.0.0a260709.dist-info/RECORD +6 -0
- agent_framework_hosting-1.0.0a260709.dist-info/WHEEL +4 -0
- agent_framework_hosting-1.0.0a260709.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Copyright (c) Microsoft. All rights reserved.
|
|
2
|
+
|
|
3
|
+
"""Execution-state helpers for app-owned Agent Framework hosting routes."""
|
|
4
|
+
|
|
5
|
+
import importlib.metadata
|
|
6
|
+
|
|
7
|
+
from ._state import (
|
|
8
|
+
AgentRunArgs,
|
|
9
|
+
AgentState,
|
|
10
|
+
SessionStore,
|
|
11
|
+
SupportsBuild,
|
|
12
|
+
WorkflowRunArgs,
|
|
13
|
+
WorkflowState,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
try:
|
|
17
|
+
__version__ = importlib.metadata.version(__name__)
|
|
18
|
+
except importlib.metadata.PackageNotFoundError:
|
|
19
|
+
__version__ = "0.0.0"
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"AgentRunArgs",
|
|
23
|
+
"AgentState",
|
|
24
|
+
"SessionStore",
|
|
25
|
+
"SupportsBuild",
|
|
26
|
+
"WorkflowRunArgs",
|
|
27
|
+
"WorkflowState",
|
|
28
|
+
"__version__",
|
|
29
|
+
]
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
# Copyright (c) Microsoft. All rights reserved.
|
|
2
|
+
|
|
3
|
+
"""Shared execution state for app-owned hosting routes.
|
|
4
|
+
|
|
5
|
+
Two independent state holders, one per target kind, since agents and
|
|
6
|
+
workflows keep different continuation state:
|
|
7
|
+
|
|
8
|
+
- ``AgentState`` pairs an agent target with a ``SessionStore``
|
|
9
|
+
(``session_id -> AgentSession``).
|
|
10
|
+
- ``WorkflowState`` resolves a workflow target. Workflow checkpointing uses
|
|
11
|
+
the existing ``CheckpointStorage`` abstraction directly; apps can keep their
|
|
12
|
+
own ``session_id -> checkpoint_id`` cursor when they need per-session resume.
|
|
13
|
+
|
|
14
|
+
``SessionStore`` is plain storage: it only gets/sets/deletes what it is given.
|
|
15
|
+
It does not know how to create a new value for a ``session_id`` it hasn't seen
|
|
16
|
+
before -- that is ``AgentState``'s job (see ``AgentState.get_or_create_session``),
|
|
17
|
+
since only the state object has both the store and the resolved target.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import asyncio
|
|
23
|
+
import inspect
|
|
24
|
+
from collections.abc import Awaitable, Callable, Mapping
|
|
25
|
+
from typing import Any, Generic, Protocol, TypedDict, TypeVar, cast, runtime_checkable
|
|
26
|
+
|
|
27
|
+
from agent_framework import (
|
|
28
|
+
AgentRunInputs,
|
|
29
|
+
AgentSession,
|
|
30
|
+
ChatOptions,
|
|
31
|
+
SupportsAgentRun,
|
|
32
|
+
Workflow,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class SessionStore:
|
|
37
|
+
"""Plain in-memory ``session_id -> AgentSession`` lookup.
|
|
38
|
+
|
|
39
|
+
This store only stores and retrieves; it does not create sessions. Use
|
|
40
|
+
:meth:`AgentState.get_or_create_session` for that -- it resolves the
|
|
41
|
+
agent target and calls ``target.create_session(...)`` the first time a
|
|
42
|
+
given ``session_id`` is seen, then stores the result here.
|
|
43
|
+
|
|
44
|
+
No eviction: every id ever stored stays resolvable for the life of the
|
|
45
|
+
process. That is intentional -- protocols such as OpenAI Responses'
|
|
46
|
+
``previous_response_id`` are designed to let a caller continue from *any*
|
|
47
|
+
earlier point in a conversation, not just the latest turn, so every id
|
|
48
|
+
that has been handed out needs to stay independently resolvable. If you
|
|
49
|
+
back a ``SessionStore``-shaped store with real storage (Redis, a
|
|
50
|
+
database, ...), you are responsible for that store's own TTL/eviction
|
|
51
|
+
policy; this in-memory reference implementation does not model that
|
|
52
|
+
concern.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
def __init__(self) -> None:
|
|
56
|
+
"""Create an empty session store."""
|
|
57
|
+
self._sessions: dict[str, AgentSession] = {}
|
|
58
|
+
|
|
59
|
+
async def get(self, session_id: str) -> AgentSession | None:
|
|
60
|
+
"""Return the stored session for ``session_id``, or ``None`` if absent.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
session_id: Opaque app-selected session id.
|
|
64
|
+
|
|
65
|
+
Raises:
|
|
66
|
+
ValueError: If ``session_id`` is empty.
|
|
67
|
+
"""
|
|
68
|
+
if not session_id:
|
|
69
|
+
raise ValueError("session_id must be a non-empty string")
|
|
70
|
+
return self._sessions.get(session_id)
|
|
71
|
+
|
|
72
|
+
async def set(self, session_id: str, session: AgentSession) -> None:
|
|
73
|
+
"""Store ``session`` under ``session_id``, replacing any existing entry.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
session_id: Opaque app-selected session id.
|
|
77
|
+
session: The session to store.
|
|
78
|
+
|
|
79
|
+
Raises:
|
|
80
|
+
ValueError: If ``session_id`` is empty.
|
|
81
|
+
"""
|
|
82
|
+
if not session_id:
|
|
83
|
+
raise ValueError("session_id must be a non-empty string")
|
|
84
|
+
self._sessions[session_id] = session
|
|
85
|
+
|
|
86
|
+
async def delete(self, session_id: str) -> None:
|
|
87
|
+
"""Forget the stored session for ``session_id``, if any.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
session_id: Opaque app-selected session id.
|
|
91
|
+
|
|
92
|
+
Raises:
|
|
93
|
+
ValueError: If ``session_id`` is empty.
|
|
94
|
+
"""
|
|
95
|
+
if not session_id:
|
|
96
|
+
raise ValueError("session_id must be a non-empty string")
|
|
97
|
+
self._sessions.pop(session_id, None)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
AgentT = TypeVar("AgentT", bound=SupportsAgentRun)
|
|
101
|
+
WorkflowT = TypeVar("WorkflowT", bound=Workflow)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@runtime_checkable
|
|
105
|
+
class SupportsBuild(Protocol):
|
|
106
|
+
"""A builder that produces a ``Workflow`` via a zero-argument ``build()``.
|
|
107
|
+
|
|
108
|
+
Matches ``agent_framework.WorkflowBuilder`` and the orchestration
|
|
109
|
+
builders in ``agent_framework_orchestrations`` (``ConcurrentBuilder``,
|
|
110
|
+
``GroupChatBuilder``, ``HandoffBuilder``, ``MagenticBuilder``,
|
|
111
|
+
``SequentialBuilder``) structurally, without ``agent-framework-hosting``
|
|
112
|
+
depending on either package.
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
def build(self) -> Workflow: ...
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class AgentRunArgs(TypedDict):
|
|
119
|
+
"""Arguments prepared for ``Agent.run``."""
|
|
120
|
+
|
|
121
|
+
messages: AgentRunInputs
|
|
122
|
+
options: ChatOptions[Any]
|
|
123
|
+
stream: bool
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class WorkflowRunArgs(TypedDict):
|
|
127
|
+
"""Arguments prepared for ``Workflow.run``."""
|
|
128
|
+
|
|
129
|
+
message: Any | None
|
|
130
|
+
responses: Mapping[str, Any] | None
|
|
131
|
+
stream: bool
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class AgentState(Generic[AgentT]):
|
|
135
|
+
"""Shared execution state for app-owned agent hosting routes.
|
|
136
|
+
|
|
137
|
+
Holds the Agent Framework agent target and a :class:`SessionStore` that
|
|
138
|
+
route code may share. Does not own routes, middleware, protocol
|
|
139
|
+
dispatch, or native SDK calls -- web frameworks keep those concerns.
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
def __init__(
|
|
143
|
+
self,
|
|
144
|
+
target: AgentT | Awaitable[AgentT] | Callable[[], AgentT | Awaitable[AgentT]],
|
|
145
|
+
*,
|
|
146
|
+
session_store: SessionStore | None = None,
|
|
147
|
+
cache_target: bool = True,
|
|
148
|
+
) -> None:
|
|
149
|
+
"""Create shared state for ``target``.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
target: Agent target used by route code. May be a target
|
|
153
|
+
instance, a synchronous factory, an asynchronous factory, or
|
|
154
|
+
an awaitable target.
|
|
155
|
+
|
|
156
|
+
Keyword Args:
|
|
157
|
+
session_store: Existing store to use. Defaults to a fresh
|
|
158
|
+
in-memory :class:`SessionStore`.
|
|
159
|
+
cache_target: Whether to cache a resolved callable/awaitable
|
|
160
|
+
target. Defaults to ``True`` so expensive target setup
|
|
161
|
+
happens once.
|
|
162
|
+
|
|
163
|
+
Raises:
|
|
164
|
+
ValueError: If ``cache_target=False`` is used with a one-shot
|
|
165
|
+
awaitable target.
|
|
166
|
+
"""
|
|
167
|
+
if not cache_target and inspect.isawaitable(target):
|
|
168
|
+
raise ValueError("cache_target=False requires a target instance or callable target factory")
|
|
169
|
+
self._target_source = target
|
|
170
|
+
self._cache_target = cache_target
|
|
171
|
+
self._cached_target: AgentT | None = None
|
|
172
|
+
self._target_lock = asyncio.Lock()
|
|
173
|
+
if not callable(target) and not inspect.isawaitable(target):
|
|
174
|
+
self._cached_target = target
|
|
175
|
+
self._session_store: SessionStore = session_store if session_store is not None else SessionStore()
|
|
176
|
+
self._session_locks: dict[str, asyncio.Lock] = {}
|
|
177
|
+
|
|
178
|
+
async def get_target(self) -> AgentT:
|
|
179
|
+
"""Return the resolved target.
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
The target instance. Callable and awaitable targets are resolved
|
|
183
|
+
first and cached by default.
|
|
184
|
+
"""
|
|
185
|
+
if self._cache_target and self._cached_target is not None:
|
|
186
|
+
return self._cached_target
|
|
187
|
+
|
|
188
|
+
if self._cache_target:
|
|
189
|
+
async with self._target_lock:
|
|
190
|
+
if self._cached_target is not None:
|
|
191
|
+
return self._cached_target
|
|
192
|
+
target = self._target_source() if callable(self._target_source) else self._target_source
|
|
193
|
+
if inspect.isawaitable(target):
|
|
194
|
+
target = await target
|
|
195
|
+
self._cached_target = target
|
|
196
|
+
return target
|
|
197
|
+
|
|
198
|
+
target = self._target_source() if callable(self._target_source) else self._target_source
|
|
199
|
+
if inspect.isawaitable(target):
|
|
200
|
+
target = await target
|
|
201
|
+
return target
|
|
202
|
+
|
|
203
|
+
@property
|
|
204
|
+
def target(self) -> AgentT:
|
|
205
|
+
"""Return a synchronously available target.
|
|
206
|
+
|
|
207
|
+
Raises:
|
|
208
|
+
RuntimeError: If the target is a callable or awaitable that has not
|
|
209
|
+
been resolved with :meth:`get_target`.
|
|
210
|
+
"""
|
|
211
|
+
if self._cached_target is not None:
|
|
212
|
+
return self._cached_target
|
|
213
|
+
if not callable(self._target_source) and not inspect.isawaitable(self._target_source):
|
|
214
|
+
return self._target_source
|
|
215
|
+
raise RuntimeError("target is resolved asynchronously; use `await state.get_target()`")
|
|
216
|
+
|
|
217
|
+
@property
|
|
218
|
+
def session_store(self) -> SessionStore:
|
|
219
|
+
"""Return the session store for this state."""
|
|
220
|
+
return self._session_store
|
|
221
|
+
|
|
222
|
+
async def get_or_create_session(self, session_id: str) -> AgentSession:
|
|
223
|
+
"""Return the session for ``session_id``, creating and storing one if missing.
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
session_id: Opaque app-selected session id.
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
The stored or newly created ``AgentSession``.
|
|
230
|
+
"""
|
|
231
|
+
if not session_id:
|
|
232
|
+
raise ValueError("session_id must be a non-empty string")
|
|
233
|
+
session_lock = self._session_locks.setdefault(session_id, asyncio.Lock())
|
|
234
|
+
async with session_lock:
|
|
235
|
+
session = await self._session_store.get(session_id)
|
|
236
|
+
if session is None:
|
|
237
|
+
target = await self.get_target()
|
|
238
|
+
session = target.create_session(session_id=session_id)
|
|
239
|
+
await self._session_store.set(session_id, session)
|
|
240
|
+
return session
|
|
241
|
+
|
|
242
|
+
async def set_session(self, session_id: str, session: AgentSession) -> None:
|
|
243
|
+
"""Store ``session`` under ``session_id`` in this state's session store.
|
|
244
|
+
|
|
245
|
+
Args:
|
|
246
|
+
session_id: Opaque app-selected session id.
|
|
247
|
+
session: Session to store.
|
|
248
|
+
"""
|
|
249
|
+
await self._session_store.set(session_id, session)
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
class WorkflowState(Generic[WorkflowT]):
|
|
253
|
+
"""Shared execution state for app-owned workflow hosting routes.
|
|
254
|
+
|
|
255
|
+
Holds the Agent Framework workflow target. Does not own routes,
|
|
256
|
+
middleware, protocol dispatch, native SDK calls, or checkpoint storage --
|
|
257
|
+
web frameworks and workflow/app code keep those concerns.
|
|
258
|
+
"""
|
|
259
|
+
|
|
260
|
+
def __init__(
|
|
261
|
+
self,
|
|
262
|
+
target: WorkflowT | SupportsBuild | Awaitable[WorkflowT] | Callable[[], WorkflowT | Awaitable[WorkflowT]],
|
|
263
|
+
*,
|
|
264
|
+
cache_target: bool = True,
|
|
265
|
+
) -> None:
|
|
266
|
+
"""Create shared state for ``target``.
|
|
267
|
+
|
|
268
|
+
Args:
|
|
269
|
+
target: Workflow target used by route code. May be a target
|
|
270
|
+
instance, a ``WorkflowBuilder``-shaped builder (see
|
|
271
|
+
:class:`SupportsBuild`; the state calls ``build()`` for you),
|
|
272
|
+
a synchronous factory, an asynchronous factory, or an
|
|
273
|
+
awaitable target.
|
|
274
|
+
|
|
275
|
+
Keyword Args:
|
|
276
|
+
cache_target: Whether to cache a resolved callable/awaitable/built
|
|
277
|
+
target. Defaults to ``True`` so expensive target setup
|
|
278
|
+
happens once.
|
|
279
|
+
|
|
280
|
+
Raises:
|
|
281
|
+
ValueError: If ``cache_target=False`` is used with a one-shot
|
|
282
|
+
awaitable target.
|
|
283
|
+
"""
|
|
284
|
+
if isinstance(target, SupportsBuild):
|
|
285
|
+
# WorkflowBuilder (and the orchestration builders) are not
|
|
286
|
+
# themselves callable or awaitable, so normalize to the bound
|
|
287
|
+
# `build` method -- the resolution logic below already knows how
|
|
288
|
+
# to treat a zero-arg factory. `build()` is typed to return the
|
|
289
|
+
# `Workflow` base class rather than this instance's narrower
|
|
290
|
+
# `WorkflowT`, but it is the same object the caller asked for.
|
|
291
|
+
target = cast("Callable[[], WorkflowT]", target.build)
|
|
292
|
+
if not cache_target and inspect.isawaitable(target):
|
|
293
|
+
raise ValueError("cache_target=False requires a target instance or callable target factory")
|
|
294
|
+
self._target_source = target
|
|
295
|
+
self._cache_target = cache_target
|
|
296
|
+
self._cached_target: WorkflowT | None = None
|
|
297
|
+
self._target_lock = asyncio.Lock()
|
|
298
|
+
if not callable(target) and not inspect.isawaitable(target):
|
|
299
|
+
self._cached_target = target
|
|
300
|
+
|
|
301
|
+
async def get_target(self) -> WorkflowT:
|
|
302
|
+
"""Return the resolved target.
|
|
303
|
+
|
|
304
|
+
Returns:
|
|
305
|
+
The target instance. Callable and awaitable targets are resolved
|
|
306
|
+
first and cached by default.
|
|
307
|
+
"""
|
|
308
|
+
if self._cache_target and self._cached_target is not None:
|
|
309
|
+
return self._cached_target
|
|
310
|
+
|
|
311
|
+
if self._cache_target:
|
|
312
|
+
async with self._target_lock:
|
|
313
|
+
if self._cached_target is not None:
|
|
314
|
+
return self._cached_target
|
|
315
|
+
target = self._target_source() if callable(self._target_source) else self._target_source
|
|
316
|
+
if inspect.isawaitable(target):
|
|
317
|
+
target = await target
|
|
318
|
+
self._cached_target = target
|
|
319
|
+
return target
|
|
320
|
+
|
|
321
|
+
target = self._target_source() if callable(self._target_source) else self._target_source
|
|
322
|
+
if inspect.isawaitable(target):
|
|
323
|
+
target = await target
|
|
324
|
+
return target
|
|
325
|
+
|
|
326
|
+
@property
|
|
327
|
+
def target(self) -> WorkflowT:
|
|
328
|
+
"""Return a synchronously available target.
|
|
329
|
+
|
|
330
|
+
Raises:
|
|
331
|
+
RuntimeError: If the target is a callable or awaitable that has not
|
|
332
|
+
been resolved with :meth:`get_target`.
|
|
333
|
+
"""
|
|
334
|
+
if self._cached_target is not None:
|
|
335
|
+
return self._cached_target
|
|
336
|
+
if not callable(self._target_source) and not inspect.isawaitable(self._target_source):
|
|
337
|
+
return self._target_source
|
|
338
|
+
raise RuntimeError("target is resolved asynchronously; use `await state.get_target()`")
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agent-framework-hosting
|
|
3
|
+
Version: 1.0.0a260709
|
|
4
|
+
Summary: Execution-state helpers for app-owned Agent Framework hosting routes.
|
|
5
|
+
Author-email: Microsoft <af-support@microsoft.com>
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Classifier: Typing :: Typed
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: agent-framework-core>=1.11.0,<2
|
|
20
|
+
Project-URL: homepage, https://aka.ms/agent-framework
|
|
21
|
+
Project-URL: issues, https://github.com/microsoft/agent-framework/issues
|
|
22
|
+
Project-URL: release_notes, https://github.com/microsoft/agent-framework/releases?q=tag%3Apython-1&expanded=true
|
|
23
|
+
Project-URL: source, https://github.com/microsoft/agent-framework/tree/main/python
|
|
24
|
+
|
|
25
|
+
# agent-framework-hosting
|
|
26
|
+
|
|
27
|
+
Shared execution-state helpers for app-owned Agent Framework hosting.
|
|
28
|
+
|
|
29
|
+
This package keeps Agent Framework state separate from web-framework concerns:
|
|
30
|
+
|
|
31
|
+
- `AgentState` — pairs an agent target with a `SessionStore`
|
|
32
|
+
(`session_id -> AgentSession`).
|
|
33
|
+
- `WorkflowState` — resolves a workflow target, including direct `Workflow`
|
|
34
|
+
instances, workflow factories, `WorkflowBuilder`, and orchestration builders.
|
|
35
|
+
|
|
36
|
+
`SessionStore` is plain storage: `get`/`set`/`delete` by an app-selected id,
|
|
37
|
+
nothing more. It does not know how to create a new value for an id it hasn't
|
|
38
|
+
seen before — use `AgentState.get_or_create_session(...)` for that, since only
|
|
39
|
+
the state object has both the store and the resolved target. Workflow
|
|
40
|
+
checkpointing should use the existing `CheckpointStorage` abstraction directly;
|
|
41
|
+
if an app needs per-session resume, keep a small app-owned cursor such as
|
|
42
|
+
`session_id -> checkpoint_id`.
|
|
43
|
+
|
|
44
|
+
Use FastAPI, Starlette, Azure Functions, Django, or another framework for route
|
|
45
|
+
registration, auth, middleware, response construction, and background work.
|
|
46
|
+
|
|
47
|
+
> The built-in `SessionStore` is an in-memory `dict` with no eviction — every
|
|
48
|
+
> id ever stored stays resolvable for the life of the process. That is
|
|
49
|
+
> intentional: protocols such as OpenAI Responses'
|
|
50
|
+
> `previous_response_id` are designed to let a caller continue from *any*
|
|
51
|
+
> earlier point in a conversation, not just the latest turn, so every id
|
|
52
|
+
> handed out needs to stay independently resolvable. If you back the store
|
|
53
|
+
> with real storage (Redis, a database, ...), you are responsible for that
|
|
54
|
+
> store's own TTL/eviction policy; this in-memory reference implementation
|
|
55
|
+
> does not model that concern.
|
|
56
|
+
|
|
57
|
+
## Quickstart
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
from agent_framework.openai import OpenAIChatClient
|
|
61
|
+
from agent_framework_hosting import AgentState
|
|
62
|
+
|
|
63
|
+
agent = OpenAIChatClient().as_agent(name="Assistant")
|
|
64
|
+
state = AgentState(agent)
|
|
65
|
+
|
|
66
|
+
session = await state.get_or_create_session("conversation-1")
|
|
67
|
+
result = await (await state.get_target()).run("Hello", session=session)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
If a protocol mints a new continuation id on every response, store the session
|
|
71
|
+
explicitly after `run(...)` returns. `run(...)` may update the session, so store
|
|
72
|
+
the post-run object:
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
session = await state.get_or_create_session(previous_response_id)
|
|
76
|
+
result = await (await state.get_target()).run("Hello", session=session)
|
|
77
|
+
await state.set_session(response_id, session)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Targets can be direct instances, synchronous factories, asynchronous factories,
|
|
81
|
+
or awaitables:
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
state = AgentState(create_agent) # cached by default
|
|
85
|
+
state = AgentState(create_agent, cache_target=False)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`WorkflowState` mirrors this shape for workflow targets:
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
from agent_framework import InMemoryCheckpointStorage
|
|
92
|
+
from agent_framework_hosting import WorkflowState
|
|
93
|
+
|
|
94
|
+
state = WorkflowState(create_workflow)
|
|
95
|
+
storage = InMemoryCheckpointStorage()
|
|
96
|
+
result = await (await state.get_target()).run("Hello", checkpoint_storage=storage)
|
|
97
|
+
latest = await storage.get_latest(workflow_name=(await state.get_target()).name)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
`WorkflowState` also accepts an unbuilt workflow builder directly:
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
from agent_framework import WorkflowBuilder
|
|
104
|
+
from agent_framework_hosting import WorkflowState
|
|
105
|
+
|
|
106
|
+
builder = WorkflowBuilder(start_executor=executor)
|
|
107
|
+
state = WorkflowState(builder) # calls builder.build() when the target is resolved
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
This is structural: orchestration builders from `agent_framework_orchestrations`
|
|
111
|
+
(`SequentialBuilder`, `ConcurrentBuilder`, `HandoffBuilder`, `GroupChatBuilder`,
|
|
112
|
+
and `MagenticBuilder`) also work because they expose the same zero-argument
|
|
113
|
+
`build() -> Workflow` method.
|
|
114
|
+
|
|
115
|
+
Cross-channel identity linking, multicast delivery, background runs,
|
|
116
|
+
continuation tokens, and durable delivery runners are follow-up enhancements,
|
|
117
|
+
not part of this v1 state surface.
|
|
118
|
+
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
agent_framework_hosting/__init__.py,sha256=cku2Rb9iNbkg5nR3w6VK2o6CPpvFSKhPYzeYGk-o6Aw,580
|
|
2
|
+
agent_framework_hosting/_state.py,sha256=X6Iun9o8PCPQZ8B54rL0YGTz2KMVUYmVkwtY9Zn_cyw,13185
|
|
3
|
+
agent_framework_hosting-1.0.0a260709.dist-info/licenses/LICENSE,sha256=ws_MuBL-SCEBqPBFl9_FqZkaaydIJmxHrJG2parhU4M,1141
|
|
4
|
+
agent_framework_hosting-1.0.0a260709.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
|
5
|
+
agent_framework_hosting-1.0.0a260709.dist-info/METADATA,sha256=5kQBPfLvi2FE33kFbpt1YQLbylOsPDQFsBfhOpSmnUc,4947
|
|
6
|
+
agent_framework_hosting-1.0.0a260709.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) Microsoft Corporation.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE
|