agent-framework-core 1.2.0__tar.gz → 1.2.2__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.
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/PKG-INFO +1 -1
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/__init__.py +2 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_telemetry.py +4 -2
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_types.py +53 -7
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_agent.py +21 -6
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_agent_executor.py +3 -2
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_runner.py +6 -1
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_workflow.py +58 -31
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_workflow_executor.py +2 -2
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/exceptions.py +2 -1
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/foundry/__init__.py +9 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/foundry/__init__.pyi +12 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/observability.py +104 -44
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/pyproject.toml +1 -1
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/LICENSE +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/README.md +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_agents.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_clients.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_compaction.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_docstrings.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_evaluation.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_feature_stage.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_mcp.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_middleware.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_serialization.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_sessions.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_settings.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_skills.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_tools.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/__init__.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_agent_utils.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_checkpoint.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_checkpoint_encoding.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_const.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_conversation_history.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_edge.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_edge_runner.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_events.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_executor.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_function_executor.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_functional.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_message_utils.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_model_utils.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_request_info_mixin.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_runner_context.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_state.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_typing_utils.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_validation.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_viz.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_workflow_builder.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_workflow_context.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/a2a/__init__.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/a2a/__init__.pyi +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/ag_ui/__init__.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/ag_ui/__init__.pyi +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/amazon/__init__.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/amazon/__init__.pyi +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/anthropic/__init__.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/anthropic/__init__.pyi +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/azure/__init__.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/azure/__init__.pyi +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/chatkit/__init__.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/chatkit/__init__.pyi +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/declarative/__init__.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/declarative/__init__.pyi +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/devui/__init__.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/devui/__init__.pyi +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/github/__init__.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/github/__init__.pyi +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/google/__init__.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/google/__init__.pyi +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/lab/__init__.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/mem0/__init__.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/mem0/__init__.pyi +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/microsoft/__init__.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/microsoft/__init__.pyi +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/ollama/__init__.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/ollama/__init__.pyi +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/openai/__init__.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/openai/__init__.pyi +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/orchestrations/__init__.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/orchestrations/__init__.pyi +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/py.typed +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/redis/__init__.py +0 -0
- {agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/redis/__init__.pyi +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-framework-core
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.2
|
|
4
4
|
Summary: Microsoft Agent Framework for building AI Agents with Python. This is the core package that has all the core abstractions and implementations.
|
|
5
5
|
Author-email: Microsoft <af-support@microsoft.com>
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -247,6 +247,7 @@ from ._workflows._workflow_executor import (
|
|
|
247
247
|
WorkflowExecutor,
|
|
248
248
|
)
|
|
249
249
|
from .exceptions import (
|
|
250
|
+
AgentFrameworkException,
|
|
250
251
|
MiddlewareException,
|
|
251
252
|
UserInputRequiredException,
|
|
252
253
|
WorkflowCheckpointException,
|
|
@@ -280,6 +281,7 @@ __all__ = [
|
|
|
280
281
|
"AgentExecutor",
|
|
281
282
|
"AgentExecutorRequest",
|
|
282
283
|
"AgentExecutorResponse",
|
|
284
|
+
"AgentFrameworkException",
|
|
283
285
|
"AgentMiddleware",
|
|
284
286
|
"AgentMiddlewareLayer",
|
|
285
287
|
"AgentMiddlewareTypes",
|
|
@@ -79,9 +79,11 @@ def _detect_hosted_environment() -> None:
|
|
|
79
79
|
except (ModuleNotFoundError, ValueError):
|
|
80
80
|
return
|
|
81
81
|
with contextlib.suppress(ImportError, AttributeError):
|
|
82
|
-
from azure.ai.agentserver.core import
|
|
82
|
+
from azure.ai.agentserver.core import ( # pyright: ignore[reportMissingImports]
|
|
83
|
+
AgentConfig, # pyright: ignore[reportUnknownVariableType]
|
|
84
|
+
)
|
|
83
85
|
|
|
84
|
-
if AgentConfig.from_env().is_hosted:
|
|
86
|
+
if AgentConfig.from_env().is_hosted: # pyright: ignore[reportUnknownMemberType]
|
|
85
87
|
_add_user_agent_prefix(_HOSTED_USER_AGENT_PREFIX)
|
|
86
88
|
_hosted_env_detected = True
|
|
87
89
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import base64
|
|
6
|
+
import contextlib
|
|
6
7
|
import json
|
|
7
8
|
import logging
|
|
8
9
|
import re
|
|
@@ -2890,6 +2891,7 @@ class ResponseStream(AsyncIterable[UpdateT], Generic[UpdateT, FinalT]):
|
|
|
2890
2891
|
self._inner_stream_source: ResponseStream[Any, Any] | Awaitable[ResponseStream[Any, Any]] | None = None
|
|
2891
2892
|
self._wrap_inner: bool = False
|
|
2892
2893
|
self._map_update: Callable[[Any], UpdateT | Awaitable[UpdateT]] | None = None
|
|
2894
|
+
self._pull_context_manager_factories: list[Callable[[], contextlib.AbstractContextManager[Any]]] = []
|
|
2893
2895
|
|
|
2894
2896
|
def map(
|
|
2895
2897
|
self,
|
|
@@ -3008,11 +3010,18 @@ class ResponseStream(AsyncIterable[UpdateT], Generic[UpdateT, FinalT]):
|
|
|
3008
3010
|
return self
|
|
3009
3011
|
|
|
3010
3012
|
async def __anext__(self) -> UpdateT:
|
|
3011
|
-
if self._iterator is None:
|
|
3012
|
-
stream = await self._get_stream()
|
|
3013
|
-
self._iterator = stream.__aiter__()
|
|
3014
3013
|
try:
|
|
3015
|
-
|
|
3014
|
+
with contextlib.ExitStack() as stack:
|
|
3015
|
+
for factory in self._pull_context_manager_factories:
|
|
3016
|
+
stack.enter_context(factory())
|
|
3017
|
+
# Resolve the underlying stream inside the pull contexts so that any
|
|
3018
|
+
# spans/contexts created during stream resolution (e.g. inner chat
|
|
3019
|
+
# completion spans created on the first pull of a wrapped agent stream)
|
|
3020
|
+
# inherit the active context (e.g. an outer agent invoke span).
|
|
3021
|
+
if self._iterator is None:
|
|
3022
|
+
stream = await self._get_stream()
|
|
3023
|
+
self._iterator = stream.__aiter__()
|
|
3024
|
+
update: UpdateT = await self._iterator.__anext__()
|
|
3016
3025
|
except StopAsyncIteration:
|
|
3017
3026
|
self._consumed = True
|
|
3018
3027
|
await self._run_cleanup_hooks()
|
|
@@ -3038,9 +3047,25 @@ class ResponseStream(AsyncIterable[UpdateT], Generic[UpdateT, FinalT]):
|
|
|
3038
3047
|
update = hooked
|
|
3039
3048
|
return update
|
|
3040
3049
|
|
|
3050
|
+
async def _resolve_stream_with_pull_contexts(self) -> AsyncIterable[UpdateT]:
|
|
3051
|
+
"""Resolve the underlying stream while activating any registered pull context managers.
|
|
3052
|
+
|
|
3053
|
+
Used by ``__await__`` and ``get_final_response`` so that any spans/contexts created
|
|
3054
|
+
during stream resolution (e.g. when the source is an Awaitable that internally
|
|
3055
|
+
creates child telemetry spans) inherit the same active context as iterator pulls.
|
|
3056
|
+
``__anext__`` resolves the stream inside its own ExitStack and so calls ``_get_stream``
|
|
3057
|
+
directly.
|
|
3058
|
+
"""
|
|
3059
|
+
if self._stream is not None:
|
|
3060
|
+
return await self._get_stream()
|
|
3061
|
+
with contextlib.ExitStack() as stack:
|
|
3062
|
+
for factory in self._pull_context_manager_factories:
|
|
3063
|
+
stack.enter_context(factory())
|
|
3064
|
+
return await self._get_stream()
|
|
3065
|
+
|
|
3041
3066
|
def __await__(self) -> Any:
|
|
3042
3067
|
async def _wrap() -> ResponseStream[UpdateT, FinalT]:
|
|
3043
|
-
await self.
|
|
3068
|
+
await self._resolve_stream_with_pull_contexts()
|
|
3044
3069
|
return self
|
|
3045
3070
|
|
|
3046
3071
|
return _wrap().__await__()
|
|
@@ -3064,10 +3089,12 @@ class ResponseStream(AsyncIterable[UpdateT], Generic[UpdateT, FinalT]):
|
|
|
3064
3089
|
"""
|
|
3065
3090
|
if self._wrap_inner:
|
|
3066
3091
|
if self._inner_stream is None:
|
|
3067
|
-
# Use
|
|
3092
|
+
# Use _resolve_stream_with_pull_contexts() so that any spans/contexts
|
|
3093
|
+
# created while resolving the awaitable (e.g. inner telemetry spans)
|
|
3094
|
+
# inherit the same active context as iterator pulls. This also handles
|
|
3068
3095
|
# the case where _stream_source and _inner_stream_source are the same
|
|
3069
3096
|
# coroutine (e.g., from from_awaitable), avoiding double-await errors.
|
|
3070
|
-
await self.
|
|
3097
|
+
await self._resolve_stream_with_pull_contexts()
|
|
3071
3098
|
if self._inner_stream is None:
|
|
3072
3099
|
raise RuntimeError("Inner stream not available")
|
|
3073
3100
|
if not self._finalized and not self._consumed:
|
|
@@ -3177,6 +3204,25 @@ class ResponseStream(AsyncIterable[UpdateT], Generic[UpdateT, FinalT]):
|
|
|
3177
3204
|
self._cleanup_hooks.append(hook)
|
|
3178
3205
|
return self
|
|
3179
3206
|
|
|
3207
|
+
def with_pull_context_manager(
|
|
3208
|
+
self,
|
|
3209
|
+
cm_factory: Callable[[], contextlib.AbstractContextManager[Any]],
|
|
3210
|
+
) -> ResponseStream[UpdateT, FinalT]:
|
|
3211
|
+
"""Register a context manager factory invoked around each underlying iterator pull.
|
|
3212
|
+
|
|
3213
|
+
The factory is called once per ``__anext__`` and the returned context manager wraps
|
|
3214
|
+
the await of the underlying iterator. This is useful for state that needs to be
|
|
3215
|
+
active while the inner async work runs - for example, attaching an OpenTelemetry
|
|
3216
|
+
span to the current context so child spans created by inner code (HTTP clients,
|
|
3217
|
+
tool execution) are correctly parented.
|
|
3218
|
+
|
|
3219
|
+
Because the context manager is entered and exited within the same ``__anext__``
|
|
3220
|
+
invocation, attach/detach style operations remain symmetric in the same async
|
|
3221
|
+
context regardless of where the stream is iterated.
|
|
3222
|
+
"""
|
|
3223
|
+
self._pull_context_manager_factories.append(cm_factory)
|
|
3224
|
+
return self
|
|
3225
|
+
|
|
3180
3226
|
async def _run_cleanup_hooks(self) -> None:
|
|
3181
3227
|
if self._cleanup_run:
|
|
3182
3228
|
return
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_agent.py
RENAMED
|
@@ -437,6 +437,13 @@ class WorkflowAgent(BaseAgent):
|
|
|
437
437
|
yield event
|
|
438
438
|
|
|
439
439
|
elif checkpoint_id is not None:
|
|
440
|
+
# Restore the prior workflow state from the checkpoint. Shared
|
|
441
|
+
# state (e.g. accumulated conversation history maintained by the
|
|
442
|
+
# workflow's executors) survives across turns because Workflow.run
|
|
443
|
+
# no longer wipes state per call. Callers who want to deliver a
|
|
444
|
+
# new user message after restore should make a second
|
|
445
|
+
# `workflow.run(message=...)` call - they are NOT mutually
|
|
446
|
+
# exclusive on the same instance, but each must be its own call.
|
|
440
447
|
if streaming:
|
|
441
448
|
async for event in self.workflow.run(
|
|
442
449
|
stream=True,
|
|
@@ -528,6 +535,7 @@ class WorkflowAgent(BaseAgent):
|
|
|
528
535
|
raw_representations.append(output_event)
|
|
529
536
|
else:
|
|
530
537
|
data = output_event.data
|
|
538
|
+
|
|
531
539
|
if isinstance(data, AgentResponseUpdate):
|
|
532
540
|
# We cannot support AgentResponseUpdate in non-streaming mode. This is because the message
|
|
533
541
|
# sequence cannot be guaranteed when there are streaming updates in between non-streaming
|
|
@@ -628,16 +636,23 @@ class WorkflowAgent(BaseAgent):
|
|
|
628
636
|
A list of AgentResponseUpdate objects. Empty list if the event is not relevant.
|
|
629
637
|
"""
|
|
630
638
|
if event.type == "output":
|
|
631
|
-
# Convert workflow output to agent response updates.
|
|
632
|
-
# Handle different data types appropriately.
|
|
633
639
|
data = event.data
|
|
634
640
|
executor_id = event.executor_id
|
|
635
641
|
|
|
636
642
|
if isinstance(data, AgentResponseUpdate):
|
|
637
|
-
#
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
643
|
+
# Construct a fresh AgentResponseUpdate so we don't mutate a payload
|
|
644
|
+
# that AgentExecutor still holds a reference to in its `updates` list.
|
|
645
|
+
return [
|
|
646
|
+
AgentResponseUpdate(
|
|
647
|
+
contents=list(data.contents),
|
|
648
|
+
role=data.role,
|
|
649
|
+
author_name=data.author_name or executor_id,
|
|
650
|
+
response_id=data.response_id,
|
|
651
|
+
message_id=data.message_id,
|
|
652
|
+
created_at=data.created_at,
|
|
653
|
+
raw_representation=data.raw_representation,
|
|
654
|
+
)
|
|
655
|
+
]
|
|
641
656
|
if isinstance(data, AgentResponse):
|
|
642
657
|
# Convert each message in AgentResponse to an AgentResponseUpdate
|
|
643
658
|
updates: list[AgentResponseUpdate] = []
|
|
@@ -156,8 +156,9 @@ class AgentExecutor(Executor):
|
|
|
156
156
|
the agent run.
|
|
157
157
|
- "custom": use the provided context_filter function to determine which messages to include
|
|
158
158
|
as context for the agent run.
|
|
159
|
-
context_filter:
|
|
160
|
-
to
|
|
159
|
+
context_filter: A function that takes the full conversation (list of Messages) as input and returns
|
|
160
|
+
a filtered list of Messages to be used as context for the agent run. This is required
|
|
161
|
+
if context_mode is set to "custom".
|
|
161
162
|
"""
|
|
162
163
|
# Prefer provided id; else use agent.name if present; else generate deterministic prefix
|
|
163
164
|
exec_id = id or resolve_agent_id(agent)
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_runner.py
RENAMED
|
@@ -278,7 +278,12 @@ class Runner:
|
|
|
278
278
|
"Please rebuild the original workflow before resuming."
|
|
279
279
|
)
|
|
280
280
|
|
|
281
|
-
# Restore state
|
|
281
|
+
# Restore state. Clear first so import_state (which merges) does
|
|
282
|
+
# not leak stale keys from a prior run on this Workflow instance.
|
|
283
|
+
# This matters more now that Workflow.run() no longer wipes state
|
|
284
|
+
# per call - the only reset point for shared state on a reused
|
|
285
|
+
# instance is at restore time.
|
|
286
|
+
self._state.clear()
|
|
282
287
|
self._state.import_state(checkpoint.state)
|
|
283
288
|
# Restore executor states using the restored state
|
|
284
289
|
await self._restore_executor_states()
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_workflow.py
RENAMED
|
@@ -299,7 +299,7 @@ class Workflow(DictConvertible):
|
|
|
299
299
|
async def _run_workflow_with_tracing(
|
|
300
300
|
self,
|
|
301
301
|
initial_executor_fn: Callable[[], Awaitable[None]] | None = None,
|
|
302
|
-
|
|
302
|
+
is_continuation: bool = False,
|
|
303
303
|
streaming: bool = False,
|
|
304
304
|
function_invocation_kwargs: Mapping[str, Mapping[str, Any]] | Mapping[str, Any] | None = None,
|
|
305
305
|
client_kwargs: Mapping[str, Mapping[str, Any]] | Mapping[str, Any] | None = None,
|
|
@@ -310,13 +310,19 @@ class Workflow(DictConvertible):
|
|
|
310
310
|
of external callers to maintain context across different workflow runs.
|
|
311
311
|
|
|
312
312
|
Args:
|
|
313
|
-
initial_executor_fn: Optional function to execute initial executor
|
|
314
|
-
|
|
315
|
-
|
|
313
|
+
initial_executor_fn: Optional function to execute initial executor.
|
|
314
|
+
is_continuation: True when this run is a continuation of prior
|
|
315
|
+
work (a checkpoint restore or a responses-only replay) rather
|
|
316
|
+
than a fresh new turn delivered via the start executor with
|
|
317
|
+
``message=...``. Continuations preserve per-run accounting
|
|
318
|
+
(iteration counter and run kwargs) from the prior turn;
|
|
319
|
+
fresh-message runs reset them. Shared workflow state is
|
|
320
|
+
preserved in both cases.
|
|
321
|
+
streaming: Whether to enable streaming mode for agents.
|
|
316
322
|
function_invocation_kwargs: Optional kwargs to store in State for function
|
|
317
|
-
invocations in subagents
|
|
323
|
+
invocations in subagents.
|
|
318
324
|
client_kwargs: Optional kwargs to store in State for chat client
|
|
319
|
-
invocations in subagents
|
|
325
|
+
invocations in subagents.
|
|
320
326
|
|
|
321
327
|
Yields:
|
|
322
328
|
WorkflowEvent: The events generated during the workflow execution.
|
|
@@ -345,16 +351,26 @@ class Workflow(DictConvertible):
|
|
|
345
351
|
in_progress = WorkflowEvent.status(WorkflowRunState.IN_PROGRESS)
|
|
346
352
|
yield in_progress # noqa: RUF070
|
|
347
353
|
|
|
348
|
-
#
|
|
349
|
-
|
|
354
|
+
# Per-run reset for fresh-message runs only. We deliberately
|
|
355
|
+
# do NOT clear shared workflow state (`_state.clear()`) or the
|
|
356
|
+
# runner context's in-flight messages (`reset_for_new_run()`)
|
|
357
|
+
# here - state and pending work persist across `run()` calls
|
|
358
|
+
# so that a `WorkflowAgent` can deliver multi-turn input on
|
|
359
|
+
# the same instance and have prior turns' context survive.
|
|
360
|
+
# Iteration counting and per-run kwargs ARE per-run though,
|
|
361
|
+
# so they're reset here.
|
|
362
|
+
if not is_continuation:
|
|
350
363
|
self._runner.reset_iteration_count()
|
|
351
|
-
self._runner.context.reset_for_new_run()
|
|
352
|
-
self._state.clear()
|
|
353
364
|
|
|
354
365
|
# Store run kwargs in State so executors can access them.
|
|
355
|
-
#
|
|
356
|
-
#
|
|
357
|
-
#
|
|
366
|
+
# Per-run kwargs semantics:
|
|
367
|
+
# - On a fresh message run, prior kwargs go away (set to {}
|
|
368
|
+
# by default, or to the new kwargs if provided). This
|
|
369
|
+
# prevents stale kwargs from a prior turn leaking into the
|
|
370
|
+
# current turn.
|
|
371
|
+
# - On a continuation (checkpoint restore or responses), the
|
|
372
|
+
# prior run's kwargs are preserved unless the caller
|
|
373
|
+
# explicitly provides new kwargs.
|
|
358
374
|
if function_invocation_kwargs is not None or client_kwargs is not None:
|
|
359
375
|
combined_kwargs: dict[str, Any] = {}
|
|
360
376
|
if function_invocation_kwargs is not None:
|
|
@@ -366,11 +382,12 @@ class Workflow(DictConvertible):
|
|
|
366
382
|
client_kwargs, "client_kwargs"
|
|
367
383
|
)
|
|
368
384
|
self._state.set(WORKFLOW_RUN_KWARGS_KEY, combined_kwargs)
|
|
369
|
-
elif
|
|
385
|
+
elif not is_continuation:
|
|
370
386
|
self._state.set(WORKFLOW_RUN_KWARGS_KEY, {})
|
|
371
387
|
self._state.commit() # Commit immediately so kwargs are available
|
|
372
388
|
|
|
373
|
-
# Set streaming mode
|
|
389
|
+
# Set streaming mode (always set explicitly per run since
|
|
390
|
+
# reset_for_new_run() no longer runs to clear it).
|
|
374
391
|
self._runner_context.set_streaming(streaming)
|
|
375
392
|
|
|
376
393
|
# Execute initial setup if provided
|
|
@@ -585,13 +602,31 @@ class Workflow(DictConvertible):
|
|
|
585
602
|
if checkpoint_storage is not None:
|
|
586
603
|
self._runner.context.set_runtime_checkpoint_storage(checkpoint_storage)
|
|
587
604
|
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
605
|
+
# Async validation: a fresh-message run is only allowed when the
|
|
606
|
+
# runner context has fully drained from any prior run. If it still
|
|
607
|
+
# has in-flight executor messages, the prior run didn't complete -
|
|
608
|
+
# the caller must either resume from a checkpoint or wait for the
|
|
609
|
+
# prior run to drain. (Pending request_info events are intentionally
|
|
610
|
+
# NOT blocked here: a follow-up run with message=... is the normal
|
|
611
|
+
# way to deliver a response to those pending requests, e.g. via
|
|
612
|
+
# WorkflowAgent._process_pending_requests.)
|
|
613
|
+
# NOTE: _validate_run_params already enforces that ``message`` is
|
|
614
|
+
# mutually exclusive with both ``checkpoint_id`` and ``responses``,
|
|
615
|
+
# so we don't need to re-check those here.
|
|
616
|
+
if message is not None and await self._runner.context.has_messages():
|
|
617
|
+
raise RuntimeError(
|
|
618
|
+
"Cannot start a new run with 'message' while in-flight executor "
|
|
619
|
+
"messages remain from a prior run. Resume from a checkpoint "
|
|
620
|
+
"(checkpoint_id=...) or wait for the prior run to complete. "
|
|
621
|
+
"Workflows that need to recover from a mid-run failure must use "
|
|
622
|
+
"checkpointing; there is no in-process recovery path."
|
|
623
|
+
)
|
|
624
|
+
|
|
625
|
+
initial_executor_fn = self._resolve_execution_mode(message, responses, checkpoint_id, checkpoint_storage)
|
|
591
626
|
|
|
592
627
|
async for event in self._run_workflow_with_tracing(
|
|
593
628
|
initial_executor_fn=initial_executor_fn,
|
|
594
|
-
|
|
629
|
+
is_continuation=(message is None),
|
|
595
630
|
streaming=streaming,
|
|
596
631
|
function_invocation_kwargs=function_invocation_kwargs,
|
|
597
632
|
client_kwargs=client_kwargs,
|
|
@@ -674,12 +709,8 @@ class Workflow(DictConvertible):
|
|
|
674
709
|
responses: Mapping[str, Any] | None,
|
|
675
710
|
checkpoint_id: str | None,
|
|
676
711
|
checkpoint_storage: CheckpointStorage | None,
|
|
677
|
-
) ->
|
|
678
|
-
"""Determine the initial executor function
|
|
679
|
-
|
|
680
|
-
Returns:
|
|
681
|
-
A tuple of (initial_executor_fn, reset_context).
|
|
682
|
-
"""
|
|
712
|
+
) -> Callable[[], Awaitable[None]]:
|
|
713
|
+
"""Determine the initial executor function based on parameters."""
|
|
683
714
|
if responses is not None:
|
|
684
715
|
if checkpoint_id is not None:
|
|
685
716
|
# Combined: restore checkpoint then send responses
|
|
@@ -689,13 +720,9 @@ class Workflow(DictConvertible):
|
|
|
689
720
|
else:
|
|
690
721
|
# Send responses only (requires pending requests in workflow state)
|
|
691
722
|
initial_executor_fn = functools.partial(self._send_responses_internal, responses)
|
|
692
|
-
return initial_executor_fn
|
|
723
|
+
return initial_executor_fn
|
|
693
724
|
# Regular run or checkpoint restoration
|
|
694
|
-
|
|
695
|
-
self._execute_with_message_or_checkpoint, message, checkpoint_id, checkpoint_storage
|
|
696
|
-
)
|
|
697
|
-
reset_context = message is not None and checkpoint_id is None
|
|
698
|
-
return initial_executor_fn, reset_context
|
|
725
|
+
return functools.partial(self._execute_with_message_or_checkpoint, message, checkpoint_id, checkpoint_storage)
|
|
699
726
|
|
|
700
727
|
async def _restore_and_send_responses(
|
|
701
728
|
self,
|
|
@@ -361,7 +361,7 @@ class WorkflowExecutor(Executor):
|
|
|
361
361
|
return any(is_instance_of(message.data, input_type) for input_type in self.workflow.input_types)
|
|
362
362
|
|
|
363
363
|
@handler
|
|
364
|
-
async def process_workflow(self, input_data: object, ctx: WorkflowContext[Any]) -> None:
|
|
364
|
+
async def process_workflow(self, input_data: object, ctx: WorkflowContext[Any, Any]) -> None:
|
|
365
365
|
"""Execute the sub-workflow with raw input data.
|
|
366
366
|
|
|
367
367
|
This handler starts a new sub-workflow execution. When the sub-workflow
|
|
@@ -428,7 +428,7 @@ class WorkflowExecutor(Executor):
|
|
|
428
428
|
async def handle_message_wrapped_request_response(
|
|
429
429
|
self,
|
|
430
430
|
response: SubWorkflowResponseMessage,
|
|
431
|
-
ctx: WorkflowContext[Any],
|
|
431
|
+
ctx: WorkflowContext[Any, Any],
|
|
432
432
|
) -> None:
|
|
433
433
|
"""Handle response from parent for a forwarded request.
|
|
434
434
|
|
|
@@ -34,7 +34,8 @@ class AgentFrameworkException(Exception):
|
|
|
34
34
|
logger.log(log_level, message, exc_info=inner_exception)
|
|
35
35
|
if inner_exception:
|
|
36
36
|
super().__init__(message, inner_exception, *args) # type: ignore
|
|
37
|
-
|
|
37
|
+
else:
|
|
38
|
+
super().__init__(message, *args) # type: ignore
|
|
38
39
|
|
|
39
40
|
|
|
40
41
|
# region Agent Exceptions
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/foundry/__init__.py
RENAMED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
This module lazily re-exports objects from:
|
|
6
6
|
- ``agent-framework-anthropic``
|
|
7
|
+
- ``agent-framework-azure-contentunderstanding``
|
|
7
8
|
- ``agent-framework-foundry``
|
|
8
9
|
- ``agent-framework-foundry-local``
|
|
9
10
|
"""
|
|
@@ -12,7 +13,15 @@ import importlib
|
|
|
12
13
|
from typing import Any
|
|
13
14
|
|
|
14
15
|
_IMPORTS: dict[str, tuple[str, str]] = {
|
|
16
|
+
"AnalysisSection": ("agent_framework_azure_contentunderstanding", "agent-framework-azure-contentunderstanding"),
|
|
15
17
|
"AnthropicFoundryClient": ("agent_framework_anthropic", "agent-framework-anthropic"),
|
|
18
|
+
"ContentUnderstandingContextProvider": (
|
|
19
|
+
"agent_framework_azure_contentunderstanding",
|
|
20
|
+
"agent-framework-azure-contentunderstanding",
|
|
21
|
+
),
|
|
22
|
+
"DocumentStatus": ("agent_framework_azure_contentunderstanding", "agent-framework-azure-contentunderstanding"),
|
|
23
|
+
"FileSearchBackend": ("agent_framework_azure_contentunderstanding", "agent-framework-azure-contentunderstanding"),
|
|
24
|
+
"FileSearchConfig": ("agent_framework_azure_contentunderstanding", "agent-framework-azure-contentunderstanding"),
|
|
16
25
|
"FoundryAgent": ("agent_framework_foundry", "agent-framework-foundry"),
|
|
17
26
|
"FoundryAgentOptions": ("agent_framework_foundry", "agent-framework-foundry"),
|
|
18
27
|
"FoundryChatClient": ("agent_framework_foundry", "agent-framework-foundry"),
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/foundry/__init__.pyi
RENAMED
|
@@ -4,6 +4,13 @@
|
|
|
4
4
|
# Install the relevant packages for full type support.
|
|
5
5
|
|
|
6
6
|
from agent_framework_anthropic import AnthropicFoundryClient, RawAnthropicFoundryClient
|
|
7
|
+
from agent_framework_azure_contentunderstanding import ( # pyright: ignore[reportMissingImports]
|
|
8
|
+
AnalysisSection, # pyright: ignore[reportUnknownVariableType]
|
|
9
|
+
ContentUnderstandingContextProvider, # pyright: ignore[reportUnknownVariableType]
|
|
10
|
+
DocumentStatus, # pyright: ignore[reportUnknownVariableType]
|
|
11
|
+
FileSearchBackend, # pyright: ignore[reportUnknownVariableType]
|
|
12
|
+
FileSearchConfig, # pyright: ignore[reportUnknownVariableType]
|
|
13
|
+
)
|
|
7
14
|
from agent_framework_foundry import (
|
|
8
15
|
FoundryAgent,
|
|
9
16
|
FoundryChatClient,
|
|
@@ -31,7 +38,12 @@ from agent_framework_foundry_local import (
|
|
|
31
38
|
)
|
|
32
39
|
|
|
33
40
|
__all__ = [
|
|
41
|
+
"AnalysisSection",
|
|
34
42
|
"AnthropicFoundryClient",
|
|
43
|
+
"ContentUnderstandingContextProvider",
|
|
44
|
+
"DocumentStatus",
|
|
45
|
+
"FileSearchBackend",
|
|
46
|
+
"FileSearchConfig",
|
|
35
47
|
"FoundryAgent",
|
|
36
48
|
"FoundryChatClient",
|
|
37
49
|
"FoundryChatOptions",
|
|
@@ -26,6 +26,7 @@ from time import perf_counter, time_ns
|
|
|
26
26
|
from typing import TYPE_CHECKING, Any, ClassVar, Final, Generic, Literal, TypedDict, cast, overload
|
|
27
27
|
|
|
28
28
|
from dotenv import load_dotenv
|
|
29
|
+
from opentelemetry import context as otel_context
|
|
29
30
|
from opentelemetry import metrics, trace
|
|
30
31
|
|
|
31
32
|
from . import __version__ as version_info
|
|
@@ -1277,27 +1278,8 @@ class ChatTelemetryLayer(Generic[OptionsCoT]):
|
|
|
1277
1278
|
)
|
|
1278
1279
|
|
|
1279
1280
|
if stream:
|
|
1280
|
-
|
|
1281
|
-
ResponseStream[ChatResponseUpdate, ChatResponse[Any]],
|
|
1282
|
-
super_get_response(
|
|
1283
|
-
messages=messages,
|
|
1284
|
-
stream=True,
|
|
1285
|
-
options=opts,
|
|
1286
|
-
compaction_strategy=compaction_strategy,
|
|
1287
|
-
tokenizer=tokenizer,
|
|
1288
|
-
function_invocation_kwargs=function_invocation_kwargs,
|
|
1289
|
-
client_kwargs=merged_client_kwargs,
|
|
1290
|
-
),
|
|
1291
|
-
)
|
|
1281
|
+
span = _start_streaming_span(attributes, OtelAttr.REQUEST_MODEL)
|
|
1292
1282
|
|
|
1293
|
-
# Create span directly without trace.use_span() context attachment.
|
|
1294
|
-
# Streaming spans are closed asynchronously in cleanup hooks, which run
|
|
1295
|
-
# in a different async context than creation — using use_span() would
|
|
1296
|
-
# cause "Failed to detach context" errors from OpenTelemetry.
|
|
1297
|
-
operation = attributes.get(OtelAttr.OPERATION, "operation")
|
|
1298
|
-
span_name = attributes.get(OtelAttr.REQUEST_MODEL, "unknown")
|
|
1299
|
-
span = get_tracer().start_span(f"{operation} {span_name}")
|
|
1300
|
-
span.set_attributes(attributes)
|
|
1301
1283
|
if OBSERVABILITY_SETTINGS.SENSITIVE_DATA_ENABLED and messages:
|
|
1302
1284
|
_capture_messages(
|
|
1303
1285
|
span=span,
|
|
@@ -1319,6 +1301,24 @@ class ChatTelemetryLayer(Generic[OptionsCoT]):
|
|
|
1319
1301
|
def _record_duration() -> None:
|
|
1320
1302
|
duration_state["duration"] = perf_counter() - start_time
|
|
1321
1303
|
|
|
1304
|
+
try:
|
|
1305
|
+
result_stream = cast(
|
|
1306
|
+
ResponseStream[ChatResponseUpdate, ChatResponse[Any]],
|
|
1307
|
+
super_get_response(
|
|
1308
|
+
messages=messages,
|
|
1309
|
+
stream=True,
|
|
1310
|
+
options=opts,
|
|
1311
|
+
compaction_strategy=compaction_strategy,
|
|
1312
|
+
tokenizer=tokenizer,
|
|
1313
|
+
function_invocation_kwargs=function_invocation_kwargs,
|
|
1314
|
+
client_kwargs=merged_client_kwargs,
|
|
1315
|
+
),
|
|
1316
|
+
)
|
|
1317
|
+
except Exception as exception:
|
|
1318
|
+
capture_exception(span=span, exception=exception, timestamp=time_ns())
|
|
1319
|
+
_close_span()
|
|
1320
|
+
raise
|
|
1321
|
+
|
|
1322
1322
|
async def _finalize_stream() -> None:
|
|
1323
1323
|
from ._types import ChatResponse
|
|
1324
1324
|
|
|
@@ -1357,11 +1357,18 @@ class ChatTelemetryLayer(Generic[OptionsCoT]):
|
|
|
1357
1357
|
finally:
|
|
1358
1358
|
_close_span()
|
|
1359
1359
|
|
|
1360
|
-
#
|
|
1361
|
-
#
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1360
|
+
# The pull context manager attaches the span around each underlying iterator pull so
|
|
1361
|
+
# that child spans created during the pull (e.g. HTTP requests, inner tool execution)
|
|
1362
|
+
# are parented under this chat span. Attach and detach happen in the same async
|
|
1363
|
+
# context as the pull, avoiding cross-context cleanup issues. The weakref finalizer
|
|
1364
|
+
# ensures the span is closed even if the stream is garbage collected without being
|
|
1365
|
+
# consumed.
|
|
1366
|
+
wrapped_stream: ResponseStream[ChatResponseUpdate, ChatResponse[Any]] = (
|
|
1367
|
+
result_stream
|
|
1368
|
+
.with_cleanup_hook(_record_duration)
|
|
1369
|
+
.with_cleanup_hook(_finalize_stream)
|
|
1370
|
+
.with_pull_context_manager(lambda: _activate_span(span))
|
|
1371
|
+
)
|
|
1365
1372
|
weakref.finalize(wrapped_stream, _close_span)
|
|
1366
1373
|
return wrapped_stream
|
|
1367
1374
|
|
|
@@ -1543,23 +1550,8 @@ class AgentTelemetryLayer:
|
|
|
1543
1550
|
inner_accumulated_usage_token = INNER_ACCUMULATED_USAGE.set({})
|
|
1544
1551
|
|
|
1545
1552
|
if stream:
|
|
1546
|
-
|
|
1547
|
-
run_result: object = execute()
|
|
1548
|
-
if isinstance(run_result, ResponseStream):
|
|
1549
|
-
result_stream: ResponseStream[AgentResponseUpdate, AgentResponse[Any]] = run_result # pyright: ignore[reportUnknownVariableType]
|
|
1550
|
-
elif isinstance(run_result, Awaitable):
|
|
1551
|
-
result_stream = ResponseStream.from_awaitable(run_result) # type: ignore[arg-type] # pyright: ignore[reportArgumentType]
|
|
1552
|
-
else:
|
|
1553
|
-
raise RuntimeError("Streaming telemetry requires a ResponseStream result.")
|
|
1554
|
-
except Exception:
|
|
1555
|
-
INNER_RESPONSE_TELEMETRY_CAPTURED_FIELDS.reset(inner_response_telemetry_captured_fields_token)
|
|
1556
|
-
INNER_ACCUMULATED_USAGE.reset(inner_accumulated_usage_token)
|
|
1557
|
-
raise
|
|
1553
|
+
span = _start_streaming_span(attributes, OtelAttr.AGENT_NAME)
|
|
1558
1554
|
|
|
1559
|
-
operation = attributes.get(OtelAttr.OPERATION, "operation")
|
|
1560
|
-
span_name = attributes.get(OtelAttr.AGENT_NAME, "unknown")
|
|
1561
|
-
span = get_tracer().start_span(f"{operation} {span_name}")
|
|
1562
|
-
span.set_attributes(attributes)
|
|
1563
1555
|
if OBSERVABILITY_SETTINGS.SENSITIVE_DATA_ENABLED and messages:
|
|
1564
1556
|
_capture_messages(
|
|
1565
1557
|
span=span,
|
|
@@ -1581,6 +1573,21 @@ class AgentTelemetryLayer:
|
|
|
1581
1573
|
def _record_duration() -> None:
|
|
1582
1574
|
duration_state["duration"] = perf_counter() - start_time
|
|
1583
1575
|
|
|
1576
|
+
try:
|
|
1577
|
+
run_result: object = execute()
|
|
1578
|
+
if isinstance(run_result, ResponseStream):
|
|
1579
|
+
result_stream: ResponseStream[AgentResponseUpdate, AgentResponse[Any]] = run_result # pyright: ignore[reportUnknownVariableType]
|
|
1580
|
+
elif isinstance(run_result, Awaitable):
|
|
1581
|
+
result_stream = ResponseStream.from_awaitable(run_result) # type: ignore[arg-type] # pyright: ignore[reportArgumentType]
|
|
1582
|
+
else:
|
|
1583
|
+
raise RuntimeError("Streaming telemetry requires a ResponseStream result.")
|
|
1584
|
+
except Exception as exception:
|
|
1585
|
+
capture_exception(span=span, exception=exception, timestamp=time_ns())
|
|
1586
|
+
INNER_RESPONSE_TELEMETRY_CAPTURED_FIELDS.reset(inner_response_telemetry_captured_fields_token)
|
|
1587
|
+
INNER_ACCUMULATED_USAGE.reset(inner_accumulated_usage_token)
|
|
1588
|
+
_close_span()
|
|
1589
|
+
raise
|
|
1590
|
+
|
|
1584
1591
|
async def _finalize_stream() -> None:
|
|
1585
1592
|
from ._types import AgentResponse
|
|
1586
1593
|
|
|
@@ -1620,9 +1627,18 @@ class AgentTelemetryLayer:
|
|
|
1620
1627
|
INNER_ACCUMULATED_USAGE.reset(inner_accumulated_usage_token)
|
|
1621
1628
|
_close_span()
|
|
1622
1629
|
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
).
|
|
1630
|
+
# The pull context manager attaches the span around each underlying iterator pull so
|
|
1631
|
+
# that child spans created during the pull (e.g. inner chat completion spans from the
|
|
1632
|
+
# underlying ChatTelemetryLayer) are parented under this agent invoke span. Attach and
|
|
1633
|
+
# detach happen in the same async context as the pull, avoiding cross-context cleanup
|
|
1634
|
+
# issues. The weakref finalizer ensures the span is closed even if the stream is
|
|
1635
|
+
# garbage collected without being consumed.
|
|
1636
|
+
wrapped_stream: ResponseStream[AgentResponseUpdate, AgentResponse[Any]] = (
|
|
1637
|
+
result_stream
|
|
1638
|
+
.with_cleanup_hook(_record_duration)
|
|
1639
|
+
.with_cleanup_hook(_finalize_stream)
|
|
1640
|
+
.with_pull_context_manager(lambda: _activate_span(span))
|
|
1641
|
+
)
|
|
1626
1642
|
weakref.finalize(wrapped_stream, _close_span)
|
|
1627
1643
|
return wrapped_stream
|
|
1628
1644
|
|
|
@@ -1809,6 +1825,27 @@ def get_function_span(
|
|
|
1809
1825
|
)
|
|
1810
1826
|
|
|
1811
1827
|
|
|
1828
|
+
@contextlib.contextmanager
|
|
1829
|
+
def _activate_span(span: trace.Span) -> Generator[None]:
|
|
1830
|
+
"""Attach ``span`` as the current span in the OpenTelemetry context.
|
|
1831
|
+
|
|
1832
|
+
Designed to be used as a per-pull context manager registered on a
|
|
1833
|
+
``ResponseStream`` via ``with_pull_context_manager``: it attaches the span
|
|
1834
|
+
before each underlying iterator pull and detaches immediately after, so
|
|
1835
|
+
child spans created during the pull (HTTP clients, inner chat completions,
|
|
1836
|
+
tool execution) are correctly parented under ``span``.
|
|
1837
|
+
|
|
1838
|
+
Because attach and detach happen within the same ``__anext__`` invocation
|
|
1839
|
+
(and therefore the same async task / contextvars context), there is no risk
|
|
1840
|
+
of "Failed to detach context" warnings from cross-context cleanup.
|
|
1841
|
+
"""
|
|
1842
|
+
token = otel_context.attach(trace.set_span_in_context(span))
|
|
1843
|
+
try:
|
|
1844
|
+
yield
|
|
1845
|
+
finally:
|
|
1846
|
+
otel_context.detach(token)
|
|
1847
|
+
|
|
1848
|
+
|
|
1812
1849
|
@contextlib.contextmanager
|
|
1813
1850
|
def _get_span(
|
|
1814
1851
|
attributes: dict[str, Any],
|
|
@@ -1831,6 +1868,29 @@ def _get_span(
|
|
|
1831
1868
|
yield current_span
|
|
1832
1869
|
|
|
1833
1870
|
|
|
1871
|
+
def _start_streaming_span(attributes: dict[str, Any], span_name_attribute: str) -> trace.Span:
|
|
1872
|
+
"""Start a non-current span for a streaming operation.
|
|
1873
|
+
|
|
1874
|
+
Unlike :func:`_get_span`, the returned span is not attached to the current
|
|
1875
|
+
OpenTelemetry context. The caller is responsible for:
|
|
1876
|
+
|
|
1877
|
+
- Ending the span via cleanup hooks on the wrapped
|
|
1878
|
+
:class:`~agent_framework._types.ResponseStream`.
|
|
1879
|
+
- Activating the span around each iterator pull via
|
|
1880
|
+
:func:`_activate_span` registered with ``with_pull_context_manager`` so
|
|
1881
|
+
that child spans created during stream production inherit it as parent.
|
|
1882
|
+
|
|
1883
|
+
Streaming spans are closed asynchronously in cleanup hooks that run in a
|
|
1884
|
+
different async context than creation, so attaching the span at creation
|
|
1885
|
+
time would cause "Failed to detach context" errors from OpenTelemetry.
|
|
1886
|
+
"""
|
|
1887
|
+
operation = attributes.get(OtelAttr.OPERATION, "operation")
|
|
1888
|
+
span_name = attributes.get(span_name_attribute, "unknown")
|
|
1889
|
+
span = get_tracer().start_span(f"{operation} {span_name}")
|
|
1890
|
+
span.set_attributes(attributes)
|
|
1891
|
+
return span
|
|
1892
|
+
|
|
1893
|
+
|
|
1834
1894
|
def _get_instructions_from_options(options: Any) -> str | list[str] | None:
|
|
1835
1895
|
"""Extract instructions from options dict."""
|
|
1836
1896
|
if options is None:
|
|
@@ -4,7 +4,7 @@ description = "Microsoft Agent Framework for building AI Agents with Python. Thi
|
|
|
4
4
|
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
7
|
-
version = "1.2.
|
|
7
|
+
version = "1.2.2"
|
|
8
8
|
license-files = ["LICENSE"]
|
|
9
9
|
urls.homepage = "https://aka.ms/agent-framework"
|
|
10
10
|
urls.source = "https://github.com/microsoft/agent-framework/tree/main/python"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/__init__.py
RENAMED
|
File without changes
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_agent_utils.py
RENAMED
|
File without changes
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_checkpoint.py
RENAMED
|
File without changes
|
|
File without changes
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_const.py
RENAMED
|
File without changes
|
|
File without changes
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_edge.py
RENAMED
|
File without changes
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_edge_runner.py
RENAMED
|
File without changes
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_events.py
RENAMED
|
File without changes
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_executor.py
RENAMED
|
File without changes
|
|
File without changes
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_functional.py
RENAMED
|
File without changes
|
|
File without changes
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_model_utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_state.py
RENAMED
|
File without changes
|
|
File without changes
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_validation.py
RENAMED
|
File without changes
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/_workflows/_viz.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/ag_ui/__init__.pyi
RENAMED
|
File without changes
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/amazon/__init__.py
RENAMED
|
File without changes
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/amazon/__init__.pyi
RENAMED
|
File without changes
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/anthropic/__init__.py
RENAMED
|
File without changes
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/anthropic/__init__.pyi
RENAMED
|
File without changes
|
|
File without changes
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/azure/__init__.pyi
RENAMED
|
File without changes
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/chatkit/__init__.py
RENAMED
|
File without changes
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/chatkit/__init__.pyi
RENAMED
|
File without changes
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/declarative/__init__.py
RENAMED
|
File without changes
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/declarative/__init__.pyi
RENAMED
|
File without changes
|
|
File without changes
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/devui/__init__.pyi
RENAMED
|
File without changes
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/github/__init__.py
RENAMED
|
File without changes
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/github/__init__.pyi
RENAMED
|
File without changes
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/google/__init__.py
RENAMED
|
File without changes
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/google/__init__.pyi
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/microsoft/__init__.py
RENAMED
|
File without changes
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/microsoft/__init__.pyi
RENAMED
|
File without changes
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/ollama/__init__.py
RENAMED
|
File without changes
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/ollama/__init__.pyi
RENAMED
|
File without changes
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/openai/__init__.py
RENAMED
|
File without changes
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/openai/__init__.pyi
RENAMED
|
File without changes
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/orchestrations/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agent_framework_core-1.2.0 → agent_framework_core-1.2.2}/agent_framework/redis/__init__.pyi
RENAMED
|
File without changes
|