aip-agents-binary 0.0.0__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 +66 -0
- aip_agents/a2a/__init__.py +19 -0
- aip_agents/a2a/server/__init__.py +10 -0
- aip_agents/a2a/server/base_executor.py +1125 -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/a2ui/__init__.py +26 -0
- aip_agents/a2ui/catalog_provider.py +109 -0
- aip_agents/a2ui/examples/0.8/booking_form.json +1 -0
- aip_agents/a2ui/examples/0.8/confirmation.json +1 -0
- aip_agents/a2ui/examples/0.8/single_column_list.json +1 -0
- aip_agents/a2ui/examples/0.9/booking_form.json +1 -0
- aip_agents/a2ui/examples/0.9/confirmation.json +1 -0
- aip_agents/a2ui/examples/0.9/single_column_list.json +1 -0
- aip_agents/a2ui/middleware.py +754 -0
- aip_agents/a2ui/models.py +153 -0
- aip_agents/a2ui/public_url.py +138 -0
- aip_agents/a2ui/stream_filter.py +67 -0
- aip_agents/agent/__init__.py +72 -0
- aip_agents/agent/base_agent.py +1437 -0
- aip_agents/agent/base_langgraph_agent.py +4048 -0
- aip_agents/agent/google_adk_agent.py +932 -0
- aip_agents/agent/google_adk_constants.py +11 -0
- aip_agents/agent/graph_runtime.py +185 -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_react_agent.py +3741 -0
- aip_agents/agent/model_invocation_adapter.py +346 -0
- aip_agents/agent/system_instruction_context.py +34 -0
- aip_agents/agent/tool_execution_engine.py +587 -0
- aip_agents/audio_interface/__init__.py +30 -0
- aip_agents/audio_interface/audio_agent_adapter.py +254 -0
- aip_agents/audio_interface/config.py +146 -0
- aip_agents/audio_interface/errors.py +15 -0
- aip_agents/audio_interface/livekit_audio_session.py +504 -0
- aip_agents/audio_interface/livekit_realtime_audio_session.py +142 -0
- aip_agents/audio_interface/session_factory.py +40 -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/common/__init__.py +5 -0
- aip_agents/common/callable_introspection.py +17 -0
- aip_agents/constants.py +23 -0
- aip_agents/context/__init__.py +5 -0
- aip_agents/context/budget.py +237 -0
- aip_agents/context/compaction.py +339 -0
- aip_agents/context/metadata.py +169 -0
- aip_agents/context/offload.py +449 -0
- aip_agents/context/preparation.py +196 -0
- aip_agents/context/prompt.py +61 -0
- aip_agents/context/runtime.py +149 -0
- aip_agents/context/session_recall.py +69 -0
- aip_agents/context/state.py +48 -0
- aip_agents/context/utils.py +44 -0
- aip_agents/credentials/manager.py +132 -0
- aip_agents/examples/__init__.py +5 -0
- aip_agents/examples/audio_livekit_session.py +116 -0
- aip_agents/examples/compare_streaming_client.py +783 -0
- aip_agents/examples/compare_streaming_server.py +141 -0
- aip_agents/examples/gl_connectors_smoke.py +148 -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 +78 -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 +104 -0
- aip_agents/examples/hello_world_a2a_langchain_server.py +78 -0
- aip_agents/examples/hello_world_a2a_langchain_server_lm_invoker.py +77 -0
- aip_agents/examples/hello_world_a2a_langflow_client.py +83 -0
- aip_agents/examples/hello_world_a2a_langflow_server.py +80 -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 +91 -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 +83 -0
- aip_agents/examples/hello_world_a2a_langgraph_server_lm_invoker.py +78 -0
- aip_agents/examples/hello_world_a2a_langgraph_server_tool_streaming.py +131 -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 +250 -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 +79 -0
- aip_agents/examples/hello_world_filesystem.py +162 -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_gl_connector_twitter.py +44 -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_skills.py +62 -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_multi_agent_tool_output_client.py +57 -0
- aip_agents/examples/hello_world_multi_agent_tool_output_server.py +131 -0
- aip_agents/examples/hello_world_pii_logger.py +21 -0
- aip_agents/examples/hello_world_ptc.py +49 -0
- aip_agents/examples/hello_world_ptc_custom_tools.py +83 -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 +100 -0
- aip_agents/examples/hello_world_tool_output_client.py +55 -0
- aip_agents/examples/hello_world_tool_output_server.py +113 -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 +125 -0
- aip_agents/examples/pii_demo_multi_agent_client.py +80 -0
- aip_agents/examples/pii_demo_multi_agent_server.py +246 -0
- aip_agents/examples/todolist_planning_a2a_langchain_client.py +70 -0
- aip_agents/examples/todolist_planning_a2a_langgraph_server.py +87 -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/multiply_tool.py +43 -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/guardrails/__init__.py +83 -0
- aip_agents/guardrails/engines/__init__.py +69 -0
- aip_agents/guardrails/engines/base.py +90 -0
- aip_agents/guardrails/engines/nemo.py +101 -0
- aip_agents/guardrails/engines/phrase_matcher.py +113 -0
- aip_agents/guardrails/exceptions.py +39 -0
- aip_agents/guardrails/manager.py +163 -0
- aip_agents/guardrails/middleware.py +199 -0
- aip_agents/guardrails/schemas.py +63 -0
- aip_agents/guardrails/utils.py +45 -0
- aip_agents/integration/__init__.py +43 -0
- aip_agents/integration/agent.py +58 -0
- aip_agents/integration/guardrails.py +52 -0
- aip_agents/integration/hitl.py +71 -0
- aip_agents/integration/ptc.py +95 -0
- aip_agents/integration/skills.py +37 -0
- aip_agents/integration/storage.py +81 -0
- aip_agents/integration/version.py +103 -0
- aip_agents/mcp/__init__.py +1 -0
- aip_agents/mcp/client/__init__.py +60 -0
- aip_agents/mcp/client/base_mcp_client.py +377 -0
- aip_agents/mcp/client/connection_manager.py +231 -0
- aip_agents/mcp/client/errors.py +119 -0
- aip_agents/mcp/client/google_adk/__init__.py +45 -0
- aip_agents/mcp/client/google_adk/client.py +383 -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 +704 -0
- aip_agents/mcp/client/session_pool.py +358 -0
- aip_agents/mcp/client/transports.py +288 -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 +815 -0
- aip_agents/memory/adapters/mem0.py +286 -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 +65 -0
- aip_agents/middleware/backends/__init__.py +72 -0
- aip_agents/middleware/backends/in_memory.py +578 -0
- aip_agents/middleware/backends/local_disk.py +1203 -0
- aip_agents/middleware/backends/protocol.py +353 -0
- aip_agents/middleware/backends/sandbox.py +1431 -0
- aip_agents/middleware/backends/sandbox_files.py +113 -0
- aip_agents/middleware/backends/utils.py +293 -0
- aip_agents/middleware/base.py +239 -0
- aip_agents/middleware/filesystem.py +690 -0
- aip_agents/middleware/manager.py +291 -0
- aip_agents/middleware/memory.py +532 -0
- aip_agents/middleware/pii_middleware.py +192 -0
- aip_agents/middleware/schema.py +19 -0
- aip_agents/middleware/skills.py +872 -0
- aip_agents/middleware/task_delegation.py +1656 -0
- aip_agents/middleware/todolist.py +276 -0
- aip_agents/middleware/tools/__init__.py +23 -0
- aip_agents/middleware/tools/edit_file.py +70 -0
- aip_agents/middleware/tools/execute.py +428 -0
- aip_agents/middleware/tools/grep_file.py +79 -0
- aip_agents/middleware/tools/ls.py +71 -0
- aip_agents/middleware/tools/read_file.py +239 -0
- aip_agents/middleware/tools/skill_load.py +193 -0
- aip_agents/middleware/tools/skill_resource.py +338 -0
- aip_agents/middleware/tools/write_file.py +114 -0
- aip_agents/middleware/vision.py +322 -0
- aip_agents/ptc/__init__.py +87 -0
- aip_agents/ptc/custom_tools.py +511 -0
- aip_agents/ptc/custom_tools_payload.py +499 -0
- aip_agents/ptc/custom_tools_templates/__init__.py +1 -0
- aip_agents/ptc/custom_tools_templates/custom_build_function.py.template +24 -0
- aip_agents/ptc/custom_tools_templates/custom_init.py.template +26 -0
- aip_agents/ptc/custom_tools_templates/custom_invoke.py.template +149 -0
- aip_agents/ptc/custom_tools_templates/custom_registry.py.template +112 -0
- aip_agents/ptc/custom_tools_templates/custom_sources_init.py.template +7 -0
- aip_agents/ptc/custom_tools_templates/custom_wrapper.py.template +87 -0
- aip_agents/ptc/doc_gen.py +122 -0
- aip_agents/ptc/exceptions.py +57 -0
- aip_agents/ptc/executor.py +627 -0
- aip_agents/ptc/mcp/__init__.py +45 -0
- aip_agents/ptc/mcp/sandbox_bridge.py +679 -0
- aip_agents/ptc/mcp/templates/__init__.py +1 -0
- aip_agents/ptc/mcp/templates/mcp_client.py.template +240 -0
- aip_agents/ptc/naming.py +430 -0
- aip_agents/ptc/payload.py +26 -0
- aip_agents/ptc/prompt_builder.py +674 -0
- aip_agents/ptc/ptc_helper.py +16 -0
- aip_agents/ptc/sandbox_bridge.py +297 -0
- aip_agents/ptc/template_utils.py +33 -0
- aip_agents/ptc/templates/__init__.py +1 -0
- aip_agents/ptc/templates/ptc_helper.py.template +134 -0
- aip_agents/ptc/tool_def_helpers.py +101 -0
- aip_agents/ptc/tool_enrichment.py +163 -0
- aip_agents/resilience/__init__.py +10 -0
- aip_agents/resilience/base.py +46 -0
- aip_agents/resilience/tool.py +1661 -0
- aip_agents/sandbox/__init__.py +62 -0
- aip_agents/sandbox/defaults.py +220 -0
- aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-1-datasaur.Dockerfile +120 -0
- aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-1.Dockerfile +100 -0
- aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-2-datasaur.Dockerfile +143 -0
- aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-2.Dockerfile +119 -0
- aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-3-datasaur.Dockerfile +173 -0
- aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-3.Dockerfile +130 -0
- aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-4-runner-managed.Dockerfile +164 -0
- aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-4-runner-managed.patch_js_context.py +41 -0
- aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-4-runner-managed.requirements.txt +29 -0
- aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2.Dockerfile +87 -0
- aip_agents/sandbox/e2b_runtime.py +27 -0
- aip_agents/sandbox/image_registry.py +98 -0
- aip_agents/sandbox/providers/__init__.py +76 -0
- aip_agents/sandbox/providers/base.py +392 -0
- aip_agents/sandbox/providers/bedrock.py +28 -0
- aip_agents/sandbox/providers/e2b.py +230 -0
- aip_agents/sandbox/providers/opensandbox.py +163 -0
- aip_agents/sandbox/sandbox_runtime.py +967 -0
- aip_agents/sandbox/template_builder.py +240 -0
- aip_agents/sandbox/types.py +141 -0
- aip_agents/sandbox/validation.py +50 -0
- aip_agents/schema/__init__.py +106 -0
- aip_agents/schema/a2a.py +58 -0
- aip_agents/schema/agent.py +117 -0
- aip_agents/schema/artifacts.py +75 -0
- aip_agents/schema/attachments.py +425 -0
- aip_agents/schema/hitl.py +157 -0
- aip_agents/schema/langgraph.py +47 -0
- aip_agents/schema/model_id.py +103 -0
- aip_agents/schema/model_usage.py +50 -0
- aip_agents/schema/runtime_metadata.py +78 -0
- aip_agents/schema/step_limit.py +108 -0
- aip_agents/schema/storage.py +40 -0
- aip_agents/schema/tool_payloads.py +68 -0
- aip_agents/sentry/__init__.py +11 -0
- aip_agents/sentry/sentry.py +267 -0
- aip_agents/skills/__init__.py +30 -0
- aip_agents/skills/errors.py +110 -0
- aip_agents/skills/installer.py +479 -0
- aip_agents/skills/models.py +769 -0
- aip_agents/skills/stager.py +224 -0
- aip_agents/skills/validation.py +53 -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 +65 -0
- aip_agents/tools/agent_schedule_tool.py +392 -0
- aip_agents/tools/browser_use/__init__.py +83 -0
- aip_agents/tools/browser_use/action_parser.py +104 -0
- aip_agents/tools/browser_use/browser_use_tool.py +1118 -0
- aip_agents/tools/browser_use/llm_config.py +120 -0
- aip_agents/tools/browser_use/minio_storage.py +203 -0
- aip_agents/tools/browser_use/schemas.py +121 -0
- aip_agents/tools/browser_use/session.py +13 -0
- aip_agents/tools/browser_use/session_errors.py +132 -0
- aip_agents/tools/browser_use/steel_session_recording.py +321 -0
- aip_agents/tools/browser_use/streaming.py +263 -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 +37 -0
- aip_agents/tools/code_sandbox/__init__.py +48 -0
- aip_agents/tools/code_sandbox/code_sandbox_tool.py +350 -0
- aip_agents/tools/code_sandbox/constant.py +13 -0
- aip_agents/tools/code_sandbox/e2b_cloud_sandbox_extended.py +346 -0
- aip_agents/tools/code_sandbox/e2b_sandbox_tool.py +65 -0
- aip_agents/tools/computer_use/__init__.py +75 -0
- aip_agents/tools/computer_use/computer_use_tool.py +910 -0
- aip_agents/tools/computer_use/schemas.py +78 -0
- aip_agents/tools/computer_use/streaming.py +198 -0
- aip_agents/tools/computer_use/types.py +32 -0
- aip_agents/tools/constants.py +177 -0
- aip_agents/tools/date_range_tool.py +554 -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/execute_ptc_code.py +582 -0
- aip_agents/tools/gl_connector/__init__.py +5 -0
- aip_agents/tools/gl_connector/tool.py +518 -0
- aip_agents/tools/gl_connector_tools.py +170 -0
- aip_agents/tools/memory_search/__init__.py +29 -0
- aip_agents/tools/memory_search/base.py +213 -0
- aip_agents/tools/memory_search/mem0.py +424 -0
- aip_agents/tools/memory_search/schema.py +81 -0
- aip_agents/tools/memory_search_tool.py +34 -0
- aip_agents/tools/streaming_common.py +577 -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 +73 -0
- aip_agents/types/a2a_events.py +13 -0
- aip_agents/utils/__init__.py +91 -0
- aip_agents/utils/a2a_connector.py +1758 -0
- aip_agents/utils/artifact_helpers.py +542 -0
- aip_agents/utils/artifacts/__init__.py +20 -0
- aip_agents/utils/artifacts/tracker.py +367 -0
- aip_agents/utils/attachment_helpers.py +178 -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 +276 -0
- aip_agents/utils/file_watchers/__init__.py +33 -0
- aip_agents/utils/file_watchers/base.py +46 -0
- aip_agents/utils/file_watchers/e2b.py +128 -0
- aip_agents/utils/file_watchers/factory.py +39 -0
- aip_agents/utils/file_watchers/ls_diff.py +88 -0
- aip_agents/utils/file_watchers/sandbox.py +66 -0
- aip_agents/utils/file_watchers/watchdog.py +97 -0
- aip_agents/utils/final_response_builder.py +211 -0
- aip_agents/utils/formatter_llm_client.py +258 -0
- aip_agents/utils/langgraph/__init__.py +19 -0
- aip_agents/utils/langgraph/converter.py +399 -0
- aip_agents/utils/langgraph/delegation_context.py +22 -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 +1329 -0
- aip_agents/utils/langgraph/tool_output_management.py +1047 -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 +822 -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 +363 -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 +29 -0
- aip_agents/utils/pii/pii_handler.py +555 -0
- aip_agents/utils/pii/pii_helper.py +225 -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 +836 -0
- aip_agents/utils/step_limit_manager.py +265 -0
- aip_agents/utils/token_usage_helper.py +531 -0
- aip_agents_binary-0.0.0.dist-info/METADATA +825 -0
- aip_agents_binary-0.0.0.dist-info/RECORD +447 -0
- aip_agents_binary-0.0.0.dist-info/WHEEL +5 -0
- aip_agents_binary-0.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""Schema definitions for agent configuration types.
|
|
2
|
+
|
|
3
|
+
Authors:
|
|
4
|
+
Christian Trisno Sen Long Chen (christian.t.s.l.chen@gdplabs.id)
|
|
5
|
+
Reinhart Linanda (reinhart.linanda@gdplabs.id)
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from enum import StrEnum
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from a2a.types import AgentCard
|
|
12
|
+
from gllm_core.utils.retry import RetryConfig
|
|
13
|
+
from pydantic import BaseModel, Field
|
|
14
|
+
|
|
15
|
+
try:
|
|
16
|
+
# Pydantic v2 preferred config
|
|
17
|
+
from pydantic import ConfigDict # type: ignore
|
|
18
|
+
except Exception: # pragma: no cover
|
|
19
|
+
ConfigDict = None # type: ignore
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"CredentialType",
|
|
23
|
+
"StreamMode",
|
|
24
|
+
"HttpxClientOptions",
|
|
25
|
+
"A2AClientConfig",
|
|
26
|
+
"BaseAgentConfig",
|
|
27
|
+
"AgentConfig",
|
|
28
|
+
"LangflowAgentConfig",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class CredentialType(StrEnum):
|
|
33
|
+
"""Credential type enumeration for type safety and better developer experience."""
|
|
34
|
+
|
|
35
|
+
API_KEY = "api_key"
|
|
36
|
+
FILE = "file"
|
|
37
|
+
DICT = "dict"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class StreamMode(StrEnum):
|
|
41
|
+
"""LangGraph stream modes for astream operations."""
|
|
42
|
+
|
|
43
|
+
VALUES = "values"
|
|
44
|
+
CUSTOM = "custom"
|
|
45
|
+
MESSAGES = "messages"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class HttpxClientOptions(BaseModel):
|
|
49
|
+
"""Options for the HTTP client."""
|
|
50
|
+
|
|
51
|
+
timeout: float = 360.0
|
|
52
|
+
trust_env: bool = False
|
|
53
|
+
follow_redirects: bool = True
|
|
54
|
+
|
|
55
|
+
if ConfigDict is not None:
|
|
56
|
+
# Pydantic v2 style config
|
|
57
|
+
model_config = ConfigDict(extra="allow") # type: ignore[assignment]
|
|
58
|
+
else: # pragma: no cover
|
|
59
|
+
|
|
60
|
+
class Config: # type: ignore[no-redef]
|
|
61
|
+
"""Pydantic v1 fallback config for HttpxClientOptions."""
|
|
62
|
+
|
|
63
|
+
extra = "allow"
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class A2AClientConfig(BaseModel):
|
|
67
|
+
"""Configuration for A2A client."""
|
|
68
|
+
|
|
69
|
+
discovery_urls: list[str] | None = None
|
|
70
|
+
known_agents: dict[str, AgentCard] = Field(default_factory=dict)
|
|
71
|
+
httpx_client_options: HttpxClientOptions | None = HttpxClientOptions()
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class BaseAgentConfig(BaseModel):
|
|
75
|
+
"""Base configuration for agent implementations."""
|
|
76
|
+
|
|
77
|
+
tools: list[Any] | None = None
|
|
78
|
+
default_hyperparameters: dict[str, Any] | None = None
|
|
79
|
+
|
|
80
|
+
if ConfigDict is not None:
|
|
81
|
+
model_config = ConfigDict(extra="allow") # type: ignore[assignment]
|
|
82
|
+
else: # pragma: no cover
|
|
83
|
+
|
|
84
|
+
class Config: # type: ignore[no-redef]
|
|
85
|
+
"""Pydantic v1 fallback config for BaseAgentConfig."""
|
|
86
|
+
|
|
87
|
+
extra = "allow"
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class AgentConfig(BaseAgentConfig):
|
|
91
|
+
"""Configuration for agent implementations with language model settings."""
|
|
92
|
+
|
|
93
|
+
lm_name: str | None = None
|
|
94
|
+
lm_hyperparameters: dict[str, Any] | None = None
|
|
95
|
+
lm_provider: str | None = None
|
|
96
|
+
lm_base_url: str | None = None
|
|
97
|
+
lm_api_key: str | None = None
|
|
98
|
+
lm_credentials: str | dict[str, Any] | None = None
|
|
99
|
+
lm_retry_config: RetryConfig | None = None
|
|
100
|
+
a2ui: dict[str, Any] | None = None
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class LangflowAgentConfig(BaseAgentConfig):
|
|
104
|
+
"""Configuration for Langflow agent implementations."""
|
|
105
|
+
|
|
106
|
+
flow_id: str
|
|
107
|
+
base_url: str | None = None
|
|
108
|
+
api_key: str | None = None
|
|
109
|
+
|
|
110
|
+
if ConfigDict is not None:
|
|
111
|
+
model_config = ConfigDict(extra="allow") # type: ignore[assignment]
|
|
112
|
+
else: # pragma: no cover
|
|
113
|
+
|
|
114
|
+
class Config: # type: ignore[no-redef]
|
|
115
|
+
"""Pydantic v1 fallback config for LangflowAgentConfig."""
|
|
116
|
+
|
|
117
|
+
extra = "allow"
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""Shared typed contracts for artifact payloads exchanged across tool and agent boundaries.
|
|
2
|
+
|
|
3
|
+
These contracts codify the dict shapes already produced by
|
|
4
|
+
``aip_agents.utils.artifact_helpers`` and ``aip_agents.utils.artifacts.tracker``
|
|
5
|
+
so downstream typing slices can import named structures instead of repeating
|
|
6
|
+
``dict[str, Any]``. They describe the *serialized* artifact payload (the dict
|
|
7
|
+
form of ``aip_agents.a2a.types.Artifact`` plus an optional internal ``path``
|
|
8
|
+
used by the tracker); they do not replace the Pydantic ``Artifact`` model.
|
|
9
|
+
|
|
10
|
+
Authors:
|
|
11
|
+
Putu Ravindra Wiguna (putu.r.wiguna@gdplabs.id)
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from typing import Any, Required, TypedDict
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"ArtifactPayload",
|
|
20
|
+
"TrackedArtifact",
|
|
21
|
+
"ArtifactBuildError",
|
|
22
|
+
"ToolArtifactResponse",
|
|
23
|
+
"ToolMultiArtifactResponse",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ArtifactPayload(TypedDict, total=False):
|
|
28
|
+
"""Serialized artifact payload produced by artifact helpers.
|
|
29
|
+
|
|
30
|
+
Mirrors ``aip_agents.a2a.types.Artifact.model_dump()``.
|
|
31
|
+
|
|
32
|
+
Wire-mandatory keys (always present when emitted by the A2A pipeline) are
|
|
33
|
+
marked ``Required``: ``artifact_type``, ``data``, ``name``, ``mime_type``.
|
|
34
|
+
``description`` defaults to an empty string and ``metadata`` is omitted when
|
|
35
|
+
absent.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
artifact_type: Required[str]
|
|
39
|
+
data: Required[str]
|
|
40
|
+
name: Required[str]
|
|
41
|
+
description: str
|
|
42
|
+
mime_type: Required[str]
|
|
43
|
+
metadata: dict[str, Any] | None
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class TrackedArtifact(ArtifactPayload, total=False):
|
|
47
|
+
"""``ArtifactPayload`` plus the tracker-only ``path`` provenance key.
|
|
48
|
+
|
|
49
|
+
Emitted by ``ArtifactTracker.build_artifacts``; ``path`` is internal and
|
|
50
|
+
must be stripped before serialization to the A2A wire contract
|
|
51
|
+
(``ArtifactPayload``).
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
path: str
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class ArtifactBuildError(TypedDict):
|
|
58
|
+
"""Error entry returned by ``ArtifactTracker.build_artifacts`` for a file that could not be turned into an artifact."""
|
|
59
|
+
|
|
60
|
+
path: str
|
|
61
|
+
reason: str
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class ToolArtifactResponse(TypedDict):
|
|
65
|
+
"""Single-artifact tool response shape returned by ``ArtifactHandler.create_artifact``."""
|
|
66
|
+
|
|
67
|
+
result: str
|
|
68
|
+
artifact: ArtifactPayload
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class ToolMultiArtifactResponse(TypedDict):
|
|
72
|
+
"""Multi-artifact tool response shape returned by artifact command helpers."""
|
|
73
|
+
|
|
74
|
+
result: str
|
|
75
|
+
artifacts: list[ArtifactPayload]
|
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
"""Runtime attachment contracts for user inputs and generated artifacts.
|
|
2
|
+
|
|
3
|
+
Authors:
|
|
4
|
+
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import base64
|
|
10
|
+
import hashlib
|
|
11
|
+
import os
|
|
12
|
+
from enum import StrEnum
|
|
13
|
+
from typing import Any, Required, TypedDict
|
|
14
|
+
from urllib.parse import urlparse
|
|
15
|
+
|
|
16
|
+
from pydantic import BaseModel, ConfigDict, Field, ValidationInfo, field_validator
|
|
17
|
+
|
|
18
|
+
from aip_agents.a2a.types import MimeType, get_mime_type_from_filename
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class AttachmentKind(StrEnum):
|
|
22
|
+
"""Supported first-class attachment kinds."""
|
|
23
|
+
|
|
24
|
+
IMAGE = "image"
|
|
25
|
+
DOCUMENT = "document"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class AttachmentSource(StrEnum):
|
|
29
|
+
"""Where an attachment reference originated."""
|
|
30
|
+
|
|
31
|
+
USER_INPUT = "user_input"
|
|
32
|
+
LOCAL_TOOL_OUTPUT = "local_tool_output"
|
|
33
|
+
SUB_AGENT_ARTIFACT = "sub_agent_artifact"
|
|
34
|
+
REMOTE_UPLOAD = "remote_upload"
|
|
35
|
+
PLATFORM_ARTIFACT = "platform_artifact"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
_HTTP_SCHEME = "http"
|
|
39
|
+
_HTTP_PROTOCOL_PREFIXES = (f"{_HTTP_SCHEME}://", f"{_HTTP_SCHEME}s://")
|
|
40
|
+
SUPPORTED_IMAGE_MIME_TYPES = ("image/png", "image/jpeg", "image/webp")
|
|
41
|
+
SUPPORTED_DOCUMENT_MIME_TYPES = (
|
|
42
|
+
MimeType.APPLICATION_PDF,
|
|
43
|
+
MimeType.APPLICATION_MSWORD,
|
|
44
|
+
MimeType.APPLICATION_EXCEL,
|
|
45
|
+
MimeType.APPLICATION_DOCX,
|
|
46
|
+
MimeType.APPLICATION_XLSX,
|
|
47
|
+
)
|
|
48
|
+
MAX_IMAGE_ATTACHMENT_BYTES = 20 * 1024 * 1024
|
|
49
|
+
_UNSAFE_TRACE_METADATA_KEYS = {
|
|
50
|
+
"base64",
|
|
51
|
+
"bytes",
|
|
52
|
+
"data",
|
|
53
|
+
"data_ref",
|
|
54
|
+
"file_data",
|
|
55
|
+
"file_uri",
|
|
56
|
+
"local_path",
|
|
57
|
+
"path",
|
|
58
|
+
"presigned_url",
|
|
59
|
+
"raw",
|
|
60
|
+
"raw_data",
|
|
61
|
+
"s3_url",
|
|
62
|
+
"signed_url",
|
|
63
|
+
"url",
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _normalize_trace_metadata_key(key: object) -> str:
|
|
68
|
+
"""Normalize metadata keys before trace-safety blocklist checks."""
|
|
69
|
+
return str(key).strip().lower().replace("-", "_").replace(" ", "_")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _is_likely_local_path_ref(ref: str) -> bool:
|
|
73
|
+
"""Return whether a ref explicitly names a local filesystem path."""
|
|
74
|
+
if os.path.isabs(ref):
|
|
75
|
+
return True
|
|
76
|
+
normalized_ref = ref.lower()
|
|
77
|
+
if normalized_ref.startswith(("file://", "./", "../", "~/")):
|
|
78
|
+
return True
|
|
79
|
+
if "\\" in ref:
|
|
80
|
+
return True
|
|
81
|
+
return len(ref) > 2 and ref[1] == ":" and ref[0].isalpha()
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class AttachmentEntryPayload(TypedDict, total=False):
|
|
85
|
+
"""Serialized attachment entry — the dict form of ``RuntimeAttachment``.
|
|
86
|
+
|
|
87
|
+
Represents the shape stored in metadata ``attachments`` lists and
|
|
88
|
+
``attachment_registry`` dicts, and merged by
|
|
89
|
+
``utils.attachment_helpers.merge_input_attachments``. Mirrors
|
|
90
|
+
``RuntimeAttachment.model_dump()`` so registry/list consumers can use a
|
|
91
|
+
typed contract instead of ``dict[str, Any]``.
|
|
92
|
+
|
|
93
|
+
Identity fields always produced by ``model_dump()`` are marked ``Required``;
|
|
94
|
+
``size_bytes`` and ``metadata`` are genuinely optional.
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
id: Required[str]
|
|
98
|
+
kind: Required[str]
|
|
99
|
+
source: Required[str]
|
|
100
|
+
filename: Required[str]
|
|
101
|
+
mime_type: Required[str]
|
|
102
|
+
data_ref: Required[str]
|
|
103
|
+
size_bytes: int | None
|
|
104
|
+
metadata: dict[str, Any] | None
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class RuntimeAttachment(BaseModel):
|
|
108
|
+
"""Safe runtime reference for an input attachment or generated artifact."""
|
|
109
|
+
|
|
110
|
+
model_config = ConfigDict(use_enum_values=True)
|
|
111
|
+
|
|
112
|
+
id: str
|
|
113
|
+
kind: AttachmentKind
|
|
114
|
+
source: AttachmentSource
|
|
115
|
+
filename: str
|
|
116
|
+
mime_type: str
|
|
117
|
+
size_bytes: int | None = Field(default=None, ge=0)
|
|
118
|
+
data_ref: str
|
|
119
|
+
metadata: dict[str, Any] | None = None
|
|
120
|
+
|
|
121
|
+
@field_validator("mime_type")
|
|
122
|
+
@classmethod
|
|
123
|
+
def validate_mime_type(cls, value: str, info: ValidationInfo) -> str:
|
|
124
|
+
"""Apply MIME validation based on attachment kind."""
|
|
125
|
+
raw_kind = info.data.get("kind") if isinstance(info.data, dict) else None
|
|
126
|
+
kind = AttachmentKind(raw_kind) if raw_kind is not None else None
|
|
127
|
+
normalized_mime = normalize_mime_type(value)
|
|
128
|
+
|
|
129
|
+
if normalized_mime is None:
|
|
130
|
+
raise ValueError("attachment MIME type must be a non-empty string") # pragma: no cover
|
|
131
|
+
if kind == AttachmentKind.IMAGE and normalized_mime not in SUPPORTED_IMAGE_MIME_TYPES:
|
|
132
|
+
raise ValueError("image attachments must use a supported image MIME type")
|
|
133
|
+
if kind == AttachmentKind.DOCUMENT and normalized_mime not in SUPPORTED_DOCUMENT_MIME_TYPES:
|
|
134
|
+
raise ValueError("document attachments must use a supported document MIME type")
|
|
135
|
+
return normalized_mime
|
|
136
|
+
|
|
137
|
+
@field_validator("data_ref")
|
|
138
|
+
@classmethod
|
|
139
|
+
def validate_safe_data_ref(cls, value: str) -> str:
|
|
140
|
+
"""Reject inline bytes, base64 data URIs, and likely signed URLs."""
|
|
141
|
+
normalized_value = value.lower()
|
|
142
|
+
if normalized_value.startswith("data:"):
|
|
143
|
+
raise ValueError("data_ref must not contain inline data")
|
|
144
|
+
if normalized_value.startswith(_HTTP_PROTOCOL_PREFIXES):
|
|
145
|
+
parsed = urlparse(value)
|
|
146
|
+
if parsed.query:
|
|
147
|
+
raise ValueError("data_ref must not contain signed URL query parameters")
|
|
148
|
+
if "@" in parsed.netloc:
|
|
149
|
+
raise ValueError("data_ref must not contain credentials")
|
|
150
|
+
return value
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class AttachmentRegistry(BaseModel):
|
|
154
|
+
"""Lookup container shared by user inputs and generated artifacts."""
|
|
155
|
+
|
|
156
|
+
attachments: dict[str, RuntimeAttachment] = Field(default_factory=dict)
|
|
157
|
+
|
|
158
|
+
def register(self, attachment: RuntimeAttachment) -> RuntimeAttachment:
|
|
159
|
+
"""Register an attachment by ID and return the stored value."""
|
|
160
|
+
self.attachments[attachment.id] = attachment
|
|
161
|
+
return attachment
|
|
162
|
+
|
|
163
|
+
def resolve(self, attachment_id: str) -> RuntimeAttachment | None:
|
|
164
|
+
"""Resolve an attachment by ID."""
|
|
165
|
+
return self.attachments.get(attachment_id)
|
|
166
|
+
|
|
167
|
+
def as_list(self) -> list[dict[str, Any]]:
|
|
168
|
+
"""Return attachment entries as serializable dictionaries."""
|
|
169
|
+
return [attachment.model_dump() for attachment in self.attachments.values()]
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def normalize_mime_type(mime_type: str | None) -> str | None:
|
|
173
|
+
"""Normalize MIME strings for MIME support checks."""
|
|
174
|
+
if not isinstance(mime_type, str):
|
|
175
|
+
return None
|
|
176
|
+
normalized = mime_type.split(";", 1)[0].strip().lower()
|
|
177
|
+
return normalized or None
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def normalize_image_mime_type(mime_type: str | None) -> str | None:
|
|
181
|
+
"""Backward-compatible image MIME normalization helper."""
|
|
182
|
+
return normalize_mime_type(mime_type)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def is_supported_image_mime(mime_type: str | None) -> bool:
|
|
186
|
+
"""Return whether the MIME type is supported for image attachments."""
|
|
187
|
+
return normalize_mime_type(mime_type) in SUPPORTED_IMAGE_MIME_TYPES
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def is_supported_document_mime(mime_type: str | None) -> bool:
|
|
191
|
+
"""Return whether the MIME type is supported for document attachments."""
|
|
192
|
+
return normalize_mime_type(mime_type) in SUPPORTED_DOCUMENT_MIME_TYPES
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def create_runtime_attachment(
|
|
196
|
+
*,
|
|
197
|
+
attachment_id: str,
|
|
198
|
+
source: AttachmentSource | str,
|
|
199
|
+
filename: str,
|
|
200
|
+
mime_type: str,
|
|
201
|
+
data_ref: str,
|
|
202
|
+
kind: AttachmentKind | str = AttachmentKind.IMAGE,
|
|
203
|
+
size_bytes: int | None = None,
|
|
204
|
+
metadata: dict[str, Any] | None = None,
|
|
205
|
+
max_size_bytes: int = MAX_IMAGE_ATTACHMENT_BYTES,
|
|
206
|
+
) -> RuntimeAttachment:
|
|
207
|
+
"""Create a runtime attachment from caller-owned safe metadata."""
|
|
208
|
+
resolved_kind = AttachmentKind(kind)
|
|
209
|
+
normalized_mime = normalize_mime_type(mime_type)
|
|
210
|
+
|
|
211
|
+
if resolved_kind == AttachmentKind.IMAGE:
|
|
212
|
+
if normalized_mime is None or normalized_mime not in SUPPORTED_IMAGE_MIME_TYPES:
|
|
213
|
+
raise ValueError(f"unsupported image MIME type: {mime_type}")
|
|
214
|
+
if size_bytes is not None and size_bytes > max_size_bytes:
|
|
215
|
+
raise ValueError(f"image attachment {filename} exceeds {max_size_bytes} bytes")
|
|
216
|
+
elif resolved_kind == AttachmentKind.DOCUMENT:
|
|
217
|
+
if normalized_mime is None or normalized_mime not in SUPPORTED_DOCUMENT_MIME_TYPES:
|
|
218
|
+
raise ValueError(f"unsupported document MIME type: {mime_type}")
|
|
219
|
+
|
|
220
|
+
return RuntimeAttachment(
|
|
221
|
+
id=attachment_id,
|
|
222
|
+
kind=resolved_kind,
|
|
223
|
+
source=source,
|
|
224
|
+
filename=filename,
|
|
225
|
+
mime_type=normalized_mime,
|
|
226
|
+
size_bytes=size_bytes,
|
|
227
|
+
data_ref=data_ref,
|
|
228
|
+
metadata=metadata,
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
create_runtime_image_attachment = create_runtime_attachment
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def merge_attachment_registry(
|
|
236
|
+
metadata: dict[str, Any] | None,
|
|
237
|
+
attachments: list[RuntimeAttachment | dict[str, Any]],
|
|
238
|
+
) -> dict[str, Any]:
|
|
239
|
+
"""Merge image attachments into runtime metadata and registry fields."""
|
|
240
|
+
merged = dict(metadata or {})
|
|
241
|
+
existing_attachments = merged.get("attachments") if isinstance(merged.get("attachments"), list) else []
|
|
242
|
+
existing_registry = merged.get("attachment_registry") if isinstance(merged.get("attachment_registry"), dict) else {}
|
|
243
|
+
attachment_list = [*existing_attachments]
|
|
244
|
+
registry = dict(existing_registry)
|
|
245
|
+
|
|
246
|
+
for attachment in attachments:
|
|
247
|
+
attachment_dict = attachment.model_dump() if isinstance(attachment, RuntimeAttachment) else dict(attachment)
|
|
248
|
+
attachment_id = attachment_dict.get("id")
|
|
249
|
+
if not isinstance(attachment_id, str) or not attachment_id:
|
|
250
|
+
continue
|
|
251
|
+
if attachment_id not in registry:
|
|
252
|
+
attachment_list.append(attachment_dict)
|
|
253
|
+
registry[attachment_id] = attachment_dict
|
|
254
|
+
|
|
255
|
+
merged["attachments"] = attachment_list
|
|
256
|
+
merged["attachment_registry"] = registry
|
|
257
|
+
return merged
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def safe_image_data_ref(value: object) -> str | None:
|
|
261
|
+
"""Return safe non-inline image refs while excluding signed URLs and local paths."""
|
|
262
|
+
if not isinstance(value, str):
|
|
263
|
+
return None
|
|
264
|
+
ref = value.strip()
|
|
265
|
+
normalized_ref = ref.lower()
|
|
266
|
+
if not ref or normalized_ref.startswith("data:"):
|
|
267
|
+
return None
|
|
268
|
+
if normalized_ref.startswith(_HTTP_PROTOCOL_PREFIXES):
|
|
269
|
+
parsed = urlparse(ref)
|
|
270
|
+
if parsed.query or "@" in parsed.netloc:
|
|
271
|
+
return None
|
|
272
|
+
return ref
|
|
273
|
+
if _is_likely_local_path_ref(ref):
|
|
274
|
+
return None
|
|
275
|
+
return ref
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def _extract_trace_base_fields(attachment_dict: dict[str, Any]) -> dict[str, Any]:
|
|
279
|
+
"""Copy stable attachment identity fields into a trace-safe payload."""
|
|
280
|
+
trace: dict[str, Any] = {}
|
|
281
|
+
for key in ("id", "kind", "source", "filename", "mime_type", "size_bytes"):
|
|
282
|
+
if key in attachment_dict:
|
|
283
|
+
trace[key] = attachment_dict[key]
|
|
284
|
+
return trace
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def _sanitize_trace_metadata(metadata: dict[str, Any]) -> dict[str, Any]:
|
|
288
|
+
"""Remove unsafe metadata keys and values before trace emission."""
|
|
289
|
+
safe_metadata: dict[str, Any] = {}
|
|
290
|
+
for key, value in metadata.items():
|
|
291
|
+
if _normalize_trace_metadata_key(key) in _UNSAFE_TRACE_METADATA_KEYS:
|
|
292
|
+
continue
|
|
293
|
+
if isinstance(value, str):
|
|
294
|
+
safe_value = safe_image_data_ref(value.strip())
|
|
295
|
+
if safe_value is None:
|
|
296
|
+
continue
|
|
297
|
+
value = safe_value
|
|
298
|
+
safe_metadata[key] = value
|
|
299
|
+
return safe_metadata
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def sanitize_image_attachment_for_trace(attachment: RuntimeAttachment | dict[str, Any]) -> dict[str, Any]:
|
|
303
|
+
"""Return trace-safe image metadata without local paths, signed URLs, or bytes."""
|
|
304
|
+
attachment_dict = attachment.model_dump() if isinstance(attachment, RuntimeAttachment) else dict(attachment)
|
|
305
|
+
trace = _extract_trace_base_fields(attachment_dict)
|
|
306
|
+
|
|
307
|
+
safe_ref = safe_image_data_ref(attachment_dict.get("data_ref"))
|
|
308
|
+
if safe_ref is not None:
|
|
309
|
+
trace["data_ref"] = safe_ref
|
|
310
|
+
|
|
311
|
+
metadata = attachment_dict.get("metadata")
|
|
312
|
+
if isinstance(metadata, dict):
|
|
313
|
+
safe_metadata = _sanitize_trace_metadata(metadata)
|
|
314
|
+
if safe_metadata:
|
|
315
|
+
trace["metadata"] = safe_metadata
|
|
316
|
+
return trace
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def create_input_image_attachment(
|
|
320
|
+
*,
|
|
321
|
+
attachment_id: str,
|
|
322
|
+
path: str,
|
|
323
|
+
filename: str | None = None,
|
|
324
|
+
mime_type: str | None = None,
|
|
325
|
+
size_bytes: int | None = None,
|
|
326
|
+
data_ref: str | None = None,
|
|
327
|
+
source: AttachmentSource = AttachmentSource.USER_INPUT,
|
|
328
|
+
) -> RuntimeAttachment:
|
|
329
|
+
"""Create a safe image attachment reference for user-provided files."""
|
|
330
|
+
resolved_filename = filename or os.path.basename(path)
|
|
331
|
+
resolved_mime_type = mime_type or str(get_mime_type_from_filename(resolved_filename))
|
|
332
|
+
resolved_size = size_bytes if size_bytes is not None else _get_file_size(path)
|
|
333
|
+
return create_runtime_attachment(
|
|
334
|
+
attachment_id=attachment_id,
|
|
335
|
+
source=source,
|
|
336
|
+
filename=resolved_filename,
|
|
337
|
+
mime_type=resolved_mime_type,
|
|
338
|
+
data_ref=data_ref or path,
|
|
339
|
+
size_bytes=resolved_size,
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def create_generated_image_attachment(
|
|
344
|
+
*,
|
|
345
|
+
artifact_name: str,
|
|
346
|
+
mime_type: str,
|
|
347
|
+
artifact_data: str,
|
|
348
|
+
source: AttachmentSource = AttachmentSource.LOCAL_TOOL_OUTPUT,
|
|
349
|
+
metadata: dict[str, Any] | None = None,
|
|
350
|
+
) -> RuntimeAttachment:
|
|
351
|
+
"""Create a safe image attachment reference for a generated artifact."""
|
|
352
|
+
digest = hashlib.sha256(f"{artifact_name}:{artifact_data}".encode()).hexdigest()
|
|
353
|
+
return create_runtime_attachment(
|
|
354
|
+
attachment_id=f"img_artifact_{digest[:12]}",
|
|
355
|
+
source=source,
|
|
356
|
+
filename=artifact_name,
|
|
357
|
+
mime_type=mime_type,
|
|
358
|
+
data_ref=f"artifact://sha256/{digest}",
|
|
359
|
+
size_bytes=_decoded_base64_size(artifact_data),
|
|
360
|
+
metadata=metadata,
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
def create_generated_document_attachment(
|
|
365
|
+
*,
|
|
366
|
+
artifact_name: str,
|
|
367
|
+
mime_type: str,
|
|
368
|
+
artifact_data: str,
|
|
369
|
+
source: AttachmentSource = AttachmentSource.LOCAL_TOOL_OUTPUT,
|
|
370
|
+
metadata: dict[str, Any] | None = None,
|
|
371
|
+
) -> RuntimeAttachment:
|
|
372
|
+
"""Create a safe document attachment reference for a generated artifact."""
|
|
373
|
+
digest = hashlib.sha256(f"{artifact_name}:{artifact_data}".encode()).hexdigest()
|
|
374
|
+
return create_runtime_attachment(
|
|
375
|
+
attachment_id=f"document_artifact_{digest[:12]}",
|
|
376
|
+
source=source,
|
|
377
|
+
filename=artifact_name,
|
|
378
|
+
mime_type=mime_type,
|
|
379
|
+
kind=AttachmentKind.DOCUMENT,
|
|
380
|
+
data_ref=f"artifact://sha256/{digest}",
|
|
381
|
+
size_bytes=_decoded_base64_size(artifact_data),
|
|
382
|
+
metadata=metadata,
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
def _get_file_size(path: str) -> int | None:
|
|
387
|
+
"""Return file size when the local path is readable."""
|
|
388
|
+
try:
|
|
389
|
+
if os.path.isfile(path) and os.access(path, os.R_OK):
|
|
390
|
+
return os.path.getsize(path)
|
|
391
|
+
except OSError:
|
|
392
|
+
return None
|
|
393
|
+
return None
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
def _decoded_base64_size(value: str) -> int | None:
|
|
397
|
+
"""Return decoded byte length for base64 content without exposing bytes."""
|
|
398
|
+
try:
|
|
399
|
+
return len(base64.b64decode(value, validate=True))
|
|
400
|
+
except Exception:
|
|
401
|
+
return None
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
__all__ = [
|
|
405
|
+
"AttachmentKind",
|
|
406
|
+
"AttachmentRegistry",
|
|
407
|
+
"AttachmentSource",
|
|
408
|
+
"AttachmentEntryPayload",
|
|
409
|
+
"RuntimeAttachment",
|
|
410
|
+
"MAX_IMAGE_ATTACHMENT_BYTES",
|
|
411
|
+
"SUPPORTED_IMAGE_MIME_TYPES",
|
|
412
|
+
"create_generated_image_attachment",
|
|
413
|
+
"create_generated_document_attachment",
|
|
414
|
+
"create_runtime_attachment",
|
|
415
|
+
"create_input_image_attachment",
|
|
416
|
+
"create_runtime_image_attachment",
|
|
417
|
+
"is_supported_image_mime",
|
|
418
|
+
"is_supported_document_mime",
|
|
419
|
+
"SUPPORTED_DOCUMENT_MIME_TYPES",
|
|
420
|
+
"normalize_mime_type",
|
|
421
|
+
"merge_attachment_registry",
|
|
422
|
+
"normalize_image_mime_type",
|
|
423
|
+
"safe_image_data_ref",
|
|
424
|
+
"sanitize_image_attachment_for_trace",
|
|
425
|
+
]
|