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,502 @@
|
|
|
1
|
+
"""Utility functions for artifact handling in agent tools.
|
|
2
|
+
|
|
3
|
+
This module provides standardized functions for creating artifact responses
|
|
4
|
+
that are compatible with the A2A protocol and the agent's artifact handling system.
|
|
5
|
+
|
|
6
|
+
Authors:
|
|
7
|
+
Christian Trisno Sen Long Chen (christian.t.s.l.chen@gdplabs.id)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import base64
|
|
11
|
+
import hashlib
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from deprecated import deprecated
|
|
15
|
+
from langgraph.types import Command
|
|
16
|
+
from pydantic import ValidationError
|
|
17
|
+
|
|
18
|
+
from aip_agents.a2a.types import Artifact, ArtifactType, MimeType, get_mime_type_from_filename
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ArtifactHandler:
|
|
22
|
+
"""Handler class for creating and managing artifacts in agent tools.
|
|
23
|
+
|
|
24
|
+
This class provides a clean, object-oriented interface for artifact creation
|
|
25
|
+
with built-in validation, deduplication, and standardized formatting.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self):
|
|
29
|
+
"""Initialize the ArtifactHandler."""
|
|
30
|
+
self._artifact_cache: dict[str, dict[str, Any]] = {}
|
|
31
|
+
|
|
32
|
+
@deprecated(version="0.5.0", reason="Use create_artifact(...) which handles text and binary.")
|
|
33
|
+
def create_file_artifact( # noqa: PLR0913
|
|
34
|
+
self,
|
|
35
|
+
result: str,
|
|
36
|
+
artifact_data: bytes | str,
|
|
37
|
+
artifact_name: str,
|
|
38
|
+
artifact_description: str = "",
|
|
39
|
+
mime_type: str | None = None,
|
|
40
|
+
enable_deduplication: bool = True,
|
|
41
|
+
) -> dict[str, Any]:
|
|
42
|
+
"""Deprecated. Use create_artifact instead.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
result (str): The message/result to show to the agent.
|
|
46
|
+
artifact_data (bytes | str): The binary data for the artifact.
|
|
47
|
+
artifact_name (str): The name for the artifact file.
|
|
48
|
+
artifact_description (str, optional): Description of the artifact. Defaults to "".
|
|
49
|
+
mime_type (str | None, optional): MIME type of the artifact. If None, will be auto-detected.
|
|
50
|
+
enable_deduplication (bool, optional): Whether to deduplicate by content hash. Defaults to True.
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
dict[str, Any]: Dictionary with 'result' and 'artifact' keys.
|
|
54
|
+
"""
|
|
55
|
+
return self.create_artifact(
|
|
56
|
+
result=result,
|
|
57
|
+
data=artifact_data,
|
|
58
|
+
artifact_name=artifact_name,
|
|
59
|
+
artifact_description=artifact_description,
|
|
60
|
+
mime_type=mime_type,
|
|
61
|
+
enable_deduplication=enable_deduplication,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
@deprecated(version="0.5.0", reason="Use create_artifact(...) which handles text and binary.")
|
|
65
|
+
def create_text_artifact( # noqa: PLR0913
|
|
66
|
+
self,
|
|
67
|
+
result: str,
|
|
68
|
+
artifact_text: str,
|
|
69
|
+
artifact_name: str,
|
|
70
|
+
artifact_description: str = "",
|
|
71
|
+
mime_type: str | None = None,
|
|
72
|
+
enable_deduplication: bool = True,
|
|
73
|
+
) -> dict[str, Any]:
|
|
74
|
+
"""Deprecated. Use create_artifact instead.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
result (str): The message/result to show to the agent.
|
|
78
|
+
artifact_text (str): The text content for the artifact.
|
|
79
|
+
artifact_name (str): The name for the artifact file.
|
|
80
|
+
artifact_description (str, optional): Description of the artifact. Defaults to "".
|
|
81
|
+
mime_type (str | None, optional): MIME type of the artifact. If None, will be auto-detected.
|
|
82
|
+
enable_deduplication (bool, optional): Whether to deduplicate by content hash. Defaults to True.
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
dict[str, Any]: Dictionary with 'result' and 'artifact' keys.
|
|
86
|
+
"""
|
|
87
|
+
return self.create_artifact(
|
|
88
|
+
result=result,
|
|
89
|
+
data=artifact_text,
|
|
90
|
+
artifact_name=artifact_name,
|
|
91
|
+
artifact_description=artifact_description,
|
|
92
|
+
mime_type=mime_type,
|
|
93
|
+
enable_deduplication=enable_deduplication,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
def create_artifact( # noqa: PLR0913
|
|
97
|
+
self,
|
|
98
|
+
result: str,
|
|
99
|
+
data: bytes | str,
|
|
100
|
+
artifact_name: str,
|
|
101
|
+
artifact_description: str = "",
|
|
102
|
+
mime_type: str | None = None,
|
|
103
|
+
enable_deduplication: bool = True,
|
|
104
|
+
) -> dict[str, Any]:
|
|
105
|
+
"""Create an artifact with automatic text/binary handling.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
result: The message/result to show to the agent.
|
|
109
|
+
data: The data for the artifact. Bytes for binary; str for text or base64.
|
|
110
|
+
artifact_name: The filename to present to users.
|
|
111
|
+
artifact_description: Description of the artifact.
|
|
112
|
+
mime_type: Optional MIME type. If None, inferred from filename.
|
|
113
|
+
enable_deduplication: Whether to deduplicate by content hash.
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
Dictionary with 'result' and 'artifact' keys.
|
|
117
|
+
"""
|
|
118
|
+
inferred_mime = self._infer_mime_type(artifact_name, mime_type)
|
|
119
|
+
is_text_like = self._is_text_mime(inferred_mime)
|
|
120
|
+
|
|
121
|
+
artifact_b64 = self._encode_to_base64(data, treat_as_text=is_text_like)
|
|
122
|
+
effective_mime = (
|
|
123
|
+
inferred_mime
|
|
124
|
+
if not is_text_like or inferred_mime != MimeType.APPLICATION_OCTET_STREAM
|
|
125
|
+
else MimeType.TEXT_PLAIN
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# Deduplication check
|
|
129
|
+
if enable_deduplication:
|
|
130
|
+
artifact_hash = self.generate_artifact_hash(artifact_b64, artifact_name, effective_mime)
|
|
131
|
+
cached = self._artifact_cache.get(artifact_hash)
|
|
132
|
+
if cached:
|
|
133
|
+
return {
|
|
134
|
+
"result": f"Using cached artifact: {cached['artifact']['name']}",
|
|
135
|
+
"artifact": cached["artifact"],
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
artifact_payload = self._build_artifact_payload(
|
|
139
|
+
b64_data=artifact_b64,
|
|
140
|
+
name=artifact_name,
|
|
141
|
+
description=artifact_description,
|
|
142
|
+
mime=effective_mime,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
response = {"result": result, "artifact": artifact_payload}
|
|
146
|
+
|
|
147
|
+
if enable_deduplication:
|
|
148
|
+
artifact_hash = self.generate_artifact_hash(artifact_b64, artifact_name, effective_mime)
|
|
149
|
+
self._artifact_cache[artifact_hash] = response
|
|
150
|
+
|
|
151
|
+
return response
|
|
152
|
+
|
|
153
|
+
def _infer_mime_type(self, artifact_name: str, override_mime_type: str | None) -> str:
|
|
154
|
+
"""Return explicit MIME if provided; otherwise infer from filename.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
artifact_name (str): The name of the artifact.
|
|
158
|
+
override_mime_type (str | None): The MIME type to override.
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
str: The inferred MIME type.
|
|
162
|
+
"""
|
|
163
|
+
return override_mime_type or get_mime_type_from_filename(artifact_name)
|
|
164
|
+
|
|
165
|
+
def _is_text_mime(self, mime: str) -> bool:
|
|
166
|
+
"""Check whether a MIME type should be treated as text content.
|
|
167
|
+
|
|
168
|
+
Args:
|
|
169
|
+
mime (str): The MIME type to check.
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
bool: True if the MIME type should be treated as text content, False otherwise.
|
|
173
|
+
"""
|
|
174
|
+
return mime.startswith("text/") or mime == MimeType.APPLICATION_JSON
|
|
175
|
+
|
|
176
|
+
def _encode_to_base64(self, data: bytes | str, treat_as_text: bool) -> str:
|
|
177
|
+
"""Encode data to base64 according to handling rules for text/binary.
|
|
178
|
+
|
|
179
|
+
- For text: always base64-encode UTF-8 bytes
|
|
180
|
+
- For binary: bytes are base64-encoded; str is assumed already base64
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
data (bytes | str): The data to encode.
|
|
184
|
+
treat_as_text (bool): Whether to treat the data as text.
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
str: The base64 encoded data.
|
|
188
|
+
"""
|
|
189
|
+
if treat_as_text:
|
|
190
|
+
if isinstance(data, str):
|
|
191
|
+
raw = data.encode("utf-8")
|
|
192
|
+
elif isinstance(data, bytes):
|
|
193
|
+
raw = data
|
|
194
|
+
else:
|
|
195
|
+
raise TypeError("data must be bytes or str")
|
|
196
|
+
return base64.b64encode(raw).decode("utf-8")
|
|
197
|
+
|
|
198
|
+
# binary path
|
|
199
|
+
if isinstance(data, bytes):
|
|
200
|
+
return base64.b64encode(data).decode("utf-8")
|
|
201
|
+
if isinstance(data, str):
|
|
202
|
+
return data
|
|
203
|
+
raise TypeError("data must be bytes or str")
|
|
204
|
+
|
|
205
|
+
def _build_artifact_payload(self, b64_data: str, name: str, description: str, mime: str) -> dict[str, Any]:
|
|
206
|
+
"""Construct a validated artifact payload as a dict via the Artifact model.
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
b64_data (str): Base64 encoded data.
|
|
210
|
+
name (str): Artifact name.
|
|
211
|
+
description (str): Artifact description.
|
|
212
|
+
mime (str): MIME type.
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
dict[str, Any]: Dictionary with 'artifact_type', 'data', 'name', 'description', and 'mime_type' keys.
|
|
216
|
+
"""
|
|
217
|
+
return Artifact(
|
|
218
|
+
artifact_type=ArtifactType.FILE,
|
|
219
|
+
data=b64_data,
|
|
220
|
+
name=name,
|
|
221
|
+
description=description,
|
|
222
|
+
mime_type=mime,
|
|
223
|
+
).model_dump()
|
|
224
|
+
|
|
225
|
+
def create_error_response(self, error_message: str) -> str:
|
|
226
|
+
"""Create a standardized error response for tools.
|
|
227
|
+
|
|
228
|
+
Args:
|
|
229
|
+
error_message: The error message to return.
|
|
230
|
+
|
|
231
|
+
Returns:
|
|
232
|
+
String with error information.
|
|
233
|
+
"""
|
|
234
|
+
return f"Error: {error_message}"
|
|
235
|
+
|
|
236
|
+
def clear_cache(self) -> None:
|
|
237
|
+
"""Clear the artifact cache."""
|
|
238
|
+
self._artifact_cache.clear()
|
|
239
|
+
|
|
240
|
+
def get_cache_size(self) -> int:
|
|
241
|
+
"""Get the number of cached artifacts.
|
|
242
|
+
|
|
243
|
+
Returns:
|
|
244
|
+
Number of artifacts in cache.
|
|
245
|
+
"""
|
|
246
|
+
return len(self._artifact_cache)
|
|
247
|
+
|
|
248
|
+
@staticmethod
|
|
249
|
+
def generate_artifact_hash(artifact_data: str, name: str, mime_type: str) -> str:
|
|
250
|
+
"""Generate a hash for artifact deduplication.
|
|
251
|
+
|
|
252
|
+
Args:
|
|
253
|
+
artifact_data: Base64 encoded artifact data.
|
|
254
|
+
name: Artifact name.
|
|
255
|
+
mime_type: MIME type.
|
|
256
|
+
|
|
257
|
+
Returns:
|
|
258
|
+
Hash string for deduplication.
|
|
259
|
+
"""
|
|
260
|
+
content = f"{artifact_data}:{name}:{mime_type}"
|
|
261
|
+
return hashlib.sha256(content.encode()).hexdigest()
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
# Global instance for convenience
|
|
265
|
+
_default_handler = ArtifactHandler()
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
@deprecated(version="0.5.0", reason="Use create_artifact_command(...) which returns a Command update.")
|
|
269
|
+
def create_artifact_response( # noqa: PLR0913
|
|
270
|
+
result: str,
|
|
271
|
+
artifact_data: bytes | str,
|
|
272
|
+
artifact_name: str,
|
|
273
|
+
artifact_description: str = "",
|
|
274
|
+
mime_type: str | None = None,
|
|
275
|
+
) -> dict[str, Any]:
|
|
276
|
+
r"""Create a standardized artifact response for tools.
|
|
277
|
+
|
|
278
|
+
This function creates a response that separates the agent-facing result
|
|
279
|
+
from the user-facing artifact, following the established pattern for
|
|
280
|
+
artifact generation in the agent system.
|
|
281
|
+
|
|
282
|
+
Args:
|
|
283
|
+
result: The message/result to show to the agent (clean, no file data).
|
|
284
|
+
artifact_data: The binary data or base64 string for the artifact.
|
|
285
|
+
artifact_name: The name for the artifact file.
|
|
286
|
+
artifact_description: Description of the artifact. Defaults to "".
|
|
287
|
+
mime_type: MIME type of the artifact. If None, will be auto-detected from filename.
|
|
288
|
+
|
|
289
|
+
Returns:
|
|
290
|
+
Dictionary with 'result' and 'artifacts' keys (artifacts is always a list).
|
|
291
|
+
|
|
292
|
+
Example:
|
|
293
|
+
>>> import io
|
|
294
|
+
>>> csv_data = "Name,Age\\nAlice,30\\nBob,25"
|
|
295
|
+
>>> response = create_artifact_response(
|
|
296
|
+
... result="Generated a 2-row CSV table",
|
|
297
|
+
... artifact_data=csv_data.encode('utf-8'),
|
|
298
|
+
... artifact_name="data.csv",
|
|
299
|
+
... artifact_description="Sample data table",
|
|
300
|
+
... mime_type="text/csv"
|
|
301
|
+
... )
|
|
302
|
+
>>> assert "result" in response
|
|
303
|
+
>>> assert "artifacts" in response
|
|
304
|
+
>>> assert isinstance(response["artifacts"], list)
|
|
305
|
+
"""
|
|
306
|
+
single_artifact = _default_handler.create_artifact(
|
|
307
|
+
result=result,
|
|
308
|
+
data=artifact_data,
|
|
309
|
+
artifact_name=artifact_name,
|
|
310
|
+
artifact_description=artifact_description,
|
|
311
|
+
mime_type=mime_type,
|
|
312
|
+
enable_deduplication=False,
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
# Convert single artifact to list format
|
|
316
|
+
return {"result": single_artifact["result"], "artifacts": [single_artifact["artifact"]]}
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
@deprecated(version="0.5.0", reason="Use create_artifact_command(...) which handles text and binary.")
|
|
320
|
+
def create_text_artifact_response(
|
|
321
|
+
result: str,
|
|
322
|
+
artifact_text: str,
|
|
323
|
+
artifact_name: str,
|
|
324
|
+
artifact_description: str = "",
|
|
325
|
+
mime_type: str | None = None,
|
|
326
|
+
) -> dict[str, Any]:
|
|
327
|
+
"""Create a standardized artifact response for tools.
|
|
328
|
+
|
|
329
|
+
Args:
|
|
330
|
+
result: The message/result to show to the agent.
|
|
331
|
+
artifact_text: The text content for the artifact.
|
|
332
|
+
artifact_name: The name for the artifact file.
|
|
333
|
+
artifact_description: Description of the artifact.
|
|
334
|
+
mime_type: MIME type of the artifact. If None, will be auto-detected from filename.
|
|
335
|
+
|
|
336
|
+
Returns:
|
|
337
|
+
Dictionary with 'result' and 'artifacts' keys (artifacts is always a list).
|
|
338
|
+
"""
|
|
339
|
+
return _default_handler.create_artifact(
|
|
340
|
+
result=result,
|
|
341
|
+
data=artifact_text,
|
|
342
|
+
artifact_name=artifact_name,
|
|
343
|
+
artifact_description=artifact_description,
|
|
344
|
+
mime_type=mime_type,
|
|
345
|
+
enable_deduplication=False,
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
@deprecated(version="0.5.0", reason="Use create_artifact_command(...) and compose updates instead of dict responses.")
|
|
350
|
+
def create_multiple_artifacts_response(result: str, artifacts: list[dict[str, Any]]) -> dict[str, Any]:
|
|
351
|
+
"""Create a standardized response for multiple artifacts.
|
|
352
|
+
|
|
353
|
+
Args:
|
|
354
|
+
result: The message/result to show to the agent.
|
|
355
|
+
artifacts: List of artifact dictionaries.
|
|
356
|
+
|
|
357
|
+
Returns:
|
|
358
|
+
Dictionary with 'result' and 'artifacts' keys.
|
|
359
|
+
"""
|
|
360
|
+
return {"result": result, "artifacts": artifacts}
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def create_artifact_command( # noqa: PLR0913
|
|
364
|
+
result: str,
|
|
365
|
+
artifact_data: bytes | str,
|
|
366
|
+
artifact_name: str,
|
|
367
|
+
artifact_description: str = "",
|
|
368
|
+
mime_type: str | None = None,
|
|
369
|
+
metadata_update: dict[str, Any] | None = None,
|
|
370
|
+
) -> Command:
|
|
371
|
+
"""Create a Command that updates artifacts (and optional metadata).
|
|
372
|
+
|
|
373
|
+
Args:
|
|
374
|
+
result: Message/result to show to the agent (for ToolMessage content).
|
|
375
|
+
artifact_data: The binary data or base64 string for the artifact.
|
|
376
|
+
artifact_name: The name for the artifact file.
|
|
377
|
+
artifact_description: Description of the artifact.
|
|
378
|
+
mime_type: MIME type of the artifact. If None, auto-detected from filename.
|
|
379
|
+
metadata_update: Optional metadata delta to merge into state metadata.
|
|
380
|
+
|
|
381
|
+
Returns:
|
|
382
|
+
Command: A LangGraph Command with update containing 'result', 'artifacts', and optional 'metadata'.
|
|
383
|
+
"""
|
|
384
|
+
single = _default_handler.create_artifact(
|
|
385
|
+
result=result,
|
|
386
|
+
data=artifact_data,
|
|
387
|
+
artifact_name=artifact_name,
|
|
388
|
+
artifact_description=artifact_description,
|
|
389
|
+
mime_type=mime_type,
|
|
390
|
+
enable_deduplication=False,
|
|
391
|
+
)
|
|
392
|
+
update: dict[str, Any] = {
|
|
393
|
+
"result": single["result"],
|
|
394
|
+
"artifacts": [single["artifact"]],
|
|
395
|
+
}
|
|
396
|
+
if metadata_update:
|
|
397
|
+
update["metadata"] = metadata_update
|
|
398
|
+
return Command(update=update)
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
def create_multiple_artifacts_command(
|
|
402
|
+
result: str,
|
|
403
|
+
artifacts: list[Artifact | dict[str, Any]] | None = None,
|
|
404
|
+
metadata_update: dict[str, Any] | None = None,
|
|
405
|
+
) -> Command:
|
|
406
|
+
"""Create a Command that updates multiple artifacts (and optional metadata).
|
|
407
|
+
|
|
408
|
+
The 'artifacts' list accepts mixed item types:
|
|
409
|
+
- Artifact: a typed artifact model (preferred). Will be converted via model_dump().
|
|
410
|
+
- dict: a prebuilt artifact dict ready for A2A.
|
|
411
|
+
- DataSpec dict: raw spec to build an artifact with keys:
|
|
412
|
+
{'data': bytes|str, 'artifact_name': str, 'artifact_description'?: str, 'mime_type'?: str}
|
|
413
|
+
|
|
414
|
+
Args:
|
|
415
|
+
result: Message/result to show to the agent.
|
|
416
|
+
artifacts: List of items (Artifact | dict) to attach or build.
|
|
417
|
+
metadata_update: Optional metadata delta to merge into state metadata.
|
|
418
|
+
|
|
419
|
+
Returns:
|
|
420
|
+
Command: A LangGraph Command with update containing 'result', 'artifacts', and optional 'metadata'.
|
|
421
|
+
"""
|
|
422
|
+
built_artifacts: list[dict[str, Any]] = []
|
|
423
|
+
if not artifacts:
|
|
424
|
+
raise ValueError("At least one of 'artifacts' must be provided and non-empty")
|
|
425
|
+
|
|
426
|
+
for art in artifacts or []:
|
|
427
|
+
if isinstance(art, Artifact):
|
|
428
|
+
built_artifacts.append(art.model_dump())
|
|
429
|
+
elif isinstance(art, dict):
|
|
430
|
+
try:
|
|
431
|
+
validated = Artifact.model_validate(art)
|
|
432
|
+
except ValidationError as e:
|
|
433
|
+
raise ValueError(f"Invalid artifact dict: {e}") from e
|
|
434
|
+
built_artifacts.append(validated.model_dump())
|
|
435
|
+
else:
|
|
436
|
+
raise TypeError("artifacts items must be Artifact or dict")
|
|
437
|
+
|
|
438
|
+
update: dict[str, Any] = {"result": result, "artifacts": built_artifacts}
|
|
439
|
+
if metadata_update:
|
|
440
|
+
update["metadata"] = metadata_update
|
|
441
|
+
return Command(update=update)
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
def create_error_response(error_message: str) -> str:
|
|
445
|
+
"""Create a standardized error response for tools.
|
|
446
|
+
|
|
447
|
+
For error cases, we return a simple string that will be passed directly
|
|
448
|
+
to the agent without any artifact processing.
|
|
449
|
+
|
|
450
|
+
Args:
|
|
451
|
+
error_message: The error message to return.
|
|
452
|
+
|
|
453
|
+
Returns:
|
|
454
|
+
String with error information.
|
|
455
|
+
"""
|
|
456
|
+
return _default_handler.create_error_response(error_message)
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
def extract_artifacts_from_agent_response(result: Any) -> tuple[str, list[dict[str, Any]]]:
|
|
460
|
+
"""Extract artifacts from agent response for delegation tools.
|
|
461
|
+
|
|
462
|
+
Args:
|
|
463
|
+
result: The result returned by the delegated agent.
|
|
464
|
+
|
|
465
|
+
Returns:
|
|
466
|
+
Tuple of (text_response, artifacts_list) where:
|
|
467
|
+
- text_response: The text content for the agent
|
|
468
|
+
- artifacts_list: List of artifacts to be passed through
|
|
469
|
+
"""
|
|
470
|
+
if not isinstance(result, dict):
|
|
471
|
+
return str(result), []
|
|
472
|
+
|
|
473
|
+
text_response = result.get("output", str(result))
|
|
474
|
+
artifacts = result.get("full_final_state", {}).get("artifacts", [])
|
|
475
|
+
|
|
476
|
+
# Ensure artifacts is a list before returning
|
|
477
|
+
if not isinstance(artifacts, list):
|
|
478
|
+
artifacts = []
|
|
479
|
+
|
|
480
|
+
return str(text_response), artifacts
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
def create_delegation_response_with_artifacts(
|
|
484
|
+
result: str, artifacts: list[dict[str, Any]], agent_name: str = ""
|
|
485
|
+
) -> Command:
|
|
486
|
+
"""Create a delegation response that includes artifacts only when needed.
|
|
487
|
+
|
|
488
|
+
Args:
|
|
489
|
+
result: The text result from the delegated agent.
|
|
490
|
+
artifacts: List of artifacts from the delegated agent (always a list).
|
|
491
|
+
agent_name: Name of the agent for prefixing the result.
|
|
492
|
+
|
|
493
|
+
Returns:
|
|
494
|
+
Command containing 'result' and optional 'artifacts'.
|
|
495
|
+
"""
|
|
496
|
+
# Format the result text with agent attribution
|
|
497
|
+
formatted_result = f"[{agent_name}] {result}" if agent_name else result
|
|
498
|
+
|
|
499
|
+
update: dict[str, Any] = {"result": formatted_result}
|
|
500
|
+
if artifacts:
|
|
501
|
+
update["artifacts"] = artifacts
|
|
502
|
+
return Command(update=update)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Shared constants for AIP Agents.
|
|
2
|
+
|
|
3
|
+
This module defines commonly used constants across the agents library
|
|
4
|
+
to avoid duplication and maintain consistency.
|
|
5
|
+
|
|
6
|
+
Authors:
|
|
7
|
+
Putu Ravindra Wiguna (putu.r.wiguna@gdplabs.id)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from enum import StrEnum
|
|
11
|
+
from uuid import UUID
|
|
12
|
+
|
|
13
|
+
__all__ = ["DefaultTimezone", "DEFAULT_PII_TAG_NAMESPACE"]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class DefaultTimezone(StrEnum):
|
|
17
|
+
"""Default timezone constants used across the application."""
|
|
18
|
+
|
|
19
|
+
JAKARTA = "Asia/Jakarta"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
DEFAULT_PII_TAG_NAMESPACE: UUID = UUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""Datetime-related helper utilities.
|
|
2
|
+
|
|
3
|
+
This module provides utilities for working with dates, times, and timezones
|
|
4
|
+
in a robust and consistent manner. It includes functions for normalizing
|
|
5
|
+
timestamp formats, validating date strings, and working with timezone-aware
|
|
6
|
+
datetimes.
|
|
7
|
+
|
|
8
|
+
The module is organized into the following submodules:
|
|
9
|
+
- normalization: Functions for normalizing and validating timestamp formats
|
|
10
|
+
- timezone: Functions for working with timezone-aware datetimes
|
|
11
|
+
|
|
12
|
+
All functions in this module are designed to be robust and handle edge cases
|
|
13
|
+
gracefully, making them suitable for production use.
|
|
14
|
+
|
|
15
|
+
Authors:
|
|
16
|
+
Putu Ravindra Wiguna (putu.r.wiguna@gdplabs.id)
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from aip_agents.utils.datetime.normalization import (
|
|
20
|
+
format_created_updated_label,
|
|
21
|
+
is_valid_date_string,
|
|
22
|
+
next_day_iso,
|
|
23
|
+
normalize_timestamp_to_date,
|
|
24
|
+
)
|
|
25
|
+
from aip_agents.utils.datetime.timezone import ensure_utc_datetime, get_timezone_aware_now
|
|
26
|
+
|
|
27
|
+
__all__ = [
|
|
28
|
+
"normalize_timestamp_to_date",
|
|
29
|
+
"format_created_updated_label",
|
|
30
|
+
"is_valid_date_string",
|
|
31
|
+
"next_day_iso",
|
|
32
|
+
"ensure_utc_datetime",
|
|
33
|
+
"get_timezone_aware_now",
|
|
34
|
+
]
|