aip-agents-binary 0.5.13__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 +6 -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 +37 -0
- aip_agents/tools/document_loader/base_reader.py +262 -0
- aip_agents/tools/document_loader/docx_reader_tool.py +53 -0
- aip_agents/tools/document_loader/excel_reader_tool.py +160 -0
- aip_agents/tools/document_loader/pdf_reader_tool.py +67 -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/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.13.dist-info/METADATA +690 -0
- aip_agents_binary-0.5.13.dist-info/RECORD +279 -0
- aip_agents_binary-0.5.13.dist-info/WHEEL +5 -0
- aip_agents_binary-0.5.13.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""Tool that generates random bar chart artifacts for streaming demos."""
|
|
2
|
+
|
|
3
|
+
import io
|
|
4
|
+
import random
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from langchain_core.tools import BaseTool
|
|
8
|
+
from langgraph.types import Command
|
|
9
|
+
from pydantic import BaseModel, Field
|
|
10
|
+
|
|
11
|
+
from aip_agents.a2a.types import MimeType
|
|
12
|
+
from aip_agents.utils.artifact_helpers import create_artifact_command
|
|
13
|
+
|
|
14
|
+
DEFAULT_RANDOM_CHART_TITLE = "Random Insights"
|
|
15
|
+
|
|
16
|
+
try:
|
|
17
|
+
from PIL import Image, ImageDraw # type: ignore
|
|
18
|
+
|
|
19
|
+
PIL_AVAILABLE = True
|
|
20
|
+
except ImportError: # pragma: no cover - PIL optional
|
|
21
|
+
PIL_AVAILABLE = False
|
|
22
|
+
Image = None
|
|
23
|
+
ImageDraw = None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class RandomChartInput(BaseModel):
|
|
27
|
+
"""Input schema for random chart generation."""
|
|
28
|
+
|
|
29
|
+
title: str = Field(
|
|
30
|
+
default=DEFAULT_RANDOM_CHART_TITLE,
|
|
31
|
+
description="Title rendered at the top of the chart.",
|
|
32
|
+
)
|
|
33
|
+
num_bars: int = Field(
|
|
34
|
+
default=5,
|
|
35
|
+
ge=3,
|
|
36
|
+
le=10,
|
|
37
|
+
description="How many bars to render.",
|
|
38
|
+
)
|
|
39
|
+
min_value: int = Field(
|
|
40
|
+
default=10,
|
|
41
|
+
ge=0,
|
|
42
|
+
description="Minimum possible value for a bar.",
|
|
43
|
+
)
|
|
44
|
+
max_value: int = Field(
|
|
45
|
+
default=100,
|
|
46
|
+
gt=10,
|
|
47
|
+
description="Maximum possible value for a bar.",
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class RandomChartTool(BaseTool):
|
|
52
|
+
"""Generate random bar chart images without relying on upstream data."""
|
|
53
|
+
|
|
54
|
+
name: str = "random_chart_tool"
|
|
55
|
+
description: str = "Create a random bar chart image artifact to showcase image streaming."
|
|
56
|
+
args_schema: type[BaseModel] = RandomChartInput
|
|
57
|
+
|
|
58
|
+
def _run( # noqa: D401
|
|
59
|
+
self,
|
|
60
|
+
title: str = DEFAULT_RANDOM_CHART_TITLE,
|
|
61
|
+
num_bars: int = 5,
|
|
62
|
+
min_value: int = 10,
|
|
63
|
+
max_value: int = 100,
|
|
64
|
+
**kwargs: Any,
|
|
65
|
+
) -> Command:
|
|
66
|
+
"""Generate the chart synchronously."""
|
|
67
|
+
if min_value >= max_value:
|
|
68
|
+
return Command(
|
|
69
|
+
update={"result": "❌ min_value must be less than max_value", "metadata": {"error": "invalid_range"}}
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
rng = random.Random()
|
|
73
|
+
values = [rng.randint(min_value, max_value) for _ in range(num_bars)]
|
|
74
|
+
labels = [f"Bar {i + 1}" for i in range(num_bars)]
|
|
75
|
+
|
|
76
|
+
if PIL_AVAILABLE:
|
|
77
|
+
image_bytes = self._create_image_with_pil(title, labels, values)
|
|
78
|
+
else:
|
|
79
|
+
image_bytes = self._create_text_artifact(title, labels, values)
|
|
80
|
+
|
|
81
|
+
artifact_mime_type = MimeType.IMAGE_PNG if PIL_AVAILABLE else MimeType.TEXT_PLAIN
|
|
82
|
+
return create_artifact_command(
|
|
83
|
+
result=f"📊 Generated random bar chart '{title}' with {num_bars} bars",
|
|
84
|
+
artifact_data=image_bytes,
|
|
85
|
+
artifact_name=f"{title.lower().replace(' ', '_')}_random_chart.png",
|
|
86
|
+
artifact_description=f"Random bar chart ({num_bars} bars)",
|
|
87
|
+
mime_type=artifact_mime_type,
|
|
88
|
+
metadata_update={
|
|
89
|
+
"visualization": {
|
|
90
|
+
"chart_type": "bar",
|
|
91
|
+
"title": title,
|
|
92
|
+
"data_points": num_bars,
|
|
93
|
+
"randomized": True,
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
async def _arun(
|
|
99
|
+
self,
|
|
100
|
+
title: str = DEFAULT_RANDOM_CHART_TITLE,
|
|
101
|
+
num_bars: int = 5,
|
|
102
|
+
min_value: int = 10,
|
|
103
|
+
max_value: int = 100,
|
|
104
|
+
**kwargs: Any,
|
|
105
|
+
) -> Command:
|
|
106
|
+
"""Async wrapper for random chart generation."""
|
|
107
|
+
return self._run(title=title, num_bars=num_bars, min_value=min_value, max_value=max_value, **kwargs)
|
|
108
|
+
|
|
109
|
+
def _create_image_with_pil(self, title: str, labels: list[str], values: list[int]) -> bytes:
|
|
110
|
+
width, height = 400, 300
|
|
111
|
+
image = Image.new("RGB", (width, height), "white")
|
|
112
|
+
draw = ImageDraw.Draw(image)
|
|
113
|
+
draw.text((10, 10), title[:40], fill="black")
|
|
114
|
+
|
|
115
|
+
chart_x, chart_y = 40, 50
|
|
116
|
+
chart_width = width - 80
|
|
117
|
+
chart_height = height - 100
|
|
118
|
+
|
|
119
|
+
max_val = max(values)
|
|
120
|
+
min_val = min(values)
|
|
121
|
+
val_range = max_val - min_val if max_val != min_val else 1
|
|
122
|
+
bar_width = chart_width // len(values)
|
|
123
|
+
|
|
124
|
+
for idx, (label, value) in enumerate(zip(labels, values, strict=False)):
|
|
125
|
+
x = chart_x + idx * bar_width
|
|
126
|
+
normalized = (value - min_val) / val_range
|
|
127
|
+
y = chart_y + chart_height - (normalized * chart_height)
|
|
128
|
+
|
|
129
|
+
draw.rectangle([x + 2, y, x + bar_width - 4, chart_y + chart_height], fill="#4C78A8", outline="black")
|
|
130
|
+
draw.text((x + 2, chart_y + chart_height + 5), label[:8], fill="black")
|
|
131
|
+
|
|
132
|
+
buf = io.BytesIO()
|
|
133
|
+
image.save(buf, format="PNG")
|
|
134
|
+
return buf.getvalue()
|
|
135
|
+
|
|
136
|
+
def _create_text_artifact(self, title: str, labels: list[str], values: list[int]) -> bytes:
|
|
137
|
+
max_val = max(values)
|
|
138
|
+
lines = [f"=== {title} ===", "", "Random bar chart artifact", ""]
|
|
139
|
+
for label, value in zip(labels, values, strict=False):
|
|
140
|
+
length = int((value / max_val) * 30) if max_val else 0
|
|
141
|
+
lines.append(f"{label:>8}: {'█' * length} ({value})")
|
|
142
|
+
return "\n".join(lines).encode("utf-8")
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
"""Tool to search Google Serper API.
|
|
2
|
+
|
|
3
|
+
Authors:
|
|
4
|
+
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
|
+
Fachriza Adhiatma (fachriza.d.adhiatma@gdplabs.id)
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
import logging
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from gllm_core.schema import Chunk
|
|
13
|
+
from langchain_core.tools import BaseTool
|
|
14
|
+
from pydantic import BaseModel, Field
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class GoogleSerperInput(BaseModel):
|
|
20
|
+
"""Input schema for the GoogleSerperTool."""
|
|
21
|
+
|
|
22
|
+
query: str = Field(..., description="Search query")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class MockGoogleSerperTool(BaseTool):
|
|
26
|
+
"""Mock Tool to simulate Google Serper API results for testing."""
|
|
27
|
+
|
|
28
|
+
name: str = "google_serper"
|
|
29
|
+
description: str = """
|
|
30
|
+
Useful for searching the web using the Google Serper API (mocked).
|
|
31
|
+
Input should be a search query.
|
|
32
|
+
"""
|
|
33
|
+
save_output_history: bool = True
|
|
34
|
+
args_schema: type[BaseModel] = GoogleSerperInput
|
|
35
|
+
|
|
36
|
+
def _run(self, query: str) -> str:
|
|
37
|
+
"""Return a hardcoded mock response as a JSON string.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
query (str): The search query to process.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
str: JSON string containing mock search results.
|
|
44
|
+
"""
|
|
45
|
+
normalized_query = query.lower()
|
|
46
|
+
|
|
47
|
+
# Special-case oncology AI drug discovery queries so the agent
|
|
48
|
+
# naturally sees stock-oriented company context and can call
|
|
49
|
+
# stock tools (get_stock_price, get_stock_news) afterwards.
|
|
50
|
+
if "oncology" in normalized_query or "drug discovery" in normalized_query:
|
|
51
|
+
mock_result = {
|
|
52
|
+
"organic": [
|
|
53
|
+
{
|
|
54
|
+
"title": "NVIDIA (NVDA) - AI infrastructure for healthcare and drug discovery",
|
|
55
|
+
"link": "https://nvidia.example.com/healthcare-ai",
|
|
56
|
+
"snippet": (
|
|
57
|
+
"NVIDIA (ticker: NVDA) provides GPU-accelerated platforms widely used in AI-driven "
|
|
58
|
+
"drug discovery and oncology research. Investors closely watch NVDA stock as demand "
|
|
59
|
+
"for healthcare and life-sciences AI workloads grows."
|
|
60
|
+
),
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"title": "Microsoft (MSFT) - Azure AI partnerships with pharma and oncology",
|
|
64
|
+
"link": "https://microsoft.example.com/azure-health",
|
|
65
|
+
"snippet": (
|
|
66
|
+
"Microsoft (MSFT) collaborates with leading pharma companies to apply Azure AI to "
|
|
67
|
+
"oncology and drug discovery workflows. Analysts frequently reference MSFT stock when "
|
|
68
|
+
"discussing enterprise AI in healthcare."
|
|
69
|
+
),
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"title": "Apple (AAPL) - Devices and AI ecosystems in digital health",
|
|
73
|
+
"link": "https://apple.example.com/health-ai",
|
|
74
|
+
"snippet": (
|
|
75
|
+
"Apple (AAPL) integrates AI into health and wellness features that support oncology "
|
|
76
|
+
"patients and clinical research. Market commentary often links AAPL stock to long-term "
|
|
77
|
+
"growth in digital health and AI."
|
|
78
|
+
),
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
"news": [
|
|
82
|
+
{
|
|
83
|
+
"title": "NVDA rallies on new healthcare AI partnerships",
|
|
84
|
+
"link": "https://news.example.com/nvda-oncology-ai",
|
|
85
|
+
"snippet": (
|
|
86
|
+
"NVIDIA (NVDA) announced expanded collaborations focused on oncology and AI-driven "
|
|
87
|
+
"drug discovery, prompting renewed interest in NVDA stock among growth investors."
|
|
88
|
+
),
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"title": "MSFT deepens AI oncology collaborations on Azure",
|
|
92
|
+
"link": "https://news.example.com/msft-azure-oncology",
|
|
93
|
+
"snippet": (
|
|
94
|
+
"Microsoft (MSFT) reported new Azure AI partnerships with oncology research centers, "
|
|
95
|
+
"and analysts highlighted MSFT stock as a key AI infrastructure play in healthcare."
|
|
96
|
+
),
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"title": "AAPL explores AI-enabled health insights for cancer care",
|
|
100
|
+
"link": "https://news.example.com/aapl-health-oncology",
|
|
101
|
+
"snippet": (
|
|
102
|
+
"Apple (AAPL) is reportedly piloting AI-enabled health features that could support "
|
|
103
|
+
"oncology patient monitoring, adding another angle to the long-term AAPL stock story."
|
|
104
|
+
),
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
}
|
|
108
|
+
else:
|
|
109
|
+
# Default mock result used for generic queries.
|
|
110
|
+
mock_result = {
|
|
111
|
+
"organic": [
|
|
112
|
+
{
|
|
113
|
+
"title": "NeoAI - Artificial Intelligence Research",
|
|
114
|
+
"link": "https://neoai.example.com/",
|
|
115
|
+
"snippet": (
|
|
116
|
+
"NeoAI is an AI research and deployment company. Our mission is to ensure that "
|
|
117
|
+
"artificial general intelligence benefits all of humanity."
|
|
118
|
+
),
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
"title": "Wikipedia - NeoAI",
|
|
122
|
+
"link": "https://en.wikipedia.org/wiki/NeoAI",
|
|
123
|
+
"snippet": (
|
|
124
|
+
"NeoAI is a fictional artificial intelligence research organization consisting of the "
|
|
125
|
+
"for-profit NeoAI LP and its parent company, the non-profit NeoAI Foundation."
|
|
126
|
+
),
|
|
127
|
+
},
|
|
128
|
+
],
|
|
129
|
+
"news": [
|
|
130
|
+
{
|
|
131
|
+
"title": "NeoAI unveils new LLM model",
|
|
132
|
+
"link": "https://news.example.com/neoai-llm",
|
|
133
|
+
"snippet": (
|
|
134
|
+
"NeoAI has announced the release of NeoAI-LLM, a new large multimodal model that accepts "
|
|
135
|
+
"image and text inputs."
|
|
136
|
+
),
|
|
137
|
+
}
|
|
138
|
+
],
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return json.dumps(mock_result)
|
|
142
|
+
|
|
143
|
+
def _format_agent_reference(self, tool_output: str) -> list[Chunk]:
|
|
144
|
+
parsed_output = self._parse_tool_output(tool_output)
|
|
145
|
+
if not parsed_output:
|
|
146
|
+
return []
|
|
147
|
+
formatted_chunks = []
|
|
148
|
+
file_id_counter = 0
|
|
149
|
+
for section_name, section_data in parsed_output.items():
|
|
150
|
+
if not isinstance(section_data, list):
|
|
151
|
+
continue
|
|
152
|
+
results = self._process_section_items(section_name, section_data, file_id_counter)
|
|
153
|
+
formatted_chunks.extend(results["chunks"])
|
|
154
|
+
file_id_counter = results["counter"]
|
|
155
|
+
return formatted_chunks
|
|
156
|
+
|
|
157
|
+
def _parse_tool_output(self, tool_output: str) -> dict[str, Any]:
|
|
158
|
+
if isinstance(tool_output, str):
|
|
159
|
+
try:
|
|
160
|
+
return json.loads(tool_output)
|
|
161
|
+
except json.JSONDecodeError:
|
|
162
|
+
logger.error("Error: Unable to parse tool_output as JSON when formatting agent references.")
|
|
163
|
+
return {}
|
|
164
|
+
return tool_output
|
|
165
|
+
|
|
166
|
+
def _process_section_items(self, section_name: str, section_data: list[dict], start_counter: int) -> dict[str, Any]:
|
|
167
|
+
chunks = []
|
|
168
|
+
counter = start_counter
|
|
169
|
+
for item in section_data:
|
|
170
|
+
if not isinstance(item, dict):
|
|
171
|
+
continue
|
|
172
|
+
try:
|
|
173
|
+
chunk = self._create_chunk_from_item(section_name, item, counter)
|
|
174
|
+
if chunk:
|
|
175
|
+
chunks.append(chunk)
|
|
176
|
+
counter += 1
|
|
177
|
+
except Exception as e:
|
|
178
|
+
logger.error(f"Error processing {section_name} result: {e}")
|
|
179
|
+
return {"chunks": chunks, "counter": counter}
|
|
180
|
+
|
|
181
|
+
def _create_chunk_from_item(self, section_name: str, item: dict[str, Any], file_id: int) -> Chunk | None:
|
|
182
|
+
link = item.get("link")
|
|
183
|
+
if not link:
|
|
184
|
+
logger.warning(f"Skipping item {file_id} from {section_name} result: Missing link")
|
|
185
|
+
return None
|
|
186
|
+
content = ""
|
|
187
|
+
if "snippet" in item:
|
|
188
|
+
content = item["snippet"]
|
|
189
|
+
elif "title" in item:
|
|
190
|
+
content = item["title"]
|
|
191
|
+
if not content:
|
|
192
|
+
logger.warning(f"Skipping item {file_id} from {section_name} result: Missing content")
|
|
193
|
+
return None
|
|
194
|
+
metadata = {
|
|
195
|
+
"source": item.get("title", "Untitled Source"),
|
|
196
|
+
"section_type": section_name,
|
|
197
|
+
"source_type": "website",
|
|
198
|
+
"title": item.get("title", "Untitled"),
|
|
199
|
+
"link": link,
|
|
200
|
+
"file_id": str(file_id),
|
|
201
|
+
}
|
|
202
|
+
return Chunk(content=content, metadata=metadata)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""Defines tools for stock market data.
|
|
2
|
+
|
|
3
|
+
Authors:
|
|
4
|
+
Christian Trisno Sen Long Chen (christian.t.s.l.chen@gdplabs.id)
|
|
5
|
+
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import random
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from langchain_core.tools import tool
|
|
12
|
+
from pydantic import BaseModel, Field
|
|
13
|
+
|
|
14
|
+
from aip_agents.utils.logger import get_logger
|
|
15
|
+
|
|
16
|
+
logger = get_logger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class StockPriceInput(BaseModel):
|
|
20
|
+
"""Input for the stock price tool."""
|
|
21
|
+
|
|
22
|
+
symbol: str = Field(description="The stock symbol (e.g., AAPL, MSFT) to get the price for.")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@tool(args_schema=StockPriceInput)
|
|
26
|
+
def get_stock_price(symbol: str) -> dict[str, Any]:
|
|
27
|
+
"""Get current stock price and performance data for a given symbol.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
symbol (str): The stock symbol (e.g., AAPL, MSFT) to get the price for.
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
dict[str, Any]: Dictionary containing stock price and performance data.
|
|
34
|
+
"""
|
|
35
|
+
logger.info(f"Getting stock price for {symbol}")
|
|
36
|
+
# Simulate stock price data
|
|
37
|
+
price = round(random.uniform(100, 1000), 2)
|
|
38
|
+
change = round(random.uniform(-10, 10), 2)
|
|
39
|
+
change_percent = round((change / price) * 100, 2)
|
|
40
|
+
return {
|
|
41
|
+
"symbol": symbol.upper(),
|
|
42
|
+
"price": price,
|
|
43
|
+
"change": change,
|
|
44
|
+
"change_percent": change_percent,
|
|
45
|
+
"currency": "USD",
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class StockNewsInput(BaseModel):
|
|
50
|
+
"""Input for the stock news tool."""
|
|
51
|
+
|
|
52
|
+
symbol: str = Field(description="The stock symbol (e.g., AAPL, MSFT) to get news for.")
|
|
53
|
+
days: int = Field(default=7, description="Number of days of news to retrieve.")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@tool(args_schema=StockNewsInput)
|
|
57
|
+
def get_stock_news(symbol: str, days: int = 7) -> dict[str, Any]:
|
|
58
|
+
"""Get recent news for a stock for a specified number of days.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
symbol (str): The stock symbol (e.g., AAPL, MSFT) to get news for.
|
|
62
|
+
days (int, optional): Number of days of news to retrieve. Defaults to 7.
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
dict[str, Any]: Dictionary containing stock news data.
|
|
66
|
+
"""
|
|
67
|
+
logger.info(f"Getting stock news for {symbol} for the last {days} days")
|
|
68
|
+
# Simulate news data
|
|
69
|
+
news_items = [
|
|
70
|
+
f"{symbol.upper()} announces breakthrough in AI technology",
|
|
71
|
+
f"Analysts raise price target for {symbol.upper()}",
|
|
72
|
+
f"{symbol.upper()} reports better than expected earnings",
|
|
73
|
+
f"New product launch boosts {symbol.upper()} shares",
|
|
74
|
+
f"{symbol.upper()} partners with a major tech firm",
|
|
75
|
+
f"Regulatory changes impact {symbol.upper()}'s sector",
|
|
76
|
+
f"{symbol.upper()} to release new sustainability report",
|
|
77
|
+
]
|
|
78
|
+
return {
|
|
79
|
+
"symbol": symbol.upper(),
|
|
80
|
+
"news": random.sample(news_items, min(days, len(news_items))),
|
|
81
|
+
"days": days,
|
|
82
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"""Table generator tool for demonstrating artifact generation.
|
|
2
|
+
|
|
3
|
+
This tool generates sample data tables and returns both a markdown representation
|
|
4
|
+
for the agent and a CSV file artifact for the user.
|
|
5
|
+
|
|
6
|
+
Authors:
|
|
7
|
+
Christian Trisno Sen Long Chen (christian.t.s.l.chen@gdplabs.id)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import csv
|
|
11
|
+
import io
|
|
12
|
+
import random
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from langchain_core.tools import BaseTool
|
|
16
|
+
from pydantic import BaseModel, Field
|
|
17
|
+
|
|
18
|
+
from aip_agents.a2a.types import MimeType
|
|
19
|
+
from aip_agents.utils.artifact_helpers import create_artifact_response
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class TableGeneratorInput(BaseModel):
|
|
23
|
+
"""Input schema for table generator tool."""
|
|
24
|
+
|
|
25
|
+
rows: int = Field(default=5, description="Number of rows to generate", ge=1, le=100)
|
|
26
|
+
columns: list[str] = Field(default=["Name", "Age", "City"], description="Column names for the table")
|
|
27
|
+
table_name: str = Field(default="sample_data", description="Name for the generated table")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class TableGeneratorTool(BaseTool):
|
|
31
|
+
"""Tool that generates sample data tables with artifact support.
|
|
32
|
+
|
|
33
|
+
This tool demonstrates the standardized artifact format by:
|
|
34
|
+
1. Generating sample data
|
|
35
|
+
2. Creating a markdown table for the agent's context
|
|
36
|
+
3. Creating a CSV file artifact for the user
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
name: str = "table_generator"
|
|
40
|
+
description: str = "Generate sample data tables with specified columns and rows"
|
|
41
|
+
args_schema: type[BaseModel] = TableGeneratorInput
|
|
42
|
+
|
|
43
|
+
def _run(self, rows: int = 5, columns: list[str] = None, table_name: str = "sample_data") -> dict[str, Any]:
|
|
44
|
+
"""Generate a table synchronously.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
rows (int, optional): Number of rows to generate. Defaults to 5.
|
|
48
|
+
columns (list[str], optional): Column names for the table. Defaults to None.
|
|
49
|
+
table_name (str, optional): Name for the generated table. Defaults to "sample_data".
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
dict[str, Any]: Response containing artifact data and metadata.
|
|
53
|
+
"""
|
|
54
|
+
if columns is None:
|
|
55
|
+
columns = ["Name", "Age", "City"]
|
|
56
|
+
|
|
57
|
+
# Generate sample data
|
|
58
|
+
sample_data = self._generate_sample_data(rows, columns)
|
|
59
|
+
|
|
60
|
+
# Create markdown table for agent
|
|
61
|
+
markdown_table = self._create_markdown_table(sample_data, columns)
|
|
62
|
+
|
|
63
|
+
# Create CSV data for artifact
|
|
64
|
+
csv_data = self._create_csv_data(sample_data, columns)
|
|
65
|
+
csv_bytes = csv_data.encode("utf-8")
|
|
66
|
+
|
|
67
|
+
# Return standardized format using utility function
|
|
68
|
+
return create_artifact_response(
|
|
69
|
+
result=f"I have generated a {rows}-row table with columns: {', '.join(columns)}.\n\n{markdown_table}",
|
|
70
|
+
artifact_data=csv_bytes,
|
|
71
|
+
artifact_name=f"{table_name}.csv",
|
|
72
|
+
artifact_description=f"Generated table with {rows} rows and {len(columns)} columns",
|
|
73
|
+
mime_type=MimeType.TEXT_CSV,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
async def _arun(self, rows: int = 5, columns: list[str] = None, table_name: str = "sample_data") -> dict[str, Any]:
|
|
77
|
+
"""Generate a table asynchronously.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
rows (int, optional): Number of rows to generate. Defaults to 5.
|
|
81
|
+
columns (list[str], optional): Column names for the table. Defaults to None.
|
|
82
|
+
table_name (str, optional): Name for the generated table. Defaults to "sample_data".
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
dict[str, Any]: Response containing artifact data and metadata.
|
|
86
|
+
"""
|
|
87
|
+
return self._run(rows, columns, table_name)
|
|
88
|
+
|
|
89
|
+
def _generate_sample_data(self, rows: int, columns: list[str]) -> list[list[str]]:
|
|
90
|
+
"""Generate sample data based on column names.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
rows (int): Number of rows to generate.
|
|
94
|
+
columns (list[str]): Column names for the table.
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
list[list[str]]: Generated sample data as list of rows.
|
|
98
|
+
"""
|
|
99
|
+
data = []
|
|
100
|
+
for i in range(rows):
|
|
101
|
+
row = []
|
|
102
|
+
for col in columns:
|
|
103
|
+
col_lower = col.lower()
|
|
104
|
+
if "name" in col_lower:
|
|
105
|
+
names = ["Alice", "Bob", "Charlie", "Diana", "Eve", "Frank", "Grace", "Henry"]
|
|
106
|
+
row.append(random.choice(names))
|
|
107
|
+
elif "age" in col_lower:
|
|
108
|
+
row.append(str(random.randint(20, 65)))
|
|
109
|
+
elif "city" in col_lower:
|
|
110
|
+
cities = ["New York", "London", "Tokyo", "Paris", "Sydney", "Berlin", "Toronto", "Mumbai"]
|
|
111
|
+
row.append(random.choice(cities))
|
|
112
|
+
elif "email" in col_lower:
|
|
113
|
+
domains = ["gmail.com", "yahoo.com", "outlook.com", "company.com"]
|
|
114
|
+
username = f"user{i + 1}"
|
|
115
|
+
row.append(f"{username}@{random.choice(domains)}")
|
|
116
|
+
elif "score" in col_lower or "rating" in col_lower:
|
|
117
|
+
row.append(str(random.randint(1, 100)))
|
|
118
|
+
else:
|
|
119
|
+
row.append(f"Value{i + 1}")
|
|
120
|
+
data.append(row)
|
|
121
|
+
return data
|
|
122
|
+
|
|
123
|
+
def _create_markdown_table(self, data: list[list[str]], columns: list[str]) -> str:
|
|
124
|
+
"""Create a markdown table representation.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
data (list[list[str]]): Table data as list of rows.
|
|
128
|
+
columns (list[str]): Column names for the table.
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
str: Markdown formatted table string.
|
|
132
|
+
"""
|
|
133
|
+
if not data:
|
|
134
|
+
return "| No data |\n|---------|"
|
|
135
|
+
|
|
136
|
+
# Header
|
|
137
|
+
header = "| " + " | ".join(columns) + " |"
|
|
138
|
+
separator = "| " + " | ".join(["---"] * len(columns)) + " |"
|
|
139
|
+
|
|
140
|
+
# Rows
|
|
141
|
+
rows = []
|
|
142
|
+
for row in data:
|
|
143
|
+
rows.append("| " + " | ".join(row) + " |")
|
|
144
|
+
|
|
145
|
+
return "\n".join([header, separator] + rows)
|
|
146
|
+
|
|
147
|
+
def _create_csv_data(self, data: list[list[str]], columns: list[str]) -> str:
|
|
148
|
+
"""Create CSV data string.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
data (list[list[str]]): Table data as list of rows.
|
|
152
|
+
columns (list[str]): Column names for the table.
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
str: CSV formatted data string.
|
|
156
|
+
"""
|
|
157
|
+
output = io.StringIO()
|
|
158
|
+
writer = csv.writer(output)
|
|
159
|
+
|
|
160
|
+
# Write header
|
|
161
|
+
writer.writerow(columns)
|
|
162
|
+
|
|
163
|
+
# Write data
|
|
164
|
+
for row in data:
|
|
165
|
+
writer.writerow(row)
|
|
166
|
+
|
|
167
|
+
return output.getvalue()
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""Tool to get the current time.
|
|
2
|
+
|
|
3
|
+
Authors:
|
|
4
|
+
Saul Sayers (saul.sayers@gdplabs.id)
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
|
|
9
|
+
from gllm_core.schema import Chunk
|
|
10
|
+
from langchain_core.tools import BaseTool
|
|
11
|
+
from pydantic import BaseModel, Field
|
|
12
|
+
|
|
13
|
+
FORMAT_STRING = "%m/%d/%y %H:%M:%S"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class TimeToolInput(BaseModel):
|
|
17
|
+
"""Input schema for the TimeTool."""
|
|
18
|
+
|
|
19
|
+
datetime_format: str = Field(
|
|
20
|
+
default=FORMAT_STRING,
|
|
21
|
+
description="""
|
|
22
|
+
Optional datetime format string. Default: '%m/%d/%y %H:%M:%S'
|
|
23
|
+
|
|
24
|
+
Common format codes:
|
|
25
|
+
%Y: Year with century (2024)
|
|
26
|
+
%m: Month as number (01-12)
|
|
27
|
+
%d: Day of month (01-31)
|
|
28
|
+
%A: Full weekday name (Wednesday)
|
|
29
|
+
%a: Short weekday name (Wed)
|
|
30
|
+
%H: Hour (00-23)
|
|
31
|
+
%M: Minute (00-59)
|
|
32
|
+
%S: Second (00-59)
|
|
33
|
+
%Z: Timezone name
|
|
34
|
+
%j: Day of year (001-366)
|
|
35
|
+
%W: Week number (00-53, Monday first)
|
|
36
|
+
%U: Week number (00-53, Sunday first)
|
|
37
|
+
%c: Locale's date and time
|
|
38
|
+
%x: Locale's date
|
|
39
|
+
%X: Locale's time
|
|
40
|
+
""",
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class TimeTool(BaseTool):
|
|
45
|
+
"""Tool to get the current time."""
|
|
46
|
+
|
|
47
|
+
name: str = "time_tool"
|
|
48
|
+
description: str = """
|
|
49
|
+
Useful for getting the current time in a specified format or the default format.
|
|
50
|
+
Default format: '%m/%d/%y %H:%M:%S' (e.g., '05/15/24 17:30:00')
|
|
51
|
+
"""
|
|
52
|
+
save_output_history: bool = True
|
|
53
|
+
args_schema: type[BaseModel] = TimeToolInput
|
|
54
|
+
|
|
55
|
+
def _run(self, datetime_format: str = FORMAT_STRING) -> str:
|
|
56
|
+
"""Get the current time formatted as a string.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
datetime_format (str): The format string to use for the datetime output. Defaults to FORMAT_STRING.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
str: The current time formatted according to the provided format string.
|
|
63
|
+
"""
|
|
64
|
+
return datetime.now().strftime(datetime_format)
|
|
65
|
+
|
|
66
|
+
def _format_agent_reference(self, tool_output: str) -> list[Chunk]:
|
|
67
|
+
"""Format the tool output as a reference chunk for agent use.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
tool_output (str): The output string from the tool execution.
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
list[Chunk]: A list containing a single reference chunk with the tool output and metadata.
|
|
74
|
+
"""
|
|
75
|
+
return [
|
|
76
|
+
Chunk(
|
|
77
|
+
content=tool_output,
|
|
78
|
+
metadata={
|
|
79
|
+
"tool_name": self.name,
|
|
80
|
+
},
|
|
81
|
+
)
|
|
82
|
+
]
|