agent-framework-core 1.15.0__tar.gz → 1.16.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/PKG-INFO +1 -1
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_agents.py +3 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_harness/_agent.py +17 -3
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_harness/_agent.pyi +2 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_harness/_background_agents.py +26 -3
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_harness/_loop.py +4 -3
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_serialization.py +2 -2
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_types.py +5 -3
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_workflows/_agent.py +1 -5
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_workflows/_checkpoint.py +9 -8
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_workflows/_state.py +17 -10
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/observability.py +406 -51
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/pyproject.toml +1 -1
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/LICENSE +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/README.md +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/__init__.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/__init__.pyi +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_agent_hooks.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_clients.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_compaction.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_docstrings.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_evaluation.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_feature_stage.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_filesystem.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_harness/__init__.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_harness/_file_access.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_harness/_file_memory.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_harness/_memory.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_harness/_mode.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_harness/_todo.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_harness/_tool_approval.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_mcp.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_middleware.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_sessions.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_settings.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_skills.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_telemetry.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_tools.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_workflows/__init__.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_workflows/_agent_executor.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_workflows/_agent_utils.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_workflows/_checkpoint_encoding.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_workflows/_const.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_workflows/_conversation_history.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_workflows/_edge.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_workflows/_edge_runner.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_workflows/_events.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_workflows/_executor.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_workflows/_function_executor.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_workflows/_functional.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_workflows/_message_utils.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_workflows/_model_utils.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_workflows/_request_info_mixin.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_workflows/_runner.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_workflows/_runner_context.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_workflows/_typing_utils.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_workflows/_validation.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_workflows/_viz.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_workflows/_workflow.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_workflows/_workflow_builder.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_workflows/_workflow_context.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_workflows/_workflow_executor.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/a2a/__init__.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/a2a/__init__.pyi +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/ag_ui/__init__.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/ag_ui/__init__.pyi +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/amazon/__init__.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/amazon/__init__.pyi +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/anthropic/__init__.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/anthropic/__init__.pyi +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/azure/__init__.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/azure/__init__.pyi +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/chatkit/__init__.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/chatkit/__init__.pyi +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/declarative/__init__.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/declarative/__init__.pyi +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/devui/__init__.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/devui/__init__.pyi +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/exceptions.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/foundry/__init__.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/foundry/__init__.pyi +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/gemini/__init__.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/gemini/__init__.pyi +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/github/__init__.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/github/__init__.pyi +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/google/__init__.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/google/__init__.pyi +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/hyperlight/__init__.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/lab/__init__.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/mem0/__init__.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/mem0/__init__.pyi +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/microsoft/__init__.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/microsoft/__init__.pyi +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/mistral/__init__.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/mistral/__init__.pyi +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/monty/__init__.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/monty/__init__.pyi +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/ollama/__init__.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/ollama/__init__.pyi +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/openai/__init__.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/openai/__init__.pyi +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/orchestrations/__init__.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/orchestrations/__init__.pyi +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/py.typed +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/redis/__init__.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/redis/__init__.pyi +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/security.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/tools/__init__.py +0 -0
- {agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/tools/__init__.pyi +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-framework-core
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.16.0
|
|
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
|
|
@@ -1512,6 +1512,9 @@ class RawAgent(BaseAgent, Generic[OptionsCoT]):
|
|
|
1512
1512
|
# _merge_options strips unset (None) options, so e.g. an unset `store` is not forwarded
|
|
1513
1513
|
# and the service decides its own default.
|
|
1514
1514
|
co = _merge_options(chat_options, run_opts)
|
|
1515
|
+
# The loop marker must remain on SessionContext.options for after_run provider
|
|
1516
|
+
# scoping, but it is framework-private metadata and must not reach the client.
|
|
1517
|
+
co.pop(_LOOP_ITERATION_TOKEN_KEY, None)
|
|
1515
1518
|
|
|
1516
1519
|
# Build session_messages from session context: context messages + input messages
|
|
1517
1520
|
session_messages: list[Message] = session_context.get_messages(include_input=True)
|
{agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_harness/_agent.py
RENAMED
|
@@ -24,7 +24,7 @@ from .._sessions import ContextProvider, HistoryProvider, InMemoryHistoryProvide
|
|
|
24
24
|
from .._skills import SkillsProvider
|
|
25
25
|
from .._telemetry import FeatureIndex, mark_feature_used
|
|
26
26
|
from .._types import ChatOptions
|
|
27
|
-
from ._background_agents import BackgroundAgentsProvider
|
|
27
|
+
from ._background_agents import DEFAULT_BACKGROUND_AGENTS_WAIT_TIMEOUT_SECONDS, BackgroundAgentsProvider
|
|
28
28
|
from ._file_access import AgentFileStore, FileAccessProvider, FileSystemAgentFileStore
|
|
29
29
|
from ._file_memory import FileMemoryProvider
|
|
30
30
|
from ._loop import DEFAULT_MAX_ITERATIONS, AgentLoopMiddleware
|
|
@@ -160,6 +160,7 @@ def _assemble_context_providers(
|
|
|
160
160
|
skills_paths: str | Path | Sequence[str | Path] | None,
|
|
161
161
|
background_agents: Sequence[SupportsAgentRun] | None,
|
|
162
162
|
background_agents_instructions: str | None,
|
|
163
|
+
background_agents_wait_timeout_seconds: int,
|
|
163
164
|
shell_context_provider: ContextProvider | None,
|
|
164
165
|
extra_context_providers: Sequence[ContextProvider] | None,
|
|
165
166
|
) -> list[ContextProvider]:
|
|
@@ -205,7 +206,13 @@ def _assemble_context_providers(
|
|
|
205
206
|
|
|
206
207
|
# Background agents are opt-in: only added when agents are provided.
|
|
207
208
|
if background_agents:
|
|
208
|
-
providers.append(
|
|
209
|
+
providers.append(
|
|
210
|
+
BackgroundAgentsProvider(
|
|
211
|
+
background_agents,
|
|
212
|
+
instructions=background_agents_instructions,
|
|
213
|
+
wait_timeout_seconds=background_agents_wait_timeout_seconds,
|
|
214
|
+
)
|
|
215
|
+
)
|
|
209
216
|
|
|
210
217
|
# Shell environment provider is opt-in: only added when a shell tool was wired.
|
|
211
218
|
if shell_context_provider is not None:
|
|
@@ -329,6 +336,7 @@ def create_harness_agent(
|
|
|
329
336
|
skills_paths: str | Path | Sequence[str | Path] | None = None,
|
|
330
337
|
background_agents: Sequence[SupportsAgentRun] | None = None,
|
|
331
338
|
background_agents_instructions: str | None = None,
|
|
339
|
+
background_agents_wait_timeout_seconds: int = DEFAULT_BACKGROUND_AGENTS_WAIT_TIMEOUT_SECONDS,
|
|
332
340
|
shell_executor: ShellExecutor | None = None,
|
|
333
341
|
shell_environment_provider_options: ShellEnvironmentProviderOptions | None = None,
|
|
334
342
|
disable_web_search: bool = False,
|
|
@@ -478,6 +486,10 @@ def create_harness_agent(
|
|
|
478
486
|
background_agents_instructions: Optional instruction override for the
|
|
479
487
|
``BackgroundAgentsProvider``. May include ``{background_agents}`` placeholder
|
|
480
488
|
which will be replaced with the agent listing.
|
|
489
|
+
background_agents_wait_timeout_seconds: Maximum seconds the background-agent wait
|
|
490
|
+
tool blocks for a task to complete. Must be a positive integer.
|
|
491
|
+
Defaults to 300 seconds. On expiry, the tool returns normally and leaves the
|
|
492
|
+
background tasks running. Only used when ``background_agents`` is provided.
|
|
481
493
|
shell_executor: Optional shell tool that enables shell command execution. When
|
|
482
494
|
provided, the shell tool and a ``ShellEnvironmentProvider`` are automatically
|
|
483
495
|
added (provided the client supports shell tools; otherwise a warning is logged
|
|
@@ -525,7 +537,8 @@ def create_harness_agent(
|
|
|
525
537
|
Raises:
|
|
526
538
|
ValueError: If max_context_window_tokens is provided and <= 0, or
|
|
527
539
|
max_output_tokens is provided and <= 0, or max_output_tokens >=
|
|
528
|
-
max_context_window_tokens when both are provided
|
|
540
|
+
max_context_window_tokens when both are provided, or
|
|
541
|
+
background_agents_wait_timeout_seconds is invalid when background agents are provided.
|
|
529
542
|
"""
|
|
530
543
|
if max_context_window_tokens is not None and max_context_window_tokens <= 0:
|
|
531
544
|
raise ValueError("max_context_window_tokens must be positive.")
|
|
@@ -601,6 +614,7 @@ def create_harness_agent(
|
|
|
601
614
|
skills_paths=skills_paths,
|
|
602
615
|
background_agents=background_agents,
|
|
603
616
|
background_agents_instructions=background_agents_instructions,
|
|
617
|
+
background_agents_wait_timeout_seconds=background_agents_wait_timeout_seconds,
|
|
604
618
|
shell_context_provider=shell_provider,
|
|
605
619
|
extra_context_providers=context_providers,
|
|
606
620
|
)
|
{agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_harness/_agent.pyi
RENAMED
|
@@ -14,6 +14,7 @@ from .._sessions import ContextProvider, HistoryProvider
|
|
|
14
14
|
from .._skills import SkillsProvider
|
|
15
15
|
from .._tools import ToolTypes
|
|
16
16
|
from .._types import ChatOptions
|
|
17
|
+
from ._background_agents import DEFAULT_BACKGROUND_AGENTS_WAIT_TIMEOUT_SECONDS
|
|
17
18
|
from ._file_access import AgentFileStore
|
|
18
19
|
from ._loop import DEFAULT_MAX_ITERATIONS, NextMessageCallable, ShouldContinueCallable
|
|
19
20
|
from ._mode import AgentModeProvider
|
|
@@ -77,6 +78,7 @@ def create_harness_agent(
|
|
|
77
78
|
skills_paths: str | Path | Sequence[str | Path] | None = None,
|
|
78
79
|
background_agents: Sequence[SupportsAgentRun] | None = None,
|
|
79
80
|
background_agents_instructions: str | None = None,
|
|
81
|
+
background_agents_wait_timeout_seconds: int = DEFAULT_BACKGROUND_AGENTS_WAIT_TIMEOUT_SECONDS,
|
|
80
82
|
shell_executor: _ShellExecutorLike | None = None,
|
|
81
83
|
shell_environment_provider_options: _ShellEnvironmentProviderOptionsLike | None = None,
|
|
82
84
|
disable_web_search: bool = False,
|
|
@@ -27,6 +27,7 @@ from .._types import AgentResponse, Message
|
|
|
27
27
|
logger = logging.getLogger(__name__)
|
|
28
28
|
|
|
29
29
|
DEFAULT_BACKGROUND_AGENTS_SOURCE_ID = "background_agents"
|
|
30
|
+
DEFAULT_BACKGROUND_AGENTS_WAIT_TIMEOUT_SECONDS = 300
|
|
30
31
|
|
|
31
32
|
DEFAULT_BACKGROUND_AGENTS_INSTRUCTIONS = """\
|
|
32
33
|
## Background Agents
|
|
@@ -275,7 +276,8 @@ class BackgroundAgentsProvider(ContextProvider):
|
|
|
275
276
|
This provider exposes the following tools to the agent:
|
|
276
277
|
|
|
277
278
|
- ``background_agents_start_task`` — Start a background task on a named agent with text input.
|
|
278
|
-
- ``background_agents_wait_for_first_completion`` —
|
|
279
|
+
- ``background_agents_wait_for_first_completion`` — Wait until the first specified task completes or the
|
|
280
|
+
configured timeout expires. A timeout leaves the tasks running so the tool can be called again.
|
|
279
281
|
- ``background_agents_get_task_results`` — Retrieve the text output of a completed background task.
|
|
280
282
|
- ``background_agents_get_all_tasks`` — List all background tasks with their IDs, statuses, and descriptions.
|
|
281
283
|
- ``background_agents_continue_task`` — Send follow-up input to a completed task's session to resume work.
|
|
@@ -297,6 +299,7 @@ class BackgroundAgentsProvider(ContextProvider):
|
|
|
297
299
|
*,
|
|
298
300
|
source_id: str = DEFAULT_BACKGROUND_AGENTS_SOURCE_ID,
|
|
299
301
|
instructions: str | None = None,
|
|
302
|
+
wait_timeout_seconds: int = DEFAULT_BACKGROUND_AGENTS_WAIT_TIMEOUT_SECONDS,
|
|
300
303
|
) -> None:
|
|
301
304
|
"""Initialize the background agents provider.
|
|
302
305
|
|
|
@@ -312,13 +315,23 @@ class BackgroundAgentsProvider(ContextProvider):
|
|
|
312
315
|
source_id: Unique source ID for serializable task state in session.
|
|
313
316
|
instructions: Optional instruction override. May include ``{background_agents}``
|
|
314
317
|
placeholder which will be replaced with the agent listing.
|
|
318
|
+
wait_timeout_seconds: Maximum seconds the wait tool blocks for a task to complete.
|
|
319
|
+
Must be a positive integer. Defaults to 300 seconds.
|
|
315
320
|
|
|
316
321
|
Raises:
|
|
317
|
-
ValueError: If agents is empty, an agent has no name,
|
|
322
|
+
ValueError: If agents is empty, an agent has no name, names are not unique, or
|
|
323
|
+
wait_timeout_seconds is not a positive integer.
|
|
318
324
|
"""
|
|
319
325
|
super().__init__(source_id)
|
|
320
326
|
|
|
321
327
|
self._agents = _validate_and_build_agent_dict(agents)
|
|
328
|
+
if (
|
|
329
|
+
isinstance(wait_timeout_seconds, bool)
|
|
330
|
+
or not isinstance(wait_timeout_seconds, int)
|
|
331
|
+
or wait_timeout_seconds <= 0
|
|
332
|
+
):
|
|
333
|
+
raise ValueError("wait_timeout_seconds must be a positive integer.")
|
|
334
|
+
self._wait_timeout_seconds = wait_timeout_seconds
|
|
322
335
|
|
|
323
336
|
# Build instructions with agent listing.
|
|
324
337
|
base_instructions = instructions if instructions is not None else DEFAULT_BACKGROUND_AGENTS_INSTRUCTIONS
|
|
@@ -502,7 +515,11 @@ class BackgroundAgentsProvider(ContextProvider):
|
|
|
502
515
|
|
|
503
516
|
@tool(name="background_agents_wait_for_first_completion", approval_mode="never_require")
|
|
504
517
|
async def background_agents_wait_for_first_completion(task_ids: list[int]) -> str:
|
|
505
|
-
"""
|
|
518
|
+
"""Wait until the first of the specified tasks completes or the configured timeout expires.
|
|
519
|
+
|
|
520
|
+
Returns the completed task's ID. On timeout, the tasks remain running and this tool
|
|
521
|
+
can be called again to continue waiting.
|
|
522
|
+
"""
|
|
506
523
|
if runtime.closed:
|
|
507
524
|
return "Error: Session is being released; cannot wait for background tasks."
|
|
508
525
|
|
|
@@ -531,8 +548,14 @@ class BackgroundAgentsProvider(ContextProvider):
|
|
|
531
548
|
# Wait for the first one to complete.
|
|
532
549
|
done, _ = await asyncio.wait(
|
|
533
550
|
[t for _, t in waitable],
|
|
551
|
+
timeout=self._wait_timeout_seconds,
|
|
534
552
|
return_when=asyncio.FIRST_COMPLETED,
|
|
535
553
|
)
|
|
554
|
+
if not done:
|
|
555
|
+
return (
|
|
556
|
+
f"No background task completed within {self._wait_timeout_seconds} seconds. "
|
|
557
|
+
"The tasks are still running; call this tool again if you wish to continue waiting."
|
|
558
|
+
)
|
|
536
559
|
|
|
537
560
|
# Find which ID completed.
|
|
538
561
|
completed_id: int | None = None
|
{agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_harness/_loop.py
RENAMED
|
@@ -34,7 +34,9 @@ from typing import TYPE_CHECKING, Any, TypeAlias
|
|
|
34
34
|
from pydantic import BaseModel, Field
|
|
35
35
|
from typing_extensions import Self
|
|
36
36
|
|
|
37
|
-
from .._agents import
|
|
37
|
+
from .._agents import (
|
|
38
|
+
_LOOP_ITERATION_TOKEN_KEY, # pyright: ignore[reportPrivateUsage] -- shared loop-turn marker, see _agents.py
|
|
39
|
+
)
|
|
38
40
|
from .._feature_stage import ExperimentalFeature, experimental
|
|
39
41
|
from .._middleware import AgentContext, AgentMiddleware, MiddlewareTermination
|
|
40
42
|
from .._sessions import SessionContext
|
|
@@ -490,8 +492,7 @@ class AgentLoopMiddleware(AgentMiddleware):
|
|
|
490
492
|
if response is None or run_after is None:
|
|
491
493
|
return
|
|
492
494
|
if not any(
|
|
493
|
-
getattr(provider, "after_run_once_per_turn", False)
|
|
494
|
-
for provider in getattr(agent, "context_providers", [])
|
|
495
|
+
getattr(provider, "after_run_once_per_turn", False) for provider in getattr(agent, "context_providers", [])
|
|
495
496
|
):
|
|
496
497
|
return
|
|
497
498
|
session_context = SessionContext(
|
{agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_serialization.py
RENAMED
|
@@ -569,7 +569,7 @@ class SerializationMixin:
|
|
|
569
569
|
and param_name in kwargs
|
|
570
570
|
and isinstance(kwargs[param_name], dict)
|
|
571
571
|
):
|
|
572
|
-
kwargs[param_name]
|
|
572
|
+
kwargs[param_name] = {**kwargs[param_name], **param_value}
|
|
573
573
|
else:
|
|
574
574
|
kwargs[param_name] = param_value
|
|
575
575
|
else:
|
|
@@ -581,7 +581,7 @@ class SerializationMixin:
|
|
|
581
581
|
)
|
|
582
582
|
# Handle dict parameters - merge if both are dicts
|
|
583
583
|
if isinstance(dep_value, dict) and dep_key in kwargs and isinstance(kwargs[dep_key], dict):
|
|
584
|
-
kwargs[dep_key]
|
|
584
|
+
kwargs[dep_key] = {**kwargs[dep_key], **dep_value}
|
|
585
585
|
else:
|
|
586
586
|
kwargs[dep_key] = dep_value
|
|
587
587
|
|
|
@@ -589,10 +589,10 @@ class Content:
|
|
|
589
589
|
self.consent_link = consent_link
|
|
590
590
|
|
|
591
591
|
def __deepcopy__(self, memo: dict[int, Any]) -> Content:
|
|
592
|
-
"""Create a deep copy,
|
|
592
|
+
"""Create a deep copy, discarding non-``None`` ``_SHALLOW_COPY_FIELDS``.
|
|
593
593
|
|
|
594
594
|
Fields listed in ``_SHALLOW_COPY_FIELDS`` may contain LLM SDK objects
|
|
595
|
-
(e.g., proto/gRPC responses) that are not safe to deep-copy.
|
|
595
|
+
(e.g., proto/gRPC responses) that are not safe to deep-copy or share.
|
|
596
596
|
"""
|
|
597
597
|
cls = type(self)
|
|
598
598
|
result = cls.__new__(cls)
|
|
@@ -600,7 +600,9 @@ class Content:
|
|
|
600
600
|
shallow = cls._SHALLOW_COPY_FIELDS
|
|
601
601
|
for k, v in self.__dict__.items():
|
|
602
602
|
if k in shallow:
|
|
603
|
-
|
|
603
|
+
if v is not None:
|
|
604
|
+
logger.debug("Discarding field '%s' while deep-copying Content.", k)
|
|
605
|
+
object.__setattr__(result, k, None)
|
|
604
606
|
else:
|
|
605
607
|
object.__setattr__(result, k, deepcopy(v, memo))
|
|
606
608
|
return result
|
{agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_workflows/_agent.py
RENAMED
|
@@ -713,11 +713,7 @@ class WorkflowAgent(BaseAgent):
|
|
|
713
713
|
Note:
|
|
714
714
|
Text requests use the function-call envelope so callers can reply with a matching function result.
|
|
715
715
|
"""
|
|
716
|
-
if (
|
|
717
|
-
isinstance(event.data, Content)
|
|
718
|
-
and event.data.user_input_request
|
|
719
|
-
and event.data.type != "text"
|
|
720
|
-
):
|
|
716
|
+
if isinstance(event.data, Content) and event.data.user_input_request and event.data.type != "text":
|
|
721
717
|
# Preserve specialized requests that callers already understand how to present.
|
|
722
718
|
return event.data
|
|
723
719
|
|
|
@@ -130,7 +130,7 @@ class CheckpointStorage(Protocol):
|
|
|
130
130
|
"""Protocol for checkpoint storage backends."""
|
|
131
131
|
|
|
132
132
|
async def save(self, checkpoint: WorkflowCheckpoint) -> CheckpointID:
|
|
133
|
-
"""
|
|
133
|
+
"""Create a copy of the given checkpoint and store it, returning its ID.
|
|
134
134
|
|
|
135
135
|
Args:
|
|
136
136
|
checkpoint: The WorkflowCheckpoint object to save.
|
|
@@ -147,7 +147,7 @@ class CheckpointStorage(Protocol):
|
|
|
147
147
|
checkpoint_id: The unique ID of the checkpoint to load.
|
|
148
148
|
|
|
149
149
|
Returns:
|
|
150
|
-
|
|
150
|
+
A copy of the WorkflowCheckpoint object corresponding to the given ID.
|
|
151
151
|
|
|
152
152
|
Raises:
|
|
153
153
|
WorkflowCheckpointException: If no checkpoint with the given ID exists.
|
|
@@ -161,7 +161,7 @@ class CheckpointStorage(Protocol):
|
|
|
161
161
|
workflow_name: The name of the workflow to list checkpoints for.
|
|
162
162
|
|
|
163
163
|
Returns:
|
|
164
|
-
A list of WorkflowCheckpoint objects for the specified workflow name.
|
|
164
|
+
A list of copies of WorkflowCheckpoint objects for the specified workflow name.
|
|
165
165
|
"""
|
|
166
166
|
...
|
|
167
167
|
|
|
@@ -183,7 +183,8 @@ class CheckpointStorage(Protocol):
|
|
|
183
183
|
workflow_name: The name of the workflow to get the latest checkpoint for.
|
|
184
184
|
|
|
185
185
|
Returns:
|
|
186
|
-
|
|
186
|
+
A copy of the latest WorkflowCheckpoint object for the specified workflow name,
|
|
187
|
+
or None if no checkpoints exist.
|
|
187
188
|
"""
|
|
188
189
|
...
|
|
189
190
|
|
|
@@ -207,7 +208,7 @@ class InMemoryCheckpointStorage:
|
|
|
207
208
|
self._checkpoints: dict[CheckpointID, WorkflowCheckpoint] = {}
|
|
208
209
|
|
|
209
210
|
async def save(self, checkpoint: WorkflowCheckpoint) -> CheckpointID:
|
|
210
|
-
"""
|
|
211
|
+
"""Create a copy of the given checkpoint and store it, returning its ID."""
|
|
211
212
|
self._checkpoints[checkpoint.checkpoint_id] = copy.deepcopy(checkpoint)
|
|
212
213
|
logger.debug(f"Saved checkpoint {checkpoint.checkpoint_id} to memory")
|
|
213
214
|
return checkpoint.checkpoint_id
|
|
@@ -217,12 +218,12 @@ class InMemoryCheckpointStorage:
|
|
|
217
218
|
checkpoint = self._checkpoints.get(checkpoint_id)
|
|
218
219
|
if checkpoint:
|
|
219
220
|
logger.debug(f"Loaded checkpoint {checkpoint_id} from memory")
|
|
220
|
-
return checkpoint
|
|
221
|
+
return copy.deepcopy(checkpoint)
|
|
221
222
|
raise WorkflowCheckpointException(f"No checkpoint found with ID {checkpoint_id}")
|
|
222
223
|
|
|
223
224
|
async def list_checkpoints(self, *, workflow_name: str) -> list[WorkflowCheckpoint]:
|
|
224
225
|
"""List checkpoint objects for a given workflow name."""
|
|
225
|
-
return [cp for cp in self._checkpoints.values() if cp.workflow_name == workflow_name]
|
|
226
|
+
return [copy.deepcopy(cp) for cp in self._checkpoints.values() if cp.workflow_name == workflow_name]
|
|
226
227
|
|
|
227
228
|
async def delete(self, checkpoint_id: CheckpointID) -> bool:
|
|
228
229
|
"""Delete a checkpoint by ID."""
|
|
@@ -239,7 +240,7 @@ class InMemoryCheckpointStorage:
|
|
|
239
240
|
return None
|
|
240
241
|
latest_checkpoint = max(checkpoints, key=lambda cp: datetime.fromisoformat(cp.timestamp))
|
|
241
242
|
logger.debug(f"Latest checkpoint for workflow {workflow_name} is {latest_checkpoint.checkpoint_id}")
|
|
242
|
-
return latest_checkpoint
|
|
243
|
+
return copy.deepcopy(latest_checkpoint)
|
|
243
244
|
|
|
244
245
|
async def list_checkpoint_ids(self, *, workflow_name: str) -> list[CheckpointID]:
|
|
245
246
|
"""List checkpoint IDs. If workflow_id is provided, filter by that workflow."""
|
{agent_framework_core-1.15.0 → agent_framework_core-1.16.0}/agent_framework/_workflows/_state.py
RENAMED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# Copyright (c) Microsoft. All rights reserved.
|
|
2
2
|
|
|
3
|
+
import copy
|
|
3
4
|
from typing import Any
|
|
4
5
|
|
|
5
6
|
|
|
@@ -31,7 +32,8 @@ class State:
|
|
|
31
32
|
"""Set a value in the pending state buffer.
|
|
32
33
|
|
|
33
34
|
The value will be visible to subsequent `get()` calls but won't be
|
|
34
|
-
committed to the actual state until `commit()` is called.
|
|
35
|
+
committed to the actual state until `commit()` is called. A deep copy is
|
|
36
|
+
stored so later caller mutations do not change pending state.
|
|
35
37
|
|
|
36
38
|
Note:
|
|
37
39
|
When multiple executors run concurrently within the same superstep,
|
|
@@ -40,7 +42,7 @@ class State:
|
|
|
40
42
|
the .NET behavior and the superstep execution model where all executors
|
|
41
43
|
in a superstep see the same committed state at the start.
|
|
42
44
|
"""
|
|
43
|
-
self._pending[key] = value
|
|
45
|
+
self._pending[key] = copy.deepcopy(value)
|
|
44
46
|
|
|
45
47
|
def get(self, key: str, default: Any = None) -> Any:
|
|
46
48
|
"""Get a value from state, checking pending first then committed.
|
|
@@ -50,14 +52,17 @@ class State:
|
|
|
50
52
|
default: Value to return if key is not found. Defaults to None.
|
|
51
53
|
|
|
52
54
|
Returns:
|
|
53
|
-
|
|
55
|
+
A deep copy of the value if found, otherwise the default value. Mutate
|
|
56
|
+
the returned value and pass it to :meth:`set` to update workflow state.
|
|
54
57
|
"""
|
|
55
58
|
if key in self._pending:
|
|
56
59
|
value = self._pending[key]
|
|
57
60
|
if value is _DeleteSentinel:
|
|
58
61
|
return default
|
|
59
|
-
return value
|
|
60
|
-
|
|
62
|
+
return copy.deepcopy(value)
|
|
63
|
+
if key in self._committed:
|
|
64
|
+
return copy.deepcopy(self._committed[key])
|
|
65
|
+
return default
|
|
61
66
|
|
|
62
67
|
def has(self, key: str) -> bool:
|
|
63
68
|
"""Check if a key exists in pending or committed state."""
|
|
@@ -104,18 +109,20 @@ class State:
|
|
|
104
109
|
self._pending.clear()
|
|
105
110
|
|
|
106
111
|
def export_state(self) -> dict[str, Any]:
|
|
107
|
-
"""Export a
|
|
112
|
+
"""Export a deepcopy of the committed state.
|
|
108
113
|
|
|
109
|
-
Note:
|
|
114
|
+
Note:
|
|
115
|
+
Does not include pending changes. Values must support :func:`copy.deepcopy`.
|
|
110
116
|
"""
|
|
111
|
-
return
|
|
117
|
+
return copy.deepcopy(self._committed)
|
|
112
118
|
|
|
113
119
|
def import_state(self, state: dict[str, Any]) -> None:
|
|
114
120
|
"""Import state from a serialized dictionary.
|
|
115
121
|
|
|
116
|
-
Merges into committed state. Does not affect pending changes.
|
|
122
|
+
Merges a deepcopy into committed state. Does not affect pending changes.
|
|
123
|
+
Values must support :func:`copy.deepcopy`.
|
|
117
124
|
"""
|
|
118
|
-
self._committed.update(state)
|
|
125
|
+
self._committed.update(copy.deepcopy(state))
|
|
119
126
|
|
|
120
127
|
|
|
121
128
|
class _DeleteSentinelType:
|