aip-agents-binary 0.5.20__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aip_agents/__init__.py +65 -0
- aip_agents/a2a/__init__.py +19 -0
- aip_agents/a2a/server/__init__.py +10 -0
- aip_agents/a2a/server/base_executor.py +1086 -0
- aip_agents/a2a/server/google_adk_executor.py +198 -0
- aip_agents/a2a/server/langflow_executor.py +180 -0
- aip_agents/a2a/server/langgraph_executor.py +270 -0
- aip_agents/a2a/types.py +232 -0
- aip_agents/agent/__init__.py +27 -0
- aip_agents/agent/base_agent.py +970 -0
- aip_agents/agent/base_langgraph_agent.py +2942 -0
- aip_agents/agent/google_adk_agent.py +926 -0
- aip_agents/agent/google_adk_constants.py +6 -0
- aip_agents/agent/hitl/__init__.py +24 -0
- aip_agents/agent/hitl/config.py +28 -0
- aip_agents/agent/hitl/langgraph_hitl_mixin.py +515 -0
- aip_agents/agent/hitl/manager.py +532 -0
- aip_agents/agent/hitl/models.py +18 -0
- aip_agents/agent/hitl/prompt/__init__.py +9 -0
- aip_agents/agent/hitl/prompt/base.py +42 -0
- aip_agents/agent/hitl/prompt/deferred.py +73 -0
- aip_agents/agent/hitl/registry.py +149 -0
- aip_agents/agent/interface.py +138 -0
- aip_agents/agent/interfaces.py +65 -0
- aip_agents/agent/langflow_agent.py +464 -0
- aip_agents/agent/langgraph_memory_enhancer_agent.py +433 -0
- aip_agents/agent/langgraph_react_agent.py +2514 -0
- aip_agents/agent/system_instruction_context.py +34 -0
- aip_agents/clients/__init__.py +10 -0
- aip_agents/clients/langflow/__init__.py +10 -0
- aip_agents/clients/langflow/client.py +477 -0
- aip_agents/clients/langflow/types.py +18 -0
- aip_agents/constants.py +23 -0
- aip_agents/credentials/manager.py +132 -0
- aip_agents/examples/__init__.py +5 -0
- aip_agents/examples/compare_streaming_client.py +783 -0
- aip_agents/examples/compare_streaming_server.py +142 -0
- aip_agents/examples/demo_memory_recall.py +401 -0
- aip_agents/examples/hello_world_a2a_google_adk_client.py +49 -0
- aip_agents/examples/hello_world_a2a_google_adk_client_agent.py +48 -0
- aip_agents/examples/hello_world_a2a_google_adk_client_streaming.py +60 -0
- aip_agents/examples/hello_world_a2a_google_adk_server.py +79 -0
- aip_agents/examples/hello_world_a2a_langchain_client.py +39 -0
- aip_agents/examples/hello_world_a2a_langchain_client_agent.py +39 -0
- aip_agents/examples/hello_world_a2a_langchain_client_lm_invoker.py +37 -0
- aip_agents/examples/hello_world_a2a_langchain_client_streaming.py +41 -0
- aip_agents/examples/hello_world_a2a_langchain_reference_client_streaming.py +60 -0
- aip_agents/examples/hello_world_a2a_langchain_reference_server.py +105 -0
- aip_agents/examples/hello_world_a2a_langchain_server.py +79 -0
- aip_agents/examples/hello_world_a2a_langchain_server_lm_invoker.py +78 -0
- aip_agents/examples/hello_world_a2a_langflow_client.py +83 -0
- aip_agents/examples/hello_world_a2a_langflow_server.py +82 -0
- aip_agents/examples/hello_world_a2a_langgraph_artifact_client.py +73 -0
- aip_agents/examples/hello_world_a2a_langgraph_artifact_client_streaming.py +76 -0
- aip_agents/examples/hello_world_a2a_langgraph_artifact_server.py +92 -0
- aip_agents/examples/hello_world_a2a_langgraph_client.py +54 -0
- aip_agents/examples/hello_world_a2a_langgraph_client_agent.py +54 -0
- aip_agents/examples/hello_world_a2a_langgraph_client_agent_lm_invoker.py +32 -0
- aip_agents/examples/hello_world_a2a_langgraph_client_streaming.py +50 -0
- aip_agents/examples/hello_world_a2a_langgraph_client_streaming_lm_invoker.py +44 -0
- aip_agents/examples/hello_world_a2a_langgraph_client_streaming_tool_streaming.py +92 -0
- aip_agents/examples/hello_world_a2a_langgraph_server.py +84 -0
- aip_agents/examples/hello_world_a2a_langgraph_server_lm_invoker.py +79 -0
- aip_agents/examples/hello_world_a2a_langgraph_server_tool_streaming.py +132 -0
- aip_agents/examples/hello_world_a2a_mcp_langgraph.py +196 -0
- aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_client.py +244 -0
- aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_server.py +251 -0
- aip_agents/examples/hello_world_a2a_with_metadata_langchain_client.py +57 -0
- aip_agents/examples/hello_world_a2a_with_metadata_langchain_server_lm_invoker.py +80 -0
- aip_agents/examples/hello_world_google_adk.py +41 -0
- aip_agents/examples/hello_world_google_adk_mcp_http.py +34 -0
- aip_agents/examples/hello_world_google_adk_mcp_http_stream.py +40 -0
- aip_agents/examples/hello_world_google_adk_mcp_sse.py +44 -0
- aip_agents/examples/hello_world_google_adk_mcp_sse_stream.py +48 -0
- aip_agents/examples/hello_world_google_adk_mcp_stdio.py +44 -0
- aip_agents/examples/hello_world_google_adk_mcp_stdio_stream.py +48 -0
- aip_agents/examples/hello_world_google_adk_stream.py +44 -0
- aip_agents/examples/hello_world_langchain.py +28 -0
- aip_agents/examples/hello_world_langchain_lm_invoker.py +15 -0
- aip_agents/examples/hello_world_langchain_mcp_http.py +34 -0
- aip_agents/examples/hello_world_langchain_mcp_http_interactive.py +130 -0
- aip_agents/examples/hello_world_langchain_mcp_http_stream.py +42 -0
- aip_agents/examples/hello_world_langchain_mcp_multi_server.py +155 -0
- aip_agents/examples/hello_world_langchain_mcp_sse.py +34 -0
- aip_agents/examples/hello_world_langchain_mcp_sse_stream.py +40 -0
- aip_agents/examples/hello_world_langchain_mcp_stdio.py +30 -0
- aip_agents/examples/hello_world_langchain_mcp_stdio_stream.py +41 -0
- aip_agents/examples/hello_world_langchain_stream.py +36 -0
- aip_agents/examples/hello_world_langchain_stream_lm_invoker.py +39 -0
- aip_agents/examples/hello_world_langflow_agent.py +163 -0
- aip_agents/examples/hello_world_langgraph.py +39 -0
- aip_agents/examples/hello_world_langgraph_bosa_twitter.py +41 -0
- aip_agents/examples/hello_world_langgraph_mcp_http.py +31 -0
- aip_agents/examples/hello_world_langgraph_mcp_http_stream.py +34 -0
- aip_agents/examples/hello_world_langgraph_mcp_sse.py +35 -0
- aip_agents/examples/hello_world_langgraph_mcp_sse_stream.py +50 -0
- aip_agents/examples/hello_world_langgraph_mcp_stdio.py +35 -0
- aip_agents/examples/hello_world_langgraph_mcp_stdio_stream.py +50 -0
- aip_agents/examples/hello_world_langgraph_stream.py +43 -0
- aip_agents/examples/hello_world_langgraph_stream_lm_invoker.py +37 -0
- aip_agents/examples/hello_world_model_switch_cli.py +210 -0
- aip_agents/examples/hello_world_multi_agent_adk.py +75 -0
- aip_agents/examples/hello_world_multi_agent_langchain.py +54 -0
- aip_agents/examples/hello_world_multi_agent_langgraph.py +66 -0
- aip_agents/examples/hello_world_multi_agent_langgraph_lm_invoker.py +69 -0
- aip_agents/examples/hello_world_pii_logger.py +21 -0
- aip_agents/examples/hello_world_sentry.py +133 -0
- aip_agents/examples/hello_world_step_limits.py +273 -0
- aip_agents/examples/hello_world_stock_a2a_server.py +103 -0
- aip_agents/examples/hello_world_tool_output_client.py +46 -0
- aip_agents/examples/hello_world_tool_output_server.py +114 -0
- aip_agents/examples/hitl_demo.py +724 -0
- aip_agents/examples/mcp_configs/configs.py +63 -0
- aip_agents/examples/mcp_servers/common.py +76 -0
- aip_agents/examples/mcp_servers/mcp_name.py +29 -0
- aip_agents/examples/mcp_servers/mcp_server_http.py +19 -0
- aip_agents/examples/mcp_servers/mcp_server_sse.py +19 -0
- aip_agents/examples/mcp_servers/mcp_server_stdio.py +19 -0
- aip_agents/examples/mcp_servers/mcp_time.py +10 -0
- aip_agents/examples/pii_demo_langgraph_client.py +69 -0
- aip_agents/examples/pii_demo_langgraph_server.py +126 -0
- aip_agents/examples/pii_demo_multi_agent_client.py +80 -0
- aip_agents/examples/pii_demo_multi_agent_server.py +247 -0
- aip_agents/examples/todolist_planning_a2a_langchain_client.py +70 -0
- aip_agents/examples/todolist_planning_a2a_langgraph_server.py +88 -0
- aip_agents/examples/tools/__init__.py +27 -0
- aip_agents/examples/tools/adk_arithmetic_tools.py +36 -0
- aip_agents/examples/tools/adk_weather_tool.py +60 -0
- aip_agents/examples/tools/data_generator_tool.py +103 -0
- aip_agents/examples/tools/data_visualization_tool.py +312 -0
- aip_agents/examples/tools/image_artifact_tool.py +136 -0
- aip_agents/examples/tools/langchain_arithmetic_tools.py +26 -0
- aip_agents/examples/tools/langchain_currency_exchange_tool.py +88 -0
- aip_agents/examples/tools/langchain_graph_artifact_tool.py +172 -0
- aip_agents/examples/tools/langchain_weather_tool.py +48 -0
- aip_agents/examples/tools/langgraph_streaming_tool.py +130 -0
- aip_agents/examples/tools/mock_retrieval_tool.py +56 -0
- aip_agents/examples/tools/pii_demo_tools.py +189 -0
- aip_agents/examples/tools/random_chart_tool.py +142 -0
- aip_agents/examples/tools/serper_tool.py +202 -0
- aip_agents/examples/tools/stock_tools.py +82 -0
- aip_agents/examples/tools/table_generator_tool.py +167 -0
- aip_agents/examples/tools/time_tool.py +82 -0
- aip_agents/examples/tools/weather_forecast_tool.py +38 -0
- aip_agents/executor/agent_executor.py +473 -0
- aip_agents/executor/base.py +48 -0
- aip_agents/mcp/__init__.py +1 -0
- aip_agents/mcp/client/__init__.py +14 -0
- aip_agents/mcp/client/base_mcp_client.py +369 -0
- aip_agents/mcp/client/connection_manager.py +193 -0
- aip_agents/mcp/client/google_adk/__init__.py +11 -0
- aip_agents/mcp/client/google_adk/client.py +381 -0
- aip_agents/mcp/client/langchain/__init__.py +11 -0
- aip_agents/mcp/client/langchain/client.py +265 -0
- aip_agents/mcp/client/persistent_session.py +359 -0
- aip_agents/mcp/client/session_pool.py +351 -0
- aip_agents/mcp/client/transports.py +215 -0
- aip_agents/mcp/utils/__init__.py +7 -0
- aip_agents/mcp/utils/config_validator.py +139 -0
- aip_agents/memory/__init__.py +14 -0
- aip_agents/memory/adapters/__init__.py +10 -0
- aip_agents/memory/adapters/base_adapter.py +717 -0
- aip_agents/memory/adapters/mem0.py +84 -0
- aip_agents/memory/base.py +84 -0
- aip_agents/memory/constants.py +49 -0
- aip_agents/memory/factory.py +86 -0
- aip_agents/memory/guidance.py +20 -0
- aip_agents/memory/simple_memory.py +47 -0
- aip_agents/middleware/__init__.py +17 -0
- aip_agents/middleware/base.py +88 -0
- aip_agents/middleware/manager.py +128 -0
- aip_agents/middleware/todolist.py +274 -0
- aip_agents/schema/__init__.py +69 -0
- aip_agents/schema/a2a.py +56 -0
- aip_agents/schema/agent.py +111 -0
- aip_agents/schema/hitl.py +157 -0
- aip_agents/schema/langgraph.py +37 -0
- aip_agents/schema/model_id.py +97 -0
- aip_agents/schema/step_limit.py +108 -0
- aip_agents/schema/storage.py +40 -0
- aip_agents/sentry/__init__.py +11 -0
- aip_agents/sentry/sentry.py +151 -0
- aip_agents/storage/__init__.py +41 -0
- aip_agents/storage/base.py +85 -0
- aip_agents/storage/clients/__init__.py +12 -0
- aip_agents/storage/clients/minio_client.py +318 -0
- aip_agents/storage/config.py +62 -0
- aip_agents/storage/providers/__init__.py +15 -0
- aip_agents/storage/providers/base.py +106 -0
- aip_agents/storage/providers/memory.py +114 -0
- aip_agents/storage/providers/object_storage.py +214 -0
- aip_agents/tools/__init__.py +33 -0
- aip_agents/tools/bosa_tools.py +105 -0
- aip_agents/tools/browser_use/__init__.py +82 -0
- aip_agents/tools/browser_use/action_parser.py +103 -0
- aip_agents/tools/browser_use/browser_use_tool.py +1112 -0
- aip_agents/tools/browser_use/llm_config.py +120 -0
- aip_agents/tools/browser_use/minio_storage.py +198 -0
- aip_agents/tools/browser_use/schemas.py +119 -0
- aip_agents/tools/browser_use/session.py +76 -0
- aip_agents/tools/browser_use/session_errors.py +132 -0
- aip_agents/tools/browser_use/steel_session_recording.py +317 -0
- aip_agents/tools/browser_use/streaming.py +813 -0
- aip_agents/tools/browser_use/structured_data_parser.py +257 -0
- aip_agents/tools/browser_use/structured_data_recovery.py +204 -0
- aip_agents/tools/browser_use/types.py +78 -0
- aip_agents/tools/code_sandbox/__init__.py +26 -0
- aip_agents/tools/code_sandbox/constant.py +13 -0
- aip_agents/tools/code_sandbox/e2b_cloud_sandbox_extended.py +257 -0
- aip_agents/tools/code_sandbox/e2b_sandbox_tool.py +411 -0
- aip_agents/tools/constants.py +165 -0
- aip_agents/tools/document_loader/__init__.py +44 -0
- aip_agents/tools/document_loader/base_reader.py +302 -0
- aip_agents/tools/document_loader/docx_reader_tool.py +68 -0
- aip_agents/tools/document_loader/excel_reader_tool.py +171 -0
- aip_agents/tools/document_loader/pdf_reader_tool.py +79 -0
- aip_agents/tools/document_loader/pdf_splitter.py +169 -0
- aip_agents/tools/gl_connector/__init__.py +5 -0
- aip_agents/tools/gl_connector/tool.py +351 -0
- aip_agents/tools/memory_search/__init__.py +22 -0
- aip_agents/tools/memory_search/base.py +200 -0
- aip_agents/tools/memory_search/mem0.py +258 -0
- aip_agents/tools/memory_search/schema.py +48 -0
- aip_agents/tools/memory_search_tool.py +26 -0
- aip_agents/tools/time_tool.py +117 -0
- aip_agents/tools/tool_config_injector.py +300 -0
- aip_agents/tools/web_search/__init__.py +15 -0
- aip_agents/tools/web_search/serper_tool.py +187 -0
- aip_agents/types/__init__.py +70 -0
- aip_agents/types/a2a_events.py +13 -0
- aip_agents/utils/__init__.py +79 -0
- aip_agents/utils/a2a_connector.py +1757 -0
- aip_agents/utils/artifact_helpers.py +502 -0
- aip_agents/utils/constants.py +22 -0
- aip_agents/utils/datetime/__init__.py +34 -0
- aip_agents/utils/datetime/normalization.py +231 -0
- aip_agents/utils/datetime/timezone.py +206 -0
- aip_agents/utils/env_loader.py +27 -0
- aip_agents/utils/event_handler_registry.py +58 -0
- aip_agents/utils/file_prompt_utils.py +176 -0
- aip_agents/utils/final_response_builder.py +211 -0
- aip_agents/utils/formatter_llm_client.py +231 -0
- aip_agents/utils/langgraph/__init__.py +19 -0
- aip_agents/utils/langgraph/converter.py +128 -0
- aip_agents/utils/langgraph/tool_managers/__init__.py +15 -0
- aip_agents/utils/langgraph/tool_managers/a2a_tool_manager.py +99 -0
- aip_agents/utils/langgraph/tool_managers/base_tool_manager.py +66 -0
- aip_agents/utils/langgraph/tool_managers/delegation_tool_manager.py +1071 -0
- aip_agents/utils/langgraph/tool_output_management.py +967 -0
- aip_agents/utils/logger.py +195 -0
- aip_agents/utils/metadata/__init__.py +27 -0
- aip_agents/utils/metadata/activity_metadata_helper.py +407 -0
- aip_agents/utils/metadata/activity_narrative/__init__.py +35 -0
- aip_agents/utils/metadata/activity_narrative/builder.py +817 -0
- aip_agents/utils/metadata/activity_narrative/constants.py +51 -0
- aip_agents/utils/metadata/activity_narrative/context.py +49 -0
- aip_agents/utils/metadata/activity_narrative/formatters.py +230 -0
- aip_agents/utils/metadata/activity_narrative/utils.py +35 -0
- aip_agents/utils/metadata/schemas/__init__.py +16 -0
- aip_agents/utils/metadata/schemas/activity_schema.py +29 -0
- aip_agents/utils/metadata/schemas/thinking_schema.py +31 -0
- aip_agents/utils/metadata/thinking_metadata_helper.py +38 -0
- aip_agents/utils/metadata_helper.py +358 -0
- aip_agents/utils/name_preprocessor/__init__.py +17 -0
- aip_agents/utils/name_preprocessor/base_name_preprocessor.py +73 -0
- aip_agents/utils/name_preprocessor/google_name_preprocessor.py +100 -0
- aip_agents/utils/name_preprocessor/name_preprocessor.py +87 -0
- aip_agents/utils/name_preprocessor/openai_name_preprocessor.py +48 -0
- aip_agents/utils/pii/__init__.py +25 -0
- aip_agents/utils/pii/pii_handler.py +397 -0
- aip_agents/utils/pii/pii_helper.py +207 -0
- aip_agents/utils/pii/uuid_deanonymizer_mapping.py +195 -0
- aip_agents/utils/reference_helper.py +273 -0
- aip_agents/utils/sse_chunk_transformer.py +831 -0
- aip_agents/utils/step_limit_manager.py +265 -0
- aip_agents/utils/token_usage_helper.py +156 -0
- aip_agents_binary-0.5.20.dist-info/METADATA +681 -0
- aip_agents_binary-0.5.20.dist-info/RECORD +280 -0
- aip_agents_binary-0.5.20.dist-info/WHEEL +5 -0
- aip_agents_binary-0.5.20.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""Mem0-specific adapter built on top of the BaseMemoryAdapter.
|
|
2
|
+
|
|
3
|
+
Authors:
|
|
4
|
+
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
from gllm_memory import MemoryManager
|
|
14
|
+
|
|
15
|
+
_HAS_GLLM_MEMORY = True
|
|
16
|
+
except ImportError: # pragma: no cover
|
|
17
|
+
MemoryManager = Any # type: ignore[assignment]
|
|
18
|
+
_HAS_GLLM_MEMORY = False
|
|
19
|
+
|
|
20
|
+
from aip_agents.memory.adapters.base_adapter import BaseMemoryAdapter
|
|
21
|
+
from aip_agents.memory.constants import MemoryDefaults
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class Mem0Memory(BaseMemoryAdapter):
|
|
25
|
+
"""Mem0-backed long-term memory adapter using the gllm_memory SDK."""
|
|
26
|
+
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
*,
|
|
30
|
+
agent_id: str,
|
|
31
|
+
namespace: str | None = None,
|
|
32
|
+
limit: int = MemoryDefaults.RETRIEVAL_LIMIT,
|
|
33
|
+
max_chars: int = MemoryDefaults.MAX_CHARS,
|
|
34
|
+
host: str | None = None,
|
|
35
|
+
instruction: str | None = None,
|
|
36
|
+
) -> None:
|
|
37
|
+
"""Initialize the Mem0 memory adapter.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
agent_id: Unique identifier for the agent using this memory.
|
|
41
|
+
namespace: Optional namespace for organizing memories.
|
|
42
|
+
limit: Maximum number of memories to retrieve in search operations.
|
|
43
|
+
max_chars: Maximum character length for text content.
|
|
44
|
+
host: Optional host URL for the Mem0 service.
|
|
45
|
+
instruction: Optional instruction text for memory operations.
|
|
46
|
+
"""
|
|
47
|
+
if not _HAS_GLLM_MEMORY:
|
|
48
|
+
raise ImportError("optional dependency 'gllm-memory' is required for Mem0Memory")
|
|
49
|
+
|
|
50
|
+
manager = self._initialize_manager(host=host, instruction=instruction)
|
|
51
|
+
super().__init__(
|
|
52
|
+
agent_id=agent_id,
|
|
53
|
+
manager=manager,
|
|
54
|
+
namespace=namespace,
|
|
55
|
+
limit=limit,
|
|
56
|
+
max_chars=max_chars,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
@classmethod
|
|
60
|
+
def validate_env(cls) -> None:
|
|
61
|
+
"""Ensure the Mem0 API key is available."""
|
|
62
|
+
cls._resolve_api_key()
|
|
63
|
+
|
|
64
|
+
@staticmethod
|
|
65
|
+
def _resolve_api_key() -> str:
|
|
66
|
+
api_key = os.getenv("MEM0_API_KEY") or os.getenv("GLLM_MEMORY_API_KEY")
|
|
67
|
+
if not api_key:
|
|
68
|
+
raise ValueError("MEM0_API_KEY (or GLLM_MEMORY_API_KEY) must be configured for Mem0 access.")
|
|
69
|
+
return api_key
|
|
70
|
+
|
|
71
|
+
def _initialize_manager(self, host: str | None, instruction: str | None) -> MemoryManager:
|
|
72
|
+
"""Create a MemoryManager tailored for the Mem0 backend.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
host: Optional host URL for the Mem0 service.
|
|
76
|
+
instruction: Optional instruction text for memory operations.
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
Configured MemoryManager instance for Mem0.
|
|
80
|
+
"""
|
|
81
|
+
api_key = self._resolve_api_key()
|
|
82
|
+
resolved_host = host or os.getenv("MEM0_HOST") or os.getenv("GLLM_MEMORY_HOST")
|
|
83
|
+
resolved_instruction = instruction or os.getenv("GLLM_MEMORY_INSTRUCTION")
|
|
84
|
+
return MemoryManager(api_key=api_key, host=resolved_host, instruction=resolved_instruction)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""Base Memory Module for AIP Agents.
|
|
2
|
+
|
|
3
|
+
This module defines the BaseMemory class which serves as a template
|
|
4
|
+
for implementing different types of memory for agents.
|
|
5
|
+
|
|
6
|
+
Authors:
|
|
7
|
+
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
8
|
+
|
|
9
|
+
References:
|
|
10
|
+
https://github.com/GDP-ADMIN/gdplabs-exploration/blob/ai-agent-app/backend/aip_agents/memory/base.py
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from abc import ABC, abstractmethod
|
|
14
|
+
from collections.abc import Sequence
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
from aip_agents.types import ChatMessage
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class BaseMemory(ABC):
|
|
21
|
+
"""Base class for agent memory.
|
|
22
|
+
|
|
23
|
+
This concrete base class provides a default structure. Subclasses
|
|
24
|
+
can inherit from this class to implement specific memory management
|
|
25
|
+
behaviors.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
@classmethod
|
|
29
|
+
def validate_env(cls) -> None: # pragma: no cover - default no-op
|
|
30
|
+
"""Validate environment prerequisites for a memory backend.
|
|
31
|
+
|
|
32
|
+
This hook allows memory implementations to fail fast when required
|
|
33
|
+
environment variables or credentials are missing. Default is a no-op.
|
|
34
|
+
"""
|
|
35
|
+
return None
|
|
36
|
+
|
|
37
|
+
@abstractmethod
|
|
38
|
+
def get_messages(self) -> list[ChatMessage]:
|
|
39
|
+
"""Retrieve a list of messages.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
List[ChatMessage]: A list of messages in a generic format.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
@abstractmethod
|
|
46
|
+
def add_message(self, message: ChatMessage) -> None:
|
|
47
|
+
"""Add a message to the memory.
|
|
48
|
+
|
|
49
|
+
Adds a single ChatMessage to the memory storage. The exact implementation
|
|
50
|
+
depends on the specific memory backend.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
message: The ChatMessage object to add to memory. The message should
|
|
54
|
+
contain role and content information.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
def add_messages(self, messages: Sequence[ChatMessage]) -> None:
|
|
58
|
+
"""Add multiple messages to the memory.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
messages: A sequence of ChatMessage objects to add.
|
|
62
|
+
"""
|
|
63
|
+
for message in messages:
|
|
64
|
+
self.add_message(message)
|
|
65
|
+
|
|
66
|
+
@abstractmethod
|
|
67
|
+
def clear(self) -> None:
|
|
68
|
+
"""Clears the memory or resets the state of the agent.
|
|
69
|
+
|
|
70
|
+
This method must be implemented to define the specific behavior
|
|
71
|
+
for clearing or resetting the memory of the agent.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
def get_memory_variables(self) -> dict[str, Any]:
|
|
75
|
+
"""Retrieve memory variables.
|
|
76
|
+
|
|
77
|
+
This method returns a dictionary containing memory-related variables.
|
|
78
|
+
The default implementation returns a dictionary with chat_history.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
Dict[str, Any]: A dictionary where keys are variable names and values
|
|
82
|
+
are the corresponding memory-related data.
|
|
83
|
+
"""
|
|
84
|
+
return {"chat_history": self.get_messages()}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Memory constants for AIP Agents.
|
|
2
|
+
|
|
3
|
+
This module defines constants for memory-related method names and other
|
|
4
|
+
memory-related constants to avoid magic strings and numbers throughout the codebase.
|
|
5
|
+
|
|
6
|
+
Authors:
|
|
7
|
+
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class MemoryMethod:
|
|
12
|
+
"""Constants for memory method names used in hasattr checks."""
|
|
13
|
+
|
|
14
|
+
SEARCH = "search"
|
|
15
|
+
SAVE_INTERACTION = "save_interaction"
|
|
16
|
+
FORMAT_HITS = "format_hits"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class MemoryDefaults:
|
|
20
|
+
"""Default values for memory configuration parameters."""
|
|
21
|
+
|
|
22
|
+
# Memory retrieval and formatting limits
|
|
23
|
+
MAX_ITEMS = 8 # Maximum number of memory items to include in formatted output
|
|
24
|
+
RETRIEVAL_LIMIT = 5 # Default number of memories to retrieve per search
|
|
25
|
+
MAX_CHARS = 1500 # Maximum characters per memory entry when saving
|
|
26
|
+
|
|
27
|
+
# Logging and display
|
|
28
|
+
LOG_PREVIEW_LENGTH = 100 # Length of text preview in logs
|
|
29
|
+
SMALL_PREVIEW_LENGTH = 16 # Length of text preview in logs
|
|
30
|
+
|
|
31
|
+
# Agent ID generation
|
|
32
|
+
AGENT_ID_PREFIX = "agent-" # Prefix for generated agent IDs
|
|
33
|
+
|
|
34
|
+
# Memory formatting
|
|
35
|
+
MEMORY_TAG_OPEN = "<RELEVANT_MEMORY>"
|
|
36
|
+
MEMORY_TAG_CLOSE = "</RELEVANT_MEMORY>"
|
|
37
|
+
|
|
38
|
+
# Default user ID for compatibility
|
|
39
|
+
DEFAULT_USER_ID = "default"
|
|
40
|
+
|
|
41
|
+
# Date format constants
|
|
42
|
+
DATE_STRING_LENGTH = 10 # Length of "YYYY-MM-DD" format strings
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class MemoryBackends:
|
|
46
|
+
"""Supported memory backend identifiers."""
|
|
47
|
+
|
|
48
|
+
MEM0 = "mem0"
|
|
49
|
+
SUPPORTED = frozenset({MEM0})
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""Memory factory for creating memory adapters by backend string.
|
|
2
|
+
|
|
3
|
+
This module provides a single point of construction for memory backends,
|
|
4
|
+
so agent code does not depend on concrete memory implementations.
|
|
5
|
+
|
|
6
|
+
Authors:
|
|
7
|
+
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import importlib
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from aip_agents.memory.base import BaseMemory
|
|
14
|
+
from aip_agents.utils.logger import get_logger
|
|
15
|
+
|
|
16
|
+
logger = get_logger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
BACKENDS = {
|
|
20
|
+
# backend_name: "module_path:ClassName"
|
|
21
|
+
"mem0": "aip_agents.memory.adapters.mem0:Mem0Memory",
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _import_class(path: str) -> type[BaseMemory]:
|
|
26
|
+
"""Import a memory class from a module path string.
|
|
27
|
+
|
|
28
|
+
Dynamically imports a memory implementation class from a string path in
|
|
29
|
+
the format "module_path:ClassName".
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
path: A string in the format "module_path:ClassName" specifying the
|
|
33
|
+
module and class to import.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
type[BaseMemory]: The imported memory class that implements the
|
|
37
|
+
BaseMemory interface.
|
|
38
|
+
|
|
39
|
+
Raises:
|
|
40
|
+
ModuleNotFoundError: If the specified module cannot be imported.
|
|
41
|
+
AttributeError: If the specified class does not exist in the module.
|
|
42
|
+
ValueError: If the path format is invalid (does not contain ':').
|
|
43
|
+
"""
|
|
44
|
+
module_path, class_name = path.split(":", 1)
|
|
45
|
+
module = importlib.import_module(module_path)
|
|
46
|
+
memory_cls = getattr(module, class_name)
|
|
47
|
+
return memory_cls
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class MemoryFactory:
|
|
51
|
+
"""Factory to build concrete memory adapters by backend name."""
|
|
52
|
+
|
|
53
|
+
@staticmethod
|
|
54
|
+
def create(backend: str, **kwargs: Any) -> BaseMemory:
|
|
55
|
+
"""Create a memory adapter instance.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
backend: Backend identifier (e.g., "mem0").
|
|
59
|
+
**kwargs: Keyword args passed to adapter constructor (e.g., limit, max_chars, namespace).
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
BaseMemory: A constructed memory adapter instance.
|
|
63
|
+
|
|
64
|
+
Raises:
|
|
65
|
+
ValueError: If backend is unknown or adapter can't be constructed.
|
|
66
|
+
"""
|
|
67
|
+
if backend not in BACKENDS:
|
|
68
|
+
raise ValueError(f"Unknown memory backend: {backend}")
|
|
69
|
+
|
|
70
|
+
cls_path = BACKENDS[backend]
|
|
71
|
+
memory_cls = _import_class(cls_path)
|
|
72
|
+
|
|
73
|
+
# Validate environment (adapter may raise)
|
|
74
|
+
try:
|
|
75
|
+
if hasattr(memory_cls, "validate_env"):
|
|
76
|
+
memory_cls.validate_env() # type: ignore[misc]
|
|
77
|
+
except Exception as e: # noqa: BLE001
|
|
78
|
+
logger.error(f"MemoryFactory: environment validation failed for '{backend}': {e}")
|
|
79
|
+
raise
|
|
80
|
+
|
|
81
|
+
try:
|
|
82
|
+
instance = memory_cls(**kwargs)
|
|
83
|
+
return instance
|
|
84
|
+
except Exception as e: # noqa: BLE001
|
|
85
|
+
logger.error(f"MemoryFactory: failed to create memory adapter '{backend}': {e}")
|
|
86
|
+
raise
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""Guidance text helpers for Mem0-based memory recall.
|
|
2
|
+
|
|
3
|
+
Authors:
|
|
4
|
+
- Putu Ravindra Wiguna (putu.r.wiguna@gdplabs.id)
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import textwrap
|
|
8
|
+
|
|
9
|
+
MEM0_MEMORY_RECALL_GUIDANCE = textwrap.dedent("""
|
|
10
|
+
Memory Recall Guidance:
|
|
11
|
+
- Use `built_in_mem0_search` whenever you need facts from long-term memory.
|
|
12
|
+
- You can also use `built_in_mem0_search` for explicit recall or recap requests of
|
|
13
|
+
conversation/chat/discussion by the user.
|
|
14
|
+
Reach for it when prior context such as but not limited to: names, decisions, or
|
|
15
|
+
preferences, will help you respond.
|
|
16
|
+
It is also appropriate when the user asks about the AI's previous responses.
|
|
17
|
+
- Provide a concise `query` that describes what you are looking for (e.g., 'project plan', 'user preferences').
|
|
18
|
+
- If the user specifies a time period for a memory, set `time_period` according to user query
|
|
19
|
+
to retrieve memories based on that time period.
|
|
20
|
+
""").strip()
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""Simple Memory Module for AIP Agents.
|
|
2
|
+
|
|
3
|
+
This module provides a basic implementation of the BaseMemory class
|
|
4
|
+
that stores messages in a simple list structure.
|
|
5
|
+
|
|
6
|
+
Authors:
|
|
7
|
+
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from aip_agents.memory.base import BaseMemory, ChatMessage
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class SimpleMemory(BaseMemory):
|
|
14
|
+
"""A simple memory implementation that stores messages in a list."""
|
|
15
|
+
|
|
16
|
+
def __init__(self):
|
|
17
|
+
"""Initialize the SimpleMemory instance with an empty message list."""
|
|
18
|
+
self.messages: list[ChatMessage] = []
|
|
19
|
+
|
|
20
|
+
def add_message(self, message_or_role, content=None) -> None:
|
|
21
|
+
"""Add a message to memory.
|
|
22
|
+
|
|
23
|
+
Supports two calling patterns for backward compatibility:
|
|
24
|
+
1. add_message(ChatMessage) - Adds a ChatMessage object directly
|
|
25
|
+
2. add_message(role, content) - Creates and adds a ChatMessage with the given role and content
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
message_or_role: Either a ChatMessage object or a string role (e.g., "user", "assistant").
|
|
29
|
+
content: Optional content string when using the role+content pattern.
|
|
30
|
+
Required when message_or_role is a string role.
|
|
31
|
+
"""
|
|
32
|
+
if content is not None:
|
|
33
|
+
# Using the role+content pattern
|
|
34
|
+
self.messages.append(ChatMessage(role=message_or_role, content=content))
|
|
35
|
+
else:
|
|
36
|
+
# Using the ChatMessage object pattern
|
|
37
|
+
self.messages.append(message_or_role)
|
|
38
|
+
|
|
39
|
+
def get_messages(self) -> list[ChatMessage]:
|
|
40
|
+
"""Get all messages from memory."""
|
|
41
|
+
return self.messages
|
|
42
|
+
|
|
43
|
+
def clear(self) -> None:
|
|
44
|
+
"""Clear all messages from memory."""
|
|
45
|
+
self.messages = []
|
|
46
|
+
|
|
47
|
+
# Uses the default get_memory_variables implementation from BaseMemory
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Planning Middleware package.
|
|
2
|
+
|
|
3
|
+
Provides composable middleware components for enhancing agent capabilities with
|
|
4
|
+
planning and custom lifecycle hooks.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from aip_agents.middleware.base import AgentMiddleware, ModelRequest
|
|
8
|
+
from aip_agents.middleware.manager import MiddlewareManager
|
|
9
|
+
from aip_agents.middleware.todolist import TodoListMiddleware, TodoStatus
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"AgentMiddleware",
|
|
13
|
+
"ModelRequest",
|
|
14
|
+
"MiddlewareManager",
|
|
15
|
+
"TodoListMiddleware",
|
|
16
|
+
"TodoStatus",
|
|
17
|
+
]
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""Base protocol and types for agent middleware.
|
|
2
|
+
|
|
3
|
+
This module defines the core AgentMiddleware protocol that all middleware components
|
|
4
|
+
must implement, along with the ModelRequest type for model invocations.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any, Protocol, TypedDict
|
|
8
|
+
|
|
9
|
+
from langchain_core.tools import BaseTool
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ModelRequest(TypedDict, total=False):
|
|
13
|
+
"""Represents parameters for a model invocation that middleware can modify.
|
|
14
|
+
|
|
15
|
+
This TypedDict defines the structure of requests passed to the LLM, allowing
|
|
16
|
+
middleware to add tools or modify prompts before each invocation.
|
|
17
|
+
|
|
18
|
+
Attributes:
|
|
19
|
+
messages: List of messages in the conversation.
|
|
20
|
+
tools: List of tools available to the model.
|
|
21
|
+
system_prompt: System-level instruction for the model.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
messages: list[Any]
|
|
25
|
+
tools: list[BaseTool]
|
|
26
|
+
system_prompt: str
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class AgentMiddleware(Protocol):
|
|
30
|
+
"""Protocol defining the interface for composable agent middleware.
|
|
31
|
+
|
|
32
|
+
Middleware components can contribute tools, enhance system prompts, and provide
|
|
33
|
+
lifecycle hooks that execute before, during, and after model invocations.
|
|
34
|
+
|
|
35
|
+
All middleware must implement this protocol to be compatible with MiddlewareManager.
|
|
36
|
+
|
|
37
|
+
Attributes:
|
|
38
|
+
tools: List of tools contributed by this middleware.
|
|
39
|
+
system_prompt_additions: Optional text to append to the agent's system prompt.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
tools: list[BaseTool]
|
|
43
|
+
system_prompt_additions: str | None
|
|
44
|
+
|
|
45
|
+
def before_model(self, state: dict[str, Any]) -> dict[str, Any]:
|
|
46
|
+
"""Hook executed before each model invocation.
|
|
47
|
+
|
|
48
|
+
Use this hook to prepare state, log context, or perform setup tasks
|
|
49
|
+
before the model is called.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
state: Current agent state containing messages and other context.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
Dict of state updates to merge into the agent state. Return empty dict
|
|
56
|
+
if no updates are needed.
|
|
57
|
+
"""
|
|
58
|
+
return {} # pragma: no cover # Protocol default - cannot be executed directly
|
|
59
|
+
|
|
60
|
+
def modify_model_request(self, request: ModelRequest, state: dict[str, Any]) -> ModelRequest:
|
|
61
|
+
"""Hook to modify the model request before invocation.
|
|
62
|
+
|
|
63
|
+
Use this hook to add tools, modify the system prompt, adjust model parameters,
|
|
64
|
+
or change tool selection strategy.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
request: The model request that will be sent to the LLM.
|
|
68
|
+
state: Current agent state for context.
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
Modified ModelRequest. Can return the same request if no changes needed.
|
|
72
|
+
"""
|
|
73
|
+
return request # pragma: no cover # Protocol default - cannot be executed directly # pragma: no cover # Protocol default - cannot be executed directly
|
|
74
|
+
|
|
75
|
+
def after_model(self, state: dict[str, Any]) -> dict[str, Any]:
|
|
76
|
+
"""Hook executed after each model invocation.
|
|
77
|
+
|
|
78
|
+
Use this hook for cleanup, logging, state updates, or post-processing
|
|
79
|
+
of model outputs.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
state: Current agent state after model invocation.
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
Dict of state updates to merge into the agent state. Return empty dict
|
|
86
|
+
if no updates are needed.
|
|
87
|
+
"""
|
|
88
|
+
return {} # pragma: no cover # Protocol default - cannot be executed directly
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""Middleware orchestration and lifecycle management.
|
|
2
|
+
|
|
3
|
+
This module provides MiddlewareManager, which coordinates multiple middleware
|
|
4
|
+
components and manages their lifecycle hooks.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from langchain_core.tools import BaseTool
|
|
10
|
+
|
|
11
|
+
from aip_agents.middleware.base import AgentMiddleware, ModelRequest
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class MiddlewareManager:
|
|
15
|
+
"""Orchestrates multiple middleware components and manages hook execution.
|
|
16
|
+
|
|
17
|
+
The manager collects tools from all middleware, builds enhanced system prompts,
|
|
18
|
+
and executes lifecycle hooks in the correct order (forward for setup, reverse
|
|
19
|
+
for cleanup).
|
|
20
|
+
|
|
21
|
+
Attributes:
|
|
22
|
+
middleware: List of middleware components in registration order.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self, middleware: list[AgentMiddleware]) -> None:
|
|
26
|
+
"""Initialize the middleware manager.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
middleware: List of middleware components to manage. Order matters:
|
|
30
|
+
hooks execute forward (first to last) for before/modify,
|
|
31
|
+
and reverse (last to first) for after.
|
|
32
|
+
"""
|
|
33
|
+
self.middleware = middleware
|
|
34
|
+
|
|
35
|
+
def get_all_tools(self) -> list[BaseTool]:
|
|
36
|
+
"""Collect tools from all registered middleware.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
Combined list of all tools contributed by all middleware components.
|
|
40
|
+
Empty list if no middleware or if middleware provide no tools.
|
|
41
|
+
"""
|
|
42
|
+
tools: list[BaseTool] = []
|
|
43
|
+
for mw in self.middleware:
|
|
44
|
+
tools.extend(mw.tools)
|
|
45
|
+
return tools
|
|
46
|
+
|
|
47
|
+
def build_system_prompt(self, base_instruction: str) -> str:
|
|
48
|
+
"""Build enhanced system prompt by concatenating base instruction with middleware additions.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
base_instruction: The base system prompt for the agent.
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
Enhanced system prompt with all middleware additions appended.
|
|
55
|
+
If no middleware provide additions, returns base_instruction unchanged.
|
|
56
|
+
"""
|
|
57
|
+
parts = [base_instruction]
|
|
58
|
+
|
|
59
|
+
for mw in self.middleware:
|
|
60
|
+
if mw.system_prompt_additions:
|
|
61
|
+
parts.append(mw.system_prompt_additions)
|
|
62
|
+
|
|
63
|
+
return "\n\n".join(parts)
|
|
64
|
+
|
|
65
|
+
def before_model(self, state: dict[str, Any]) -> dict[str, Any]:
|
|
66
|
+
"""Execute before_model hooks for all middleware in forward order.
|
|
67
|
+
|
|
68
|
+
Hooks run first to last, allowing earlier middleware to prepare state
|
|
69
|
+
for later middleware.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
state: Current agent state.
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
Merged dictionary of all state updates from all middleware.
|
|
76
|
+
Updates are accumulated in order of execution.
|
|
77
|
+
"""
|
|
78
|
+
state_updates: dict[str, Any] = {}
|
|
79
|
+
|
|
80
|
+
for mw in self.middleware:
|
|
81
|
+
mw_updates = mw.before_model(state)
|
|
82
|
+
if mw_updates:
|
|
83
|
+
state_updates.update(mw_updates)
|
|
84
|
+
|
|
85
|
+
return state_updates
|
|
86
|
+
|
|
87
|
+
def modify_model_request(self, request: ModelRequest, state: dict[str, Any]) -> ModelRequest:
|
|
88
|
+
"""Execute modify_model_request hooks for all middleware in forward order.
|
|
89
|
+
|
|
90
|
+
Each middleware receives the request modified by previous middleware,
|
|
91
|
+
allowing them to build on each other's changes.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
request: The model request to be modified.
|
|
95
|
+
state: Current agent state for context.
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
Final modified request after all middleware have processed it.
|
|
99
|
+
"""
|
|
100
|
+
current_request = request
|
|
101
|
+
|
|
102
|
+
for mw in self.middleware:
|
|
103
|
+
current_request = mw.modify_model_request(current_request, state)
|
|
104
|
+
|
|
105
|
+
return current_request
|
|
106
|
+
|
|
107
|
+
def after_model(self, state: dict[str, Any]) -> dict[str, Any]:
|
|
108
|
+
"""Execute after_model hooks for all middleware in reverse order.
|
|
109
|
+
|
|
110
|
+
Hooks run last to first (reverse of registration order), allowing
|
|
111
|
+
proper cleanup and unwinding of middleware operations.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
state: Current agent state after model invocation.
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
Merged dictionary of all state updates from all middleware.
|
|
118
|
+
Updates are accumulated in reverse order of execution.
|
|
119
|
+
"""
|
|
120
|
+
updates: dict[str, Any] = {}
|
|
121
|
+
|
|
122
|
+
# Execute in reverse order
|
|
123
|
+
for mw in reversed(self.middleware):
|
|
124
|
+
mw_updates = mw.after_model(state)
|
|
125
|
+
if mw_updates:
|
|
126
|
+
updates.update(mw_updates)
|
|
127
|
+
|
|
128
|
+
return updates
|