agent-framework-core 1.20.0__tar.gz → 1.22.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.20.0 → agent_framework_core-1.22.0}/PKG-INFO +37 -30
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/README.md +6 -1
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/__init__.py +6 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/__init__.pyi +6 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_agent_hooks.py +2 -38
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_agents.py +49 -6
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_compaction.py +3 -5
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_harness/_file_access.py +59 -16
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_harness/_file_memory.py +9 -9
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_harness/_memory.py +2 -2
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_harness/_tool_approval.py +35 -4
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_mcp.py +379 -101
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_middleware.py +32 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_serialization.py +9 -3
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_sessions.py +2 -1
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_skills.py +5 -6
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_tools.py +908 -180
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_types.py +843 -127
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_workflows/_agent.py +34 -26
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_workflows/_agent_executor.py +1 -1
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_workflows/_edge.py +2 -1
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_workflows/_events.py +7 -5
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_workflows/_executor.py +7 -1
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_workflows/_functional.py +651 -53
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_workflows/_runner.py +45 -38
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_workflows/_typing_utils.py +23 -2
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_workflows/_viz.py +82 -33
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_workflows/_workflow.py +12 -5
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_workflows/_workflow_builder.py +5 -3
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_workflows/_workflow_executor.py +15 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/observability.py +73 -28
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/security.py +316 -17
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/pyproject.toml +30 -29
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/LICENSE +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_clients.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_docstrings.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_evaluation.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_feature_stage.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_filesystem.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_harness/__init__.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_harness/_agent.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_harness/_agent.pyi +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_harness/_background_agents.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_harness/_loop.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_harness/_mode.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_harness/_todo.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_in_memory.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_settings.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_telemetry.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_vector_filters.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_vectors.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_workflows/__init__.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_workflows/_agent_utils.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_workflows/_checkpoint.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_workflows/_checkpoint_encoding.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_workflows/_const.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_workflows/_conversation_history.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_workflows/_edge_runner.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_workflows/_function_executor.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_workflows/_message_utils.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_workflows/_model_utils.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_workflows/_request_info_mixin.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_workflows/_runner_context.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_workflows/_state.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_workflows/_validation.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_workflows/_workflow_context.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/a2a/__init__.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/a2a/__init__.pyi +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/ag_ui/__init__.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/ag_ui/__init__.pyi +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/amazon/__init__.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/amazon/__init__.pyi +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/anthropic/__init__.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/anthropic/__init__.pyi +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/azure/__init__.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/azure/__init__.pyi +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/chatkit/__init__.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/chatkit/__init__.pyi +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/declarative/__init__.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/declarative/__init__.pyi +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/devui/__init__.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/devui/__init__.pyi +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/exceptions.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/foundry/__init__.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/foundry/__init__.pyi +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/gemini/__init__.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/gemini/__init__.pyi +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/github/__init__.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/github/__init__.pyi +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/google/__init__.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/google/__init__.pyi +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/hyperlight/__init__.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/lab/__init__.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/mem0/__init__.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/mem0/__init__.pyi +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/microsoft/__init__.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/microsoft/__init__.pyi +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/mistral/__init__.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/mistral/__init__.pyi +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/monty/__init__.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/monty/__init__.pyi +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/ollama/__init__.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/ollama/__init__.pyi +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/openai/__init__.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/openai/__init__.pyi +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/orchestrations/__init__.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/orchestrations/__init__.pyi +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/py.typed +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/redis/__init__.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/redis/__init__.pyi +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/tools/__init__.py +0 -0
- {agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/tools/__init__.pyi +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: agent-framework-core
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.22.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
|
|
@@ -14,6 +14,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.13
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
17
18
|
Classifier: Typing :: Typed
|
|
18
19
|
License-File: LICENSE
|
|
19
20
|
Requires-Dist: msgspec>=0.20.0,<0.23
|
|
@@ -24,40 +25,41 @@ Requires-Dist: opentelemetry-api>=1.39.0,<2
|
|
|
24
25
|
Requires-Dist: pyyaml>=6.0,<7.0
|
|
25
26
|
Requires-Dist: regex>=2024.11.6
|
|
26
27
|
Requires-Dist: mcp>=1.24.0,<2 ; extra == "all"
|
|
27
|
-
Requires-Dist: agent-framework-a2a>=1.0.
|
|
28
|
-
Requires-Dist: agent-framework-ag-ui>=1.
|
|
29
|
-
Requires-Dist: agent-framework-anthropic>=1.0.
|
|
30
|
-
Requires-Dist: agent-framework-azure-ai-search>=1.0.
|
|
31
|
-
Requires-Dist: agent-framework-azure-contentunderstanding>=1.0.
|
|
32
|
-
Requires-Dist: agent-framework-azure-cosmos>=1.0.
|
|
28
|
+
Requires-Dist: agent-framework-a2a>=1.0.0b261015,<2 ; extra == "all"
|
|
29
|
+
Requires-Dist: agent-framework-ag-ui>=1.6.0,<2 ; extra == "all"
|
|
30
|
+
Requires-Dist: agent-framework-anthropic>=1.0.0b261015,<2 ; extra == "all"
|
|
31
|
+
Requires-Dist: agent-framework-azure-ai-search>=1.0.0b261015,<2 ; extra == "all"
|
|
32
|
+
Requires-Dist: agent-framework-azure-contentunderstanding>=1.0.0b261015,<2 ; extra == "all"
|
|
33
|
+
Requires-Dist: agent-framework-azure-cosmos>=1.0.0b261015,<2 ; extra == "all"
|
|
33
34
|
Requires-Dist: agent-framework-azurefunctions>=1.0.0b260922,<2 ; extra == "all"
|
|
34
|
-
Requires-Dist: agent-framework-bedrock>=1.0.
|
|
35
|
-
Requires-Dist: agent-framework-chatkit>=1.0.
|
|
36
|
-
Requires-Dist: agent-framework-claude>=1.0.
|
|
37
|
-
Requires-Dist: agent-framework-copilotstudio>=1.0.
|
|
38
|
-
Requires-Dist: agent-framework-declarative>=1.
|
|
39
|
-
Requires-Dist: agent-framework-devui>=1.0.
|
|
35
|
+
Requires-Dist: agent-framework-bedrock>=1.0.0b261015,<2 ; extra == "all"
|
|
36
|
+
Requires-Dist: agent-framework-chatkit>=1.0.0b261015,<2 ; extra == "all"
|
|
37
|
+
Requires-Dist: agent-framework-claude>=1.0.0b261015,<2 ; extra == "all"
|
|
38
|
+
Requires-Dist: agent-framework-copilotstudio>=1.0.0b261015,<2 ; extra == "all"
|
|
39
|
+
Requires-Dist: agent-framework-declarative>=1.3.0,<2 ; extra == "all"
|
|
40
|
+
Requires-Dist: agent-framework-devui>=1.0.0b261015,<2 ; extra == "all"
|
|
40
41
|
Requires-Dist: agent-framework-durabletask>=1.0.0b260922,<2 ; extra == "all"
|
|
41
|
-
Requires-Dist: agent-framework-foundry>=1.
|
|
42
|
-
Requires-Dist: agent-framework-foundry-hosting>=1.0.
|
|
43
|
-
Requires-Dist: agent-framework-foundry-local>=1.0.
|
|
44
|
-
Requires-Dist: agent-framework-gemini>=1.0.
|
|
45
|
-
Requires-Dist: agent-framework-github-copilot>=2.0
|
|
46
|
-
Requires-Dist: agent-framework-hyperlight>=1.0.
|
|
47
|
-
Requires-Dist: agent-framework-mem0>=1.0.
|
|
48
|
-
Requires-Dist: agent-framework-mistral>=1.0.
|
|
49
|
-
Requires-Dist: agent-framework-monty>=1.0.
|
|
50
|
-
Requires-Dist: agent-framework-ollama>=1.0.
|
|
51
|
-
Requires-Dist: agent-framework-openai>=1.
|
|
52
|
-
Requires-Dist: agent-framework-orchestrations>=1.3.
|
|
53
|
-
Requires-Dist: agent-framework-purview>=1.0.
|
|
54
|
-
Requires-Dist: agent-framework-redis>=1.0.
|
|
55
|
-
Requires-Dist: agent-framework-tools>=1.0.
|
|
42
|
+
Requires-Dist: agent-framework-foundry>=1.15.0,<2 ; extra == "all"
|
|
43
|
+
Requires-Dist: agent-framework-foundry-hosting>=1.0.0b261015,<2 ; extra == "all"
|
|
44
|
+
Requires-Dist: agent-framework-foundry-local>=1.0.0b261015,<2 ; extra == "all"
|
|
45
|
+
Requires-Dist: agent-framework-gemini>=1.0.0b261015,<2 ; extra == "all"
|
|
46
|
+
Requires-Dist: agent-framework-github-copilot>=2.1.0,<3 ; extra == "all" and ( python_version >= '3.11')
|
|
47
|
+
Requires-Dist: agent-framework-hyperlight>=1.0.0b261015,<2 ; extra == "all" and ( python_version < '3.15' and ((sys_platform == 'linux' and platform_machine == 'x86_64') or (sys_platform == 'win32' and platform_machine == 'AMD64')))
|
|
48
|
+
Requires-Dist: agent-framework-mem0>=1.0.0b261015,<2 ; extra == "all"
|
|
49
|
+
Requires-Dist: agent-framework-mistral>=1.0.0b261015,<2 ; extra == "all"
|
|
50
|
+
Requires-Dist: agent-framework-monty>=1.0.0b261015,<2 ; extra == "all"
|
|
51
|
+
Requires-Dist: agent-framework-ollama>=1.0.0b261015,<2 ; extra == "all"
|
|
52
|
+
Requires-Dist: agent-framework-openai>=1.17.0,<2 ; extra == "all"
|
|
53
|
+
Requires-Dist: agent-framework-orchestrations>=1.3.2,<2 ; extra == "all"
|
|
54
|
+
Requires-Dist: agent-framework-purview>=1.0.0b261015,<2 ; extra == "all"
|
|
55
|
+
Requires-Dist: agent-framework-redis>=1.0.0b261015,<2 ; extra == "all" and ( python_version < '3.15')
|
|
56
|
+
Requires-Dist: agent-framework-tools>=1.0.0b261015,<2 ; extra == "all"
|
|
56
57
|
Project-URL: homepage, https://aka.ms/agent-framework
|
|
57
58
|
Project-URL: issues, https://github.com/microsoft/agent-framework/issues
|
|
58
59
|
Project-URL: release_notes, https://github.com/microsoft/agent-framework/releases?q=tag%3Apython-1&expanded=true
|
|
59
60
|
Project-URL: source, https://github.com/microsoft/agent-framework/tree/main/python
|
|
60
61
|
Provides-Extra: all
|
|
62
|
+
Import-Name: agent_framework
|
|
61
63
|
|
|
62
64
|
# Get Started with Microsoft Agent Framework
|
|
63
65
|
|
|
@@ -83,9 +85,14 @@ pip install agent-framework-openai
|
|
|
83
85
|
|
|
84
86
|
Supported Platforms:
|
|
85
87
|
|
|
86
|
-
- Python: 3.10
|
|
88
|
+
- Python: 3.10–3.15
|
|
87
89
|
- OS: Windows, macOS, Linux
|
|
88
90
|
|
|
91
|
+
Python 3.10 remains supported and tested for this release, but it is deprecated
|
|
92
|
+
because upstream Python 3.10 has reached end of life. New environments should
|
|
93
|
+
use Python 3.11 or later. Removing Python 3.10 support will be handled in a
|
|
94
|
+
future release rather than by this compatibility update.
|
|
95
|
+
|
|
89
96
|
## 1. Setup API Keys
|
|
90
97
|
|
|
91
98
|
Depending on the client you want to use, there are various environment variables you can set to configure the chat clients. This can be done in the environment itself, or with a `.env` file in your project root, some examples of environment variables include:
|
|
@@ -22,9 +22,14 @@ pip install agent-framework-openai
|
|
|
22
22
|
|
|
23
23
|
Supported Platforms:
|
|
24
24
|
|
|
25
|
-
- Python: 3.10
|
|
25
|
+
- Python: 3.10–3.15
|
|
26
26
|
- OS: Windows, macOS, Linux
|
|
27
27
|
|
|
28
|
+
Python 3.10 remains supported and tested for this release, but it is deprecated
|
|
29
|
+
because upstream Python 3.10 has reached end of life. New environments should
|
|
30
|
+
use Python 3.11 or later. Removing Python 3.10 support will be handled in a
|
|
31
|
+
future release rather than by this compatibility update.
|
|
32
|
+
|
|
28
33
|
## 1. Setup API Keys
|
|
29
34
|
|
|
30
35
|
Depending on the client you want to use, there are various environment variables you can set to configure the chat clients. This can be done in the environment itself, or with a `.env` file in your project root, some examples of environment variables include:
|
|
@@ -277,12 +277,15 @@ _LAZY_MODULE_EXPORTS: Final[Mapping[str, tuple[str, ...]]] = {
|
|
|
277
277
|
"UsageDetails",
|
|
278
278
|
"add_usage_details",
|
|
279
279
|
"detect_media_type_from_base64",
|
|
280
|
+
"get_namespaced_value",
|
|
280
281
|
"map_chat_to_agent_update",
|
|
282
|
+
"merge_additional_properties",
|
|
281
283
|
"merge_chat_options",
|
|
282
284
|
"normalize_messages",
|
|
283
285
|
"normalize_tools",
|
|
284
286
|
"prepend_instructions_to_messages",
|
|
285
287
|
"validate_chat_options",
|
|
288
|
+
"with_namespaced_value",
|
|
286
289
|
"validate_tool_mode",
|
|
287
290
|
"validate_tools",
|
|
288
291
|
),
|
|
@@ -696,6 +699,7 @@ __all__ = [
|
|
|
696
699
|
"executor",
|
|
697
700
|
"function_middleware",
|
|
698
701
|
"get_agent_mode",
|
|
702
|
+
"get_namespaced_value",
|
|
699
703
|
"get_run_context",
|
|
700
704
|
"handler",
|
|
701
705
|
"included_messages",
|
|
@@ -703,6 +707,7 @@ __all__ = [
|
|
|
703
707
|
"keyword_check",
|
|
704
708
|
"load_settings",
|
|
705
709
|
"map_chat_to_agent_update",
|
|
710
|
+
"merge_additional_properties",
|
|
706
711
|
"merge_chat_options",
|
|
707
712
|
"normalize_function_invocation_configuration",
|
|
708
713
|
"normalize_messages",
|
|
@@ -727,6 +732,7 @@ __all__ = [
|
|
|
727
732
|
"validate_tools",
|
|
728
733
|
"validate_workflow_graph",
|
|
729
734
|
"vectorstoremodel",
|
|
735
|
+
"with_namespaced_value",
|
|
730
736
|
"workflow",
|
|
731
737
|
]
|
|
732
738
|
|
|
@@ -243,7 +243,9 @@ from ._types import (
|
|
|
243
243
|
UsageDetails,
|
|
244
244
|
add_usage_details,
|
|
245
245
|
detect_media_type_from_base64,
|
|
246
|
+
get_namespaced_value,
|
|
246
247
|
map_chat_to_agent_update,
|
|
248
|
+
merge_additional_properties,
|
|
247
249
|
merge_chat_options,
|
|
248
250
|
normalize_messages,
|
|
249
251
|
normalize_tools,
|
|
@@ -251,6 +253,7 @@ from ._types import (
|
|
|
251
253
|
validate_chat_options,
|
|
252
254
|
validate_tool_mode,
|
|
253
255
|
validate_tools,
|
|
256
|
+
with_namespaced_value,
|
|
254
257
|
)
|
|
255
258
|
from ._vector_filters import Filter, FilterGroup, FilterGroupOperator, FilterOperator, Param
|
|
256
259
|
from ._vectors import (
|
|
@@ -651,6 +654,7 @@ __all__ = [
|
|
|
651
654
|
"executor",
|
|
652
655
|
"function_middleware",
|
|
653
656
|
"get_agent_mode",
|
|
657
|
+
"get_namespaced_value",
|
|
654
658
|
"get_run_context",
|
|
655
659
|
"handler",
|
|
656
660
|
"included_messages",
|
|
@@ -658,6 +662,7 @@ __all__ = [
|
|
|
658
662
|
"keyword_check",
|
|
659
663
|
"load_settings",
|
|
660
664
|
"map_chat_to_agent_update",
|
|
665
|
+
"merge_additional_properties",
|
|
661
666
|
"merge_chat_options",
|
|
662
667
|
"normalize_function_invocation_configuration",
|
|
663
668
|
"normalize_messages",
|
|
@@ -682,5 +687,6 @@ __all__ = [
|
|
|
682
687
|
"validate_tools",
|
|
683
688
|
"validate_workflow_graph",
|
|
684
689
|
"vectorstoremodel",
|
|
690
|
+
"with_namespaced_value",
|
|
685
691
|
"workflow",
|
|
686
692
|
]
|
|
@@ -825,42 +825,6 @@ class _OutputCodec:
|
|
|
825
825
|
# region Enforcement helpers
|
|
826
826
|
|
|
827
827
|
|
|
828
|
-
def _chat_updates_from_response(response: ChatResponse[Any]) -> list[ChatResponseUpdate]:
|
|
829
|
-
"""Re-derive stream updates from a (transformed) assembled chat response."""
|
|
830
|
-
updates = [
|
|
831
|
-
ChatResponseUpdate(
|
|
832
|
-
contents=list(message.contents),
|
|
833
|
-
role=cast(Any, message.role),
|
|
834
|
-
author_name=message.author_name,
|
|
835
|
-
message_id=message.message_id,
|
|
836
|
-
response_id=response.response_id,
|
|
837
|
-
model=response.model,
|
|
838
|
-
)
|
|
839
|
-
for message in response.messages
|
|
840
|
-
]
|
|
841
|
-
if not updates:
|
|
842
|
-
updates = [ChatResponseUpdate(role="assistant", response_id=response.response_id)]
|
|
843
|
-
updates[-1].finish_reason = response.finish_reason
|
|
844
|
-
return updates
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
def _agent_updates_from_response(response: AgentResponse[Any]) -> list[AgentResponseUpdate]:
|
|
848
|
-
"""Re-derive stream updates from a (transformed) assembled agent response."""
|
|
849
|
-
updates = [
|
|
850
|
-
AgentResponseUpdate(
|
|
851
|
-
contents=list(message.contents),
|
|
852
|
-
role=message.role,
|
|
853
|
-
author_name=message.author_name,
|
|
854
|
-
message_id=message.message_id,
|
|
855
|
-
response_id=response.response_id,
|
|
856
|
-
)
|
|
857
|
-
for message in response.messages
|
|
858
|
-
]
|
|
859
|
-
if not updates:
|
|
860
|
-
updates = [AgentResponseUpdate(role="assistant", response_id=response.response_id)]
|
|
861
|
-
return updates
|
|
862
|
-
|
|
863
|
-
|
|
864
828
|
def _normalized_tools(tools: Any, *, point: str) -> list[Any]:
|
|
865
829
|
"""Normalize a tools value for projection; empty (with a warning) when it cannot be.
|
|
866
830
|
|
|
@@ -1313,7 +1277,7 @@ class _AgentHooksAgentMiddleware(_AgentHooksMiddlewareBase, AgentMiddleware):
|
|
|
1313
1277
|
await self._emit_shutdown(state, "cancelled" if isinstance(exc, asyncio.CancelledError) else "error")
|
|
1314
1278
|
|
|
1315
1279
|
context._stream_terminal_result_transforms.append(_transform) # pyright: ignore[reportPrivateUsage]
|
|
1316
|
-
context._stream_terminal_result_to_updates =
|
|
1280
|
+
context._stream_terminal_result_to_updates = AgentResponse.to_updates # pyright: ignore[reportPrivateUsage]
|
|
1317
1281
|
context._stream_terminal_result_is_authoritative = True # pyright: ignore[reportPrivateUsage]
|
|
1318
1282
|
context._stream_release_hooks.append(_release) # pyright: ignore[reportPrivateUsage]
|
|
1319
1283
|
context._stream_release_error_hooks.append(_release_error) # pyright: ignore[reportPrivateUsage]
|
|
@@ -1443,7 +1407,7 @@ class _AgentHooksChatMiddleware(_AgentHooksMiddlewareBase, ChatMiddleware):
|
|
|
1443
1407
|
gate_handle.drop()
|
|
1444
1408
|
|
|
1445
1409
|
context._stream_terminal_result_transforms.append(_transform) # pyright: ignore[reportPrivateUsage]
|
|
1446
|
-
context._stream_terminal_result_to_updates =
|
|
1410
|
+
context._stream_terminal_result_to_updates = ChatResponse.to_updates # pyright: ignore[reportPrivateUsage]
|
|
1447
1411
|
context._stream_terminal_result_is_authoritative = True # pyright: ignore[reportPrivateUsage]
|
|
1448
1412
|
context._stream_release_hooks.append(_release) # pyright: ignore[reportPrivateUsage]
|
|
1449
1413
|
context._stream_release_error_hooks.append(_release_error) # pyright: ignore[reportPrivateUsage]
|
|
@@ -81,6 +81,8 @@ else:
|
|
|
81
81
|
from typing_extensions import Self, TypedDict # pragma: no cover
|
|
82
82
|
|
|
83
83
|
if TYPE_CHECKING:
|
|
84
|
+
from collections.abc import Collection
|
|
85
|
+
|
|
84
86
|
from mcp import types
|
|
85
87
|
from mcp.server.lowlevel import Server
|
|
86
88
|
from pydantic import BaseModel
|
|
@@ -113,6 +115,17 @@ def _tool_approval_source_ids(middleware: Sequence[MiddlewareTypes] | None) -> f
|
|
|
113
115
|
return frozenset(item.source_id for item in middleware if isinstance(item, ToolApprovalMiddleware))
|
|
114
116
|
|
|
115
117
|
|
|
118
|
+
def _provider_service_session_state_keys(agent: object) -> frozenset[str]:
|
|
119
|
+
"""Return provider-owned session-state keys declared by an agent or its client."""
|
|
120
|
+
keys: set[str] = set()
|
|
121
|
+
# A generic Agent can use a provider client that persists its own continuation state.
|
|
122
|
+
for owner in (agent, getattr(agent, "client", None)):
|
|
123
|
+
declared = getattr(owner, "service_session_state_keys", ())
|
|
124
|
+
if isinstance(declared, (list, tuple, set, frozenset)):
|
|
125
|
+
keys.update(key for key in cast("Collection[Any]", declared) if isinstance(key, str))
|
|
126
|
+
return frozenset(keys)
|
|
127
|
+
|
|
128
|
+
|
|
116
129
|
def _merge_delegated_session_state(
|
|
117
130
|
parent_state: MutableMapping[str, Any],
|
|
118
131
|
initial_child_state: Mapping[str, Any],
|
|
@@ -665,9 +678,10 @@ class BaseAgent(SerializationMixin):
|
|
|
665
678
|
propagate_session: If True, the parent agent's session is forwarded
|
|
666
679
|
to this sub-agent's ``run()`` call. Application-state changes
|
|
667
680
|
propagate back to the parent, while framework approval continuation
|
|
668
|
-
state
|
|
669
|
-
|
|
670
|
-
When False, that session is
|
|
681
|
+
state and provider-owned service session state remain isolated.
|
|
682
|
+
Defaults to False. The sub-agent always receives an AgentSession
|
|
683
|
+
so session-backed middleware can run. When False, that session is
|
|
684
|
+
private to this invocation.
|
|
671
685
|
|
|
672
686
|
Returns:
|
|
673
687
|
A FunctionTool that can be used as a tool by other agents.
|
|
@@ -683,6 +697,10 @@ class BaseAgent(SerializationMixin):
|
|
|
683
697
|
values. The delegated call raises ToolExecutionException before running
|
|
684
698
|
the child when their shared session-state keys overlap.
|
|
685
699
|
|
|
700
|
+
Direct or custom-loop invocation with ``propagate_session=True`` must set
|
|
701
|
+
:attr:`FunctionInvocationContext.parent_service_session_state_keys` when
|
|
702
|
+
the parent session is non-empty. Automatic function calling supplies it.
|
|
703
|
+
|
|
686
704
|
Examples:
|
|
687
705
|
.. code-block:: python
|
|
688
706
|
|
|
@@ -733,10 +751,24 @@ class BaseAgent(SerializationMixin):
|
|
|
733
751
|
session = AgentSession()
|
|
734
752
|
child_approval_source_ids = _tool_approval_source_ids(self.middleware)
|
|
735
753
|
parent_approval_source_ids: frozenset[str] = frozenset()
|
|
754
|
+
parent_service_session_state_keys: frozenset[str] = frozenset()
|
|
736
755
|
|
|
737
756
|
if propagate_session and parent_session is not None:
|
|
738
757
|
from ._tools import _PARENT_TOOL_APPROVAL_SOURCE_IDS_CONTEXT_KEY # pyright: ignore[reportPrivateUsage]
|
|
739
758
|
|
|
759
|
+
# A custom loop can bypass the framework seam that identifies the parent provider's state.
|
|
760
|
+
# Refuse a non-empty shared session rather than guessing which keys are safe to delegate.
|
|
761
|
+
if ctx.parent_service_session_state_keys is None:
|
|
762
|
+
if parent_session.state:
|
|
763
|
+
raise ToolExecutionException(
|
|
764
|
+
f"Agent tool {tool_name!r} cannot safely propagate a non-empty parent session because "
|
|
765
|
+
"the invocation path did not provide parent provider-owned session state keys. "
|
|
766
|
+
"Use the automatic function-calling loop, set "
|
|
767
|
+
"FunctionInvocationContext.parent_service_session_state_keys in a custom loop, "
|
|
768
|
+
"or set propagate_session=False."
|
|
769
|
+
)
|
|
770
|
+
else:
|
|
771
|
+
parent_service_session_state_keys = ctx.parent_service_session_state_keys
|
|
740
772
|
raw_parent_approval_source_ids = ctx.metadata.get(_PARENT_TOOL_APPROVAL_SOURCE_IDS_CONTEXT_KEY)
|
|
741
773
|
parent_approval_source_ids = (
|
|
742
774
|
cast("frozenset[str]", raw_parent_approval_source_ids)
|
|
@@ -770,6 +802,10 @@ class BaseAgent(SerializationMixin):
|
|
|
770
802
|
_TOOL_APPROVAL_STATE_KEY,
|
|
771
803
|
_FUNCTION_INVOCATION_BUDGET_STATE_KEY,
|
|
772
804
|
_FUNCTION_RESULT_PAYLOAD_BUDGET_STATE_KEY,
|
|
805
|
+
# Service handles belong to one agent's remote resources, not shared application state.
|
|
806
|
+
# Exclude them in both directions so later children cannot inherit an earlier child's handles.
|
|
807
|
+
*_provider_service_session_state_keys(self),
|
|
808
|
+
*parent_service_session_state_keys,
|
|
773
809
|
*child_approval_source_ids,
|
|
774
810
|
*parent_approval_source_ids,
|
|
775
811
|
})
|
|
@@ -959,7 +995,7 @@ class RawAgent(BaseAgent, Generic[OptionsCoT]):
|
|
|
959
995
|
tokenizer: TokenizerProtocol | None = None,
|
|
960
996
|
additional_properties: MutableMapping[str, Any] | None = None,
|
|
961
997
|
) -> None:
|
|
962
|
-
"""Initialize
|
|
998
|
+
"""Initialize an Agent instance.
|
|
963
999
|
|
|
964
1000
|
Args:
|
|
965
1001
|
client: The chat client to use for the agent.
|
|
@@ -1589,7 +1625,10 @@ class RawAgent(BaseAgent, Generic[OptionsCoT]):
|
|
|
1589
1625
|
|
|
1590
1626
|
agent_name = self._get_agent_name()
|
|
1591
1627
|
from ._mcp import MCPTool
|
|
1592
|
-
from ._tools import
|
|
1628
|
+
from ._tools import (
|
|
1629
|
+
_PARENT_SERVICE_SESSION_STATE_KEYS_CONTEXT_KEY, # pyright: ignore[reportPrivateUsage]
|
|
1630
|
+
_PARENT_TOOL_APPROVAL_SOURCE_IDS_CONTEXT_KEY, # pyright: ignore[reportPrivateUsage]
|
|
1631
|
+
)
|
|
1593
1632
|
|
|
1594
1633
|
base_tools = _normalize_tools(chat_options.pop("tools", None))
|
|
1595
1634
|
mcp_duplicate_message = "Tool names must be unique. Consider setting `tool_name_prefix` on the MCPTool."
|
|
@@ -1637,6 +1676,10 @@ class RawAgent(BaseAgent, Generic[OptionsCoT]):
|
|
|
1637
1676
|
additional_function_arguments[_PARENT_TOOL_APPROVAL_SOURCE_IDS_CONTEXT_KEY] = _tool_approval_source_ids(
|
|
1638
1677
|
self.middleware
|
|
1639
1678
|
)
|
|
1679
|
+
# Recompute ownership for this invoking agent; caller kwargs must not replace its declarations.
|
|
1680
|
+
additional_function_arguments[_PARENT_SERVICE_SESSION_STATE_KEYS_CONTEXT_KEY] = (
|
|
1681
|
+
_provider_service_session_state_keys(self)
|
|
1682
|
+
)
|
|
1640
1683
|
|
|
1641
1684
|
model = opts.pop("model", None)
|
|
1642
1685
|
|
|
@@ -2061,7 +2104,7 @@ class Agent(
|
|
|
2061
2104
|
tokenizer: TokenizerProtocol | None = None,
|
|
2062
2105
|
additional_properties: MutableMapping[str, Any] | None = None,
|
|
2063
2106
|
) -> None:
|
|
2064
|
-
"""Initialize
|
|
2107
|
+
"""Initialize an Agent instance."""
|
|
2065
2108
|
super().__init__(
|
|
2066
2109
|
client=client,
|
|
2067
2110
|
instructions=instructions,
|
|
@@ -1013,11 +1013,9 @@ def _serialize_content(content: Content) -> dict[str, Any]:
|
|
|
1013
1013
|
else:
|
|
1014
1014
|
payload.pop("protected_data", None)
|
|
1015
1015
|
additional_properties = payload.get("additional_properties")
|
|
1016
|
-
if isinstance(additional_properties, dict) and
|
|
1017
|
-
|
|
1018
|
-
payload["additional_properties"] = {
|
|
1019
|
-
key: value for key, value in typed_properties.items() if key != "encrypted_content"
|
|
1020
|
-
}
|
|
1016
|
+
if isinstance(additional_properties, dict) and additional_properties:
|
|
1017
|
+
filtered_properties = _strip_opaque_reasoning_payload(additional_properties)
|
|
1018
|
+
payload["additional_properties"] = filtered_properties or {}
|
|
1021
1019
|
return payload
|
|
1022
1020
|
|
|
1023
1021
|
|
{agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_harness/_file_access.py
RENAMED
|
@@ -28,6 +28,8 @@ import logging
|
|
|
28
28
|
import os
|
|
29
29
|
import threading
|
|
30
30
|
import time
|
|
31
|
+
import unicodedata
|
|
32
|
+
import weakref
|
|
31
33
|
from abc import ABC, abstractmethod
|
|
32
34
|
from collections.abc import Awaitable, Mapping, MutableMapping
|
|
33
35
|
from pathlib import Path
|
|
@@ -1718,6 +1720,52 @@ class _SearchFilesInput(BaseModel):
|
|
|
1718
1720
|
] = None
|
|
1719
1721
|
|
|
1720
1722
|
|
|
1723
|
+
# Locks handed out by ``_store_write_lock``. Values are held weakly, so an entry lasts only while an
|
|
1724
|
+
# edit holds or awaits its lock, and the map never grows with the number of paths ever edited.
|
|
1725
|
+
_STORE_WRITE_LOCKS: weakref.WeakValueDictionary[tuple[int, asyncio.AbstractEventLoop, str], asyncio.Lock] = (
|
|
1726
|
+
weakref.WeakValueDictionary()
|
|
1727
|
+
)
|
|
1728
|
+
|
|
1729
|
+
|
|
1730
|
+
def _store_write_lock(store: AgentFileStore, folder: str) -> asyncio.Lock:
|
|
1731
|
+
"""Return the lock that serializes edits of the files directly inside ``folder`` in ``store``.
|
|
1732
|
+
|
|
1733
|
+
Mutating tools hold it across their read-modify-write. It belongs to the store
|
|
1734
|
+
instance rather than to one provider, so two providers sharing a store cannot both
|
|
1735
|
+
read a file before either writes it back and silently drop one of the edits.
|
|
1736
|
+
File-access tools lock the folder of the file they edit (see ``_parent_folder``) and
|
|
1737
|
+
file-memory tools lock their working folder, so the two kinds of provider also wait
|
|
1738
|
+
for each other when they edit the same file. Edits of different files in one folder
|
|
1739
|
+
therefore run one at a time.
|
|
1740
|
+
|
|
1741
|
+
The folder name is folded to NFC and lowercased because a store can treat such
|
|
1742
|
+
spellings as one folder: :class:`InMemoryAgentFileStore` ignores case, and macOS
|
|
1743
|
+
file systems resolve the NFC and NFD forms of a name to one entry. Elsewhere two
|
|
1744
|
+
spellings merely share a lock.
|
|
1745
|
+
|
|
1746
|
+
Locks are per event loop, because an :class:`asyncio.Lock` that has had a waiter on
|
|
1747
|
+
one loop raises when a caller has to wait for it on another. The key holds
|
|
1748
|
+
``id(store)`` rather than the store, so a store need not be hashable (a default
|
|
1749
|
+
``@dataclass`` is not) and is never kept alive by this map; the id cannot be reused
|
|
1750
|
+
while its lock is in use, because whoever holds or awaits the lock still references
|
|
1751
|
+
the store.
|
|
1752
|
+
|
|
1753
|
+
The lock is process-local: other processes, and other store instances over the same
|
|
1754
|
+
backing storage, are not coordinated.
|
|
1755
|
+
"""
|
|
1756
|
+
key = (id(store), asyncio.get_running_loop(), unicodedata.normalize("NFC", folder).lower())
|
|
1757
|
+
lock = _STORE_WRITE_LOCKS.get(key)
|
|
1758
|
+
if lock is None:
|
|
1759
|
+
lock = asyncio.Lock()
|
|
1760
|
+
_STORE_WRITE_LOCKS[key] = lock
|
|
1761
|
+
return lock
|
|
1762
|
+
|
|
1763
|
+
|
|
1764
|
+
def _parent_folder(path: str) -> str:
|
|
1765
|
+
"""Return the folder of a normalized store path (``""`` for a file at the root)."""
|
|
1766
|
+
return path.rpartition("/")[0]
|
|
1767
|
+
|
|
1768
|
+
|
|
1721
1769
|
@experimental(feature_id=ExperimentalFeature.HARNESS)
|
|
1722
1770
|
class FileAccessProvider(ContextProvider):
|
|
1723
1771
|
"""Context provider that gives an agent CRUD/search access to a shared file store.
|
|
@@ -1878,12 +1926,6 @@ class FileAccessProvider(ContextProvider):
|
|
|
1878
1926
|
self.disable_write_tool_approval = disable_write_tool_approval
|
|
1879
1927
|
self.session_scoped = session_scoped
|
|
1880
1928
|
self.scope = scope
|
|
1881
|
-
# Serializes mutating tool operations (write/delete/replace/replace_lines).
|
|
1882
|
-
# The provider is shared across sessions/agents, so read-modify-write tools
|
|
1883
|
-
# (replace/replace_lines) could otherwise interleave and lose updates. Note
|
|
1884
|
-
# this only serializes within a single event loop/process, not across
|
|
1885
|
-
# processes sharing a FileSystemAgentFileStore on disk.
|
|
1886
|
-
self._write_lock = asyncio.Lock()
|
|
1887
1929
|
|
|
1888
1930
|
def _resolve_session_key(self, context: SessionContext) -> str:
|
|
1889
1931
|
"""Resolve the working folder key for session-scoped mode.
|
|
@@ -1919,15 +1961,14 @@ class FileAccessProvider(ContextProvider):
|
|
|
1919
1961
|
def _is_local_tool_call(function_call: Content) -> bool:
|
|
1920
1962
|
"""Return whether a function call targets this provider's local tools.
|
|
1921
1963
|
|
|
1922
|
-
Hosted-tool calls
|
|
1923
|
-
``additional_properties`` and are a separate server-scoped approval
|
|
1964
|
+
Hosted-tool calls set ``hosted=True`` and are a separate server-scoped approval
|
|
1924
1965
|
boundary that must be passed through untouched (see
|
|
1925
1966
|
:func:`agent_framework._tools._is_hosted_tool_approval`). These rules
|
|
1926
1967
|
only ever auto-approve the provider's own local tools, so any call that
|
|
1927
|
-
|
|
1968
|
+
is hosted is rejected even if its name collides with a
|
|
1928
1969
|
file-access tool name.
|
|
1929
1970
|
"""
|
|
1930
|
-
return not function_call.
|
|
1971
|
+
return not function_call.hosted
|
|
1931
1972
|
|
|
1932
1973
|
@staticmethod
|
|
1933
1974
|
def read_only_tools_auto_approval_rule(function_call: Content) -> bool:
|
|
@@ -1941,7 +1982,7 @@ class FileAccessProvider(ContextProvider):
|
|
|
1941
1982
|
the tools that modify it (``file_access_write``, ``file_access_delete``,
|
|
1942
1983
|
``file_access_replace``, and ``file_access_replace_lines``).
|
|
1943
1984
|
|
|
1944
|
-
Hosted-tool calls
|
|
1985
|
+
Hosted-tool calls are never
|
|
1945
1986
|
auto-approved, even when their name matches a file-access tool, so the
|
|
1946
1987
|
rule stays scoped to this provider's local tools.
|
|
1947
1988
|
|
|
@@ -1978,7 +2019,7 @@ class FileAccessProvider(ContextProvider):
|
|
|
1978
2019
|
``file_access_delete``, ``file_access_replace``, and
|
|
1979
2020
|
``file_access_replace_lines``).
|
|
1980
2021
|
|
|
1981
|
-
Hosted-tool calls
|
|
2022
|
+
Hosted-tool calls are never
|
|
1982
2023
|
auto-approved, even when their name matches a file-access tool, so the
|
|
1983
2024
|
rule stays scoped to this provider's local tools.
|
|
1984
2025
|
|
|
@@ -2036,7 +2077,9 @@ class FileAccessProvider(ContextProvider):
|
|
|
2036
2077
|
try:
|
|
2037
2078
|
normalized = _normalize_relative_path(file_name)
|
|
2038
2079
|
store_path = _session_path(normalized)
|
|
2039
|
-
|
|
2080
|
+
# Every mutating tool takes the lock of the file's folder, which all providers
|
|
2081
|
+
# on the store share, so a write cannot land between another edit's read and write.
|
|
2082
|
+
async with _store_write_lock(self.store, _parent_folder(store_path)):
|
|
2040
2083
|
await self.store.write(store_path, content, overwrite=overwrite)
|
|
2041
2084
|
except NotADirectoryError:
|
|
2042
2085
|
return f"Could not write file '{file_name}': a parent path is already a file. Choose a different path."
|
|
@@ -2091,7 +2134,7 @@ class FileAccessProvider(ContextProvider):
|
|
|
2091
2134
|
try:
|
|
2092
2135
|
normalized = _normalize_relative_path(file_name)
|
|
2093
2136
|
store_path = _session_path(normalized)
|
|
2094
|
-
async with self.
|
|
2137
|
+
async with _store_write_lock(self.store, _parent_folder(store_path)):
|
|
2095
2138
|
deleted = await self.store.delete(store_path)
|
|
2096
2139
|
except ValueError as exc:
|
|
2097
2140
|
return f"Could not delete file '{file_name}': {exc}"
|
|
@@ -2132,7 +2175,7 @@ class FileAccessProvider(ContextProvider):
|
|
|
2132
2175
|
try:
|
|
2133
2176
|
normalized = _normalize_relative_path(file_name)
|
|
2134
2177
|
store_path = _session_path(normalized)
|
|
2135
|
-
async with self.
|
|
2178
|
+
async with _store_write_lock(self.store, _parent_folder(store_path)):
|
|
2136
2179
|
content = await self.store.read(store_path)
|
|
2137
2180
|
if content is None:
|
|
2138
2181
|
return f"File '{file_name}' not found."
|
|
@@ -2154,7 +2197,7 @@ class FileAccessProvider(ContextProvider):
|
|
|
2154
2197
|
try:
|
|
2155
2198
|
normalized = _normalize_relative_path(file_name)
|
|
2156
2199
|
store_path = _session_path(normalized)
|
|
2157
|
-
async with self.
|
|
2200
|
+
async with _store_write_lock(self.store, _parent_folder(store_path)):
|
|
2158
2201
|
content = await self.store.read(store_path)
|
|
2159
2202
|
if content is None:
|
|
2160
2203
|
return f"File '{file_name}' not found."
|
{agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_harness/_file_memory.py
RENAMED
|
@@ -42,7 +42,6 @@ per-invocation :class:`~agent_framework.SessionContext` in
|
|
|
42
42
|
|
|
43
43
|
from __future__ import annotations
|
|
44
44
|
|
|
45
|
-
import asyncio
|
|
46
45
|
import logging
|
|
47
46
|
from typing import Annotated, Any
|
|
48
47
|
|
|
@@ -60,6 +59,7 @@ from ._file_access import (
|
|
|
60
59
|
_line_edits, # pyright: ignore[reportPrivateUsage]
|
|
61
60
|
_matches_glob, # pyright: ignore[reportPrivateUsage]
|
|
62
61
|
_normalize_relative_path, # pyright: ignore[reportPrivateUsage]
|
|
62
|
+
_store_write_lock, # pyright: ignore[reportPrivateUsage]
|
|
63
63
|
)
|
|
64
64
|
|
|
65
65
|
logger = logging.getLogger(__name__)
|
|
@@ -297,10 +297,6 @@ class FileMemoryProvider(ContextProvider):
|
|
|
297
297
|
self.store = store
|
|
298
298
|
self.scope = scope
|
|
299
299
|
self.instructions = instructions or DEFAULT_FILE_MEMORY_INSTRUCTIONS
|
|
300
|
-
# Serializes write/delete operations (and their index rebuilds) so the
|
|
301
|
-
# ``memories.md`` index stays consistent. A single per-instance lock is
|
|
302
|
-
# sufficient for v1; concurrent writes across scopes are rare in practice.
|
|
303
|
-
self._write_lock = asyncio.Lock()
|
|
304
300
|
|
|
305
301
|
def _resolve_working_folder(self, context: SessionContext) -> str:
|
|
306
302
|
"""Resolve the working folder for the current invocation.
|
|
@@ -382,7 +378,11 @@ class FileMemoryProvider(ContextProvider):
|
|
|
382
378
|
|
|
383
379
|
path = _combine_paths(working_folder, normalized)
|
|
384
380
|
desc_path = _combine_paths(working_folder, _description_file_name(normalized))
|
|
385
|
-
|
|
381
|
+
# Every mutating tool locks the whole working folder, not just the file, because each
|
|
382
|
+
# write and delete rebuilds the folder's memories.md index. The lock is shared by all
|
|
383
|
+
# providers on the store, including file-access tools editing a file in this folder, so
|
|
384
|
+
# they cannot interleave edits or index rebuilds.
|
|
385
|
+
async with _store_write_lock(self.store, working_folder):
|
|
386
386
|
try:
|
|
387
387
|
await self.store.write(path, content)
|
|
388
388
|
if description and description.strip():
|
|
@@ -427,7 +427,7 @@ class FileMemoryProvider(ContextProvider):
|
|
|
427
427
|
|
|
428
428
|
path = _combine_paths(working_folder, normalized)
|
|
429
429
|
desc_path = _combine_paths(working_folder, _description_file_name(normalized))
|
|
430
|
-
async with self.
|
|
430
|
+
async with _store_write_lock(self.store, working_folder):
|
|
431
431
|
try:
|
|
432
432
|
deleted = await self.store.delete(path)
|
|
433
433
|
await self.store.delete(desc_path)
|
|
@@ -478,7 +478,7 @@ class FileMemoryProvider(ContextProvider):
|
|
|
478
478
|
"Please choose a different file name."
|
|
479
479
|
)
|
|
480
480
|
path = _combine_paths(working_folder, normalized)
|
|
481
|
-
async with self.
|
|
481
|
+
async with _store_write_lock(self.store, working_folder):
|
|
482
482
|
try:
|
|
483
483
|
content = await self.store.read(path)
|
|
484
484
|
if content is None:
|
|
@@ -506,7 +506,7 @@ class FileMemoryProvider(ContextProvider):
|
|
|
506
506
|
"Please choose a different file name."
|
|
507
507
|
)
|
|
508
508
|
path = _combine_paths(working_folder, normalized)
|
|
509
|
-
async with self.
|
|
509
|
+
async with _store_write_lock(self.store, working_folder):
|
|
510
510
|
try:
|
|
511
511
|
content = await self.store.read(path)
|
|
512
512
|
if content is None:
|
{agent_framework_core-1.20.0 → agent_framework_core-1.22.0}/agent_framework/_harness/_memory.py
RENAMED
|
@@ -506,10 +506,10 @@ class MemoryTopicRecord:
|
|
|
506
506
|
topic = stripped[2:].strip()
|
|
507
507
|
current_section = None
|
|
508
508
|
continue
|
|
509
|
-
if stripped.startswith("Updated: "):
|
|
509
|
+
if current_section is None and stripped.startswith("Updated: "):
|
|
510
510
|
updated_at = stripped.removeprefix("Updated: ").strip()
|
|
511
511
|
continue
|
|
512
|
-
if stripped.startswith("Sessions: "):
|
|
512
|
+
if current_section is None and stripped.startswith("Sessions: "):
|
|
513
513
|
raw_sessions = stripped.removeprefix("Sessions: ").strip()
|
|
514
514
|
session_ids = (
|
|
515
515
|
[]
|