aip-agents-binary 0.5.25__py3-none-macosx_13_0_arm64.whl → 0.6.8__py3-none-macosx_13_0_arm64.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.
Files changed (109) hide show
  1. aip_agents/agent/__init__.py +44 -4
  2. aip_agents/agent/base_langgraph_agent.py +163 -74
  3. aip_agents/agent/base_langgraph_agent.pyi +3 -2
  4. aip_agents/agent/langgraph_memory_enhancer_agent.py +368 -34
  5. aip_agents/agent/langgraph_memory_enhancer_agent.pyi +3 -2
  6. aip_agents/agent/langgraph_react_agent.py +329 -22
  7. aip_agents/agent/langgraph_react_agent.pyi +41 -2
  8. aip_agents/examples/hello_world_ptc.py +49 -0
  9. aip_agents/examples/hello_world_ptc.pyi +5 -0
  10. aip_agents/examples/hello_world_ptc_custom_tools.py +83 -0
  11. aip_agents/examples/hello_world_ptc_custom_tools.pyi +7 -0
  12. aip_agents/examples/hello_world_tool_output_client.py +9 -0
  13. aip_agents/examples/tools/multiply_tool.py +43 -0
  14. aip_agents/examples/tools/multiply_tool.pyi +18 -0
  15. aip_agents/guardrails/engines/base.py +6 -6
  16. aip_agents/mcp/client/__init__.py +38 -2
  17. aip_agents/mcp/client/connection_manager.py +36 -1
  18. aip_agents/mcp/client/connection_manager.pyi +3 -0
  19. aip_agents/mcp/client/persistent_session.py +318 -68
  20. aip_agents/mcp/client/persistent_session.pyi +9 -0
  21. aip_agents/mcp/client/transports.py +37 -2
  22. aip_agents/mcp/client/transports.pyi +9 -0
  23. aip_agents/memory/adapters/base_adapter.py +98 -0
  24. aip_agents/memory/adapters/base_adapter.pyi +25 -0
  25. aip_agents/ptc/__init__.py +87 -0
  26. aip_agents/ptc/__init__.pyi +14 -0
  27. aip_agents/ptc/custom_tools.py +473 -0
  28. aip_agents/ptc/custom_tools.pyi +184 -0
  29. aip_agents/ptc/custom_tools_payload.py +400 -0
  30. aip_agents/ptc/custom_tools_payload.pyi +31 -0
  31. aip_agents/ptc/custom_tools_templates/__init__.py +1 -0
  32. aip_agents/ptc/custom_tools_templates/__init__.pyi +0 -0
  33. aip_agents/ptc/custom_tools_templates/custom_build_function.py.template +23 -0
  34. aip_agents/ptc/custom_tools_templates/custom_init.py.template +15 -0
  35. aip_agents/ptc/custom_tools_templates/custom_invoke.py.template +60 -0
  36. aip_agents/ptc/custom_tools_templates/custom_registry.py.template +87 -0
  37. aip_agents/ptc/custom_tools_templates/custom_sources_init.py.template +7 -0
  38. aip_agents/ptc/custom_tools_templates/custom_wrapper.py.template +19 -0
  39. aip_agents/ptc/doc_gen.py +122 -0
  40. aip_agents/ptc/doc_gen.pyi +40 -0
  41. aip_agents/ptc/exceptions.py +57 -0
  42. aip_agents/ptc/exceptions.pyi +37 -0
  43. aip_agents/ptc/executor.py +261 -0
  44. aip_agents/ptc/executor.pyi +99 -0
  45. aip_agents/ptc/mcp/__init__.py +45 -0
  46. aip_agents/ptc/mcp/__init__.pyi +7 -0
  47. aip_agents/ptc/mcp/sandbox_bridge.py +668 -0
  48. aip_agents/ptc/mcp/sandbox_bridge.pyi +47 -0
  49. aip_agents/ptc/mcp/templates/__init__.py +1 -0
  50. aip_agents/ptc/mcp/templates/__init__.pyi +0 -0
  51. aip_agents/ptc/mcp/templates/mcp_client.py.template +239 -0
  52. aip_agents/ptc/naming.py +196 -0
  53. aip_agents/ptc/naming.pyi +85 -0
  54. aip_agents/ptc/payload.py +26 -0
  55. aip_agents/ptc/payload.pyi +15 -0
  56. aip_agents/ptc/prompt_builder.py +673 -0
  57. aip_agents/ptc/prompt_builder.pyi +59 -0
  58. aip_agents/ptc/ptc_helper.py +16 -0
  59. aip_agents/ptc/ptc_helper.pyi +1 -0
  60. aip_agents/ptc/sandbox_bridge.py +256 -0
  61. aip_agents/ptc/sandbox_bridge.pyi +38 -0
  62. aip_agents/ptc/template_utils.py +33 -0
  63. aip_agents/ptc/template_utils.pyi +13 -0
  64. aip_agents/ptc/templates/__init__.py +1 -0
  65. aip_agents/ptc/templates/__init__.pyi +0 -0
  66. aip_agents/ptc/templates/ptc_helper.py.template +134 -0
  67. aip_agents/ptc/tool_def_helpers.py +101 -0
  68. aip_agents/ptc/tool_def_helpers.pyi +38 -0
  69. aip_agents/ptc/tool_enrichment.py +163 -0
  70. aip_agents/ptc/tool_enrichment.pyi +60 -0
  71. aip_agents/sandbox/__init__.py +43 -0
  72. aip_agents/sandbox/__init__.pyi +5 -0
  73. aip_agents/sandbox/defaults.py +205 -0
  74. aip_agents/sandbox/defaults.pyi +30 -0
  75. aip_agents/sandbox/e2b_runtime.py +295 -0
  76. aip_agents/sandbox/e2b_runtime.pyi +57 -0
  77. aip_agents/sandbox/template_builder.py +131 -0
  78. aip_agents/sandbox/template_builder.pyi +36 -0
  79. aip_agents/sandbox/types.py +24 -0
  80. aip_agents/sandbox/types.pyi +14 -0
  81. aip_agents/sandbox/validation.py +50 -0
  82. aip_agents/sandbox/validation.pyi +20 -0
  83. aip_agents/sentry/sentry.py +29 -8
  84. aip_agents/sentry/sentry.pyi +3 -2
  85. aip_agents/tools/__init__.py +13 -2
  86. aip_agents/tools/__init__.pyi +3 -1
  87. aip_agents/tools/browser_use/browser_use_tool.py +8 -0
  88. aip_agents/tools/browser_use/streaming.py +2 -0
  89. aip_agents/tools/date_range_tool.py +554 -0
  90. aip_agents/tools/date_range_tool.pyi +21 -0
  91. aip_agents/tools/execute_ptc_code.py +357 -0
  92. aip_agents/tools/execute_ptc_code.pyi +90 -0
  93. aip_agents/tools/memory_search/__init__.py +8 -1
  94. aip_agents/tools/memory_search/__init__.pyi +3 -3
  95. aip_agents/tools/memory_search/mem0.py +114 -1
  96. aip_agents/tools/memory_search/mem0.pyi +11 -1
  97. aip_agents/tools/memory_search/schema.py +33 -0
  98. aip_agents/tools/memory_search/schema.pyi +10 -0
  99. aip_agents/tools/memory_search_tool.py +8 -0
  100. aip_agents/tools/memory_search_tool.pyi +2 -2
  101. aip_agents/utils/langgraph/tool_managers/delegation_tool_manager.py +26 -1
  102. aip_agents/utils/langgraph/tool_output_management.py +80 -0
  103. aip_agents/utils/langgraph/tool_output_management.pyi +37 -0
  104. {aip_agents_binary-0.5.25.dist-info → aip_agents_binary-0.6.8.dist-info}/METADATA +9 -19
  105. {aip_agents_binary-0.5.25.dist-info → aip_agents_binary-0.6.8.dist-info}/RECORD +107 -41
  106. {aip_agents_binary-0.5.25.dist-info → aip_agents_binary-0.6.8.dist-info}/WHEEL +1 -1
  107. aip_agents/examples/demo_memory_recall.py +0 -401
  108. aip_agents/examples/demo_memory_recall.pyi +0 -58
  109. {aip_agents_binary-0.5.25.dist-info → aip_agents_binary-0.6.8.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,59 @@
1
+ from _typeshed import Incomplete
2
+ from aip_agents.mcp.client.base_mcp_client import BaseMCPClient as BaseMCPClient
3
+ from aip_agents.ptc.custom_tools import PTCCustomToolConfig as PTCCustomToolConfig
4
+ from aip_agents.ptc.naming import example_value_from_schema as example_value_from_schema, sanitize_function_name as sanitize_function_name, sanitize_module_name_with_reserved as sanitize_module_name_with_reserved, sanitize_param_name as sanitize_param_name, schema_to_params as schema_to_params
5
+ from aip_agents.utils.logger import get_logger as get_logger
6
+ from dataclasses import dataclass
7
+
8
+ logger: Incomplete
9
+ PromptMode: Incomplete
10
+ PYTHON_BLOCK_START: str
11
+
12
+ @dataclass
13
+ class PromptConfig:
14
+ """Configuration for PTC prompt generation.
15
+
16
+ Attributes:
17
+ mode: Prompt mode - minimal, index, full, or auto.
18
+ auto_threshold: Total tool count threshold for auto mode (default 10).
19
+ include_example: Whether to include example code in prompt.
20
+ """
21
+ mode: PromptMode = ...
22
+ auto_threshold: int = ...
23
+ include_example: bool = ...
24
+
25
+ PTC_USAGE_RULES: str
26
+
27
+ def build_ptc_prompt(mcp_client: BaseMCPClient | None = None, config: PromptConfig | None = None, custom_tools_config: PTCCustomToolConfig | None = None) -> str:
28
+ """Build PTC usage guidance prompt from MCP and custom tool configurations.
29
+
30
+ Generates a short usage block that includes:
31
+ - The import patterns: MCP (`from tools.<server> import <tool>`) and
32
+ custom (`from tools.custom import <tool>`)
33
+ - Rule: use `print()`; only printed output returns
34
+ - Rule: parameter names are sanitized to lowercase/underscored
35
+ - Prompt mode content (minimal/index/full)
36
+ - Examples based on the resolved prompt mode
37
+
38
+ Args:
39
+ mcp_client: The MCP client with configured servers. Can be None if only custom tools.
40
+ config: Prompt configuration. If None, uses default PromptConfig.
41
+ custom_tools_config: Optional custom LangChain tools configuration.
42
+
43
+ Returns:
44
+ PTC usage guidance prompt string.
45
+ """
46
+ def compute_ptc_prompt_hash(mcp_client: BaseMCPClient | None = None, config: PromptConfig | None = None, custom_tools_config: PTCCustomToolConfig | None = None) -> str:
47
+ """Compute a hash of the MCP and custom tool configuration for change detection.
48
+
49
+ Includes PromptConfig fields, allowed_tools, and custom tools in hash computation
50
+ so prompt updates re-sync correctly when configuration changes.
51
+
52
+ Args:
53
+ mcp_client: MCP client instance. Can be None if only custom tools.
54
+ config: Prompt configuration. If None, uses default PromptConfig.
55
+ custom_tools_config: Optional custom LangChain tools configuration.
56
+
57
+ Returns:
58
+ Hash string representing current configuration.
59
+ """
@@ -0,0 +1,16 @@
1
+ """PTC helper module generation utilities."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from aip_agents.ptc.template_utils import render_template
6
+
7
+ _TEMPLATE_PACKAGE = "aip_agents.ptc.templates"
8
+
9
+
10
+ def _generate_ptc_helper_module() -> str:
11
+ """Generate the tools/ptc_helper.py discovery module.
12
+
13
+ Returns:
14
+ Python source code for the PTC helper module.
15
+ """
16
+ return render_template(_TEMPLATE_PACKAGE, "ptc_helper.py.template")
@@ -0,0 +1 @@
1
+ from aip_agents.ptc.template_utils import render_template as render_template
@@ -0,0 +1,256 @@
1
+ """Top-level PTC Sandbox Bridge.
2
+
3
+ This module provides the unified entry point for building sandbox payloads
4
+ across different tool sources (MCP, custom tools, etc.).
5
+
6
+ Authors:
7
+ Putu Ravindra Wiguna (putu.r.wiguna@gdplabs.id)
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import json
13
+ from typing import Any
14
+
15
+ from aip_agents.mcp.client.base_mcp_client import BaseMCPClient
16
+ from aip_agents.ptc.custom_tools import (
17
+ PTCCustomToolConfig,
18
+ validate_custom_tool_config,
19
+ )
20
+ from aip_agents.ptc.custom_tools_payload import build_custom_tools_payload
21
+ from aip_agents.ptc.doc_gen import (
22
+ render_tool_doc,
23
+ )
24
+ from aip_agents.ptc.exceptions import PTCPayloadConflictError
25
+ from aip_agents.ptc.mcp.sandbox_bridge import build_mcp_payload
26
+ from aip_agents.ptc.naming import sanitize_function_name, schema_to_params
27
+ from aip_agents.ptc.payload import SandboxPayload
28
+ from aip_agents.ptc.ptc_helper import _generate_ptc_helper_module
29
+
30
+
31
+ async def build_sandbox_payload(
32
+ mcp_client: BaseMCPClient | None = None,
33
+ default_tool_timeout: float = 60.0,
34
+ custom_tools_config: PTCCustomToolConfig | None = None,
35
+ tool_configs: dict[str, dict] | None = None,
36
+ ) -> SandboxPayload:
37
+ """Build sandbox payload from all configured tool sources.
38
+
39
+ Composes MCP and custom LangChain tool payloads into a single payload.
40
+
41
+ Args:
42
+ mcp_client: The MCP client with configured servers. Can be None if only custom tools.
43
+ default_tool_timeout: Default timeout for tool calls in seconds.
44
+ custom_tools_config: Optional custom LangChain tools configuration.
45
+ tool_configs: Optional per-tool config values for custom tools.
46
+
47
+ Returns:
48
+ SandboxPayload containing files and env vars for the sandbox.
49
+ """
50
+ # Build MCP payload
51
+ mcp_payload = SandboxPayload()
52
+ if mcp_client:
53
+ mcp_payload = await build_mcp_payload(mcp_client, default_tool_timeout)
54
+
55
+ # Build custom tools payload if enabled
56
+ custom_payload = SandboxPayload()
57
+ if custom_tools_config and custom_tools_config.enabled:
58
+ # Validate config before building payload (fail fast)
59
+ validate_custom_tool_config(custom_tools_config)
60
+ result = build_custom_tools_payload(custom_tools_config, tool_configs)
61
+ custom_payload = result.payload
62
+
63
+ # Check for conflicts before merging payloads
64
+ _check_payload_conflicts(mcp_payload, custom_payload)
65
+
66
+ # Merge payloads (custom tools files should not conflict with MCP files)
67
+ merged = SandboxPayload()
68
+ merged.files.update(mcp_payload.files)
69
+ merged.files.update(custom_payload.files)
70
+ merged.per_run_files.update(mcp_payload.per_run_files)
71
+ merged.per_run_files.update(custom_payload.per_run_files)
72
+ merged.env.update(mcp_payload.env)
73
+ merged.env.update(custom_payload.env)
74
+
75
+ # Merge custom tools into ptc_index.json
76
+ if custom_tools_config and custom_tools_config.enabled and custom_tools_config.tools:
77
+ merged.files["tools/ptc_index.json"] = _merge_custom_tools_into_index(
78
+ merged.files.get("tools/ptc_index.json"),
79
+ custom_tools_config,
80
+ )
81
+ if "tools/ptc_helper.py" not in merged.files:
82
+ merged.files["tools/ptc_helper.py"] = _generate_ptc_helper_module()
83
+ # Generate documentation for custom tools
84
+ custom_docs = _generate_custom_tool_docs(custom_tools_config)
85
+ merged.files.update(custom_docs)
86
+
87
+ return merged
88
+
89
+
90
+ def _check_payload_conflicts(
91
+ mcp_payload: SandboxPayload,
92
+ custom_payload: SandboxPayload,
93
+ ) -> None:
94
+ """Check for conflicts between MCP and custom tool payloads.
95
+
96
+ Args:
97
+ mcp_payload: Payload from MCP tool configuration.
98
+ custom_payload: Payload from custom tool configuration.
99
+
100
+ Raises:
101
+ PTCPayloadConflictError: If any conflicts are detected in files,
102
+ per-run files, or environment variables.
103
+ """
104
+ mcp_files = set(mcp_payload.files.keys())
105
+ custom_files = set(custom_payload.files.keys())
106
+ file_conflicts = mcp_files & custom_files
107
+
108
+ mcp_per_run = set(mcp_payload.per_run_files.keys())
109
+ custom_per_run = set(custom_payload.per_run_files.keys())
110
+ per_run_conflicts = mcp_per_run & custom_per_run
111
+
112
+ mcp_env_keys = set(mcp_payload.env.keys())
113
+ custom_env_keys = set(custom_payload.env.keys())
114
+ env_conflicts = mcp_env_keys & custom_env_keys
115
+
116
+ all_conflicts = file_conflicts | per_run_conflicts | env_conflicts
117
+ if all_conflicts:
118
+ raise PTCPayloadConflictError(
119
+ f"Conflicts detected when merging MCP and custom tool payloads. "
120
+ f"Files: {sorted(file_conflicts)}, "
121
+ f"Per-run files: {sorted(per_run_conflicts)}, "
122
+ f"Environment variables: {sorted(env_conflicts)}",
123
+ conflicts=all_conflicts,
124
+ )
125
+
126
+
127
+ def _merge_custom_tools_into_index(
128
+ existing_index_json: str | None,
129
+ custom_tools_config: PTCCustomToolConfig,
130
+ ) -> str:
131
+ """Merge custom tools into the ptc_index.json.
132
+
133
+ Args:
134
+ existing_index_json: Existing ptc_index.json content (may be None).
135
+ custom_tools_config: Custom tools configuration.
136
+
137
+ Returns:
138
+ Updated JSON string with custom tools included.
139
+ """
140
+ # Parse existing index or create new one
141
+ if existing_index_json:
142
+ index = json.loads(existing_index_json)
143
+ else:
144
+ index = {"packages": {}}
145
+
146
+ # Add custom tools package
147
+ tool_entries = []
148
+ for tool_def in sorted(custom_tools_config.tools, key=lambda t: sanitize_function_name(t.get("name", ""))):
149
+ name = tool_def.get("name", "")
150
+ func_name = sanitize_function_name(name)
151
+ schema = tool_def.get("input_schema", {"type": "object", "properties": {}})
152
+ signature = f"{func_name}({schema_to_params(schema)})"
153
+
154
+ tool_entries.append(
155
+ {
156
+ "name": func_name,
157
+ "signature": signature,
158
+ "doc_path": f"tools/docs/custom/{func_name}.md",
159
+ }
160
+ )
161
+
162
+ index["packages"]["custom"] = {"tools": tool_entries}
163
+
164
+ return json.dumps(index, indent=2, sort_keys=True)
165
+
166
+
167
+ def _generate_custom_tool_docs(custom_tools_config: PTCCustomToolConfig) -> dict[str, str]:
168
+ """Generate documentation files for custom tools.
169
+
170
+ Args:
171
+ custom_tools_config: Custom tools configuration.
172
+
173
+ Returns:
174
+ Dict mapping file path to content.
175
+ """
176
+ docs: dict[str, str] = {}
177
+
178
+ for tool_def in sorted(custom_tools_config.tools, key=lambda t: sanitize_function_name(t.get("name", ""))):
179
+ name = tool_def.get("name", "")
180
+ func_name = sanitize_function_name(name)
181
+ description = tool_def.get("description", "")
182
+ schema = tool_def.get("input_schema", {"type": "object", "properties": {}})
183
+
184
+ doc_content = _generate_tool_doc_content(func_name, description, schema)
185
+ docs[f"tools/docs/custom/{func_name}.md"] = doc_content
186
+
187
+ return docs
188
+
189
+
190
+ def _generate_tool_doc_content(
191
+ func_name: str,
192
+ description: str,
193
+ schema: dict[str, Any],
194
+ ) -> str:
195
+ """Generate markdown documentation for a single tool.
196
+
197
+ Args:
198
+ func_name: Sanitized function name.
199
+ description: Tool description.
200
+ schema: JSON schema for tool input.
201
+
202
+ Returns:
203
+ Markdown documentation string.
204
+ """
205
+ # Use schema_to_params for consistent signatures
206
+ params = schema_to_params(schema)
207
+ signature = f"{func_name}({params})"
208
+
209
+ example_code = f"from tools.custom import {func_name}\nresult = {func_name}(...)\nprint(result)"
210
+
211
+ return render_tool_doc(
212
+ func_name=func_name,
213
+ signature=signature,
214
+ description=description,
215
+ schema=schema,
216
+ example_code=example_code,
217
+ )
218
+
219
+
220
+ def wrap_ptc_code(code: str, include_packages_path: bool = False) -> str:
221
+ """Wrap user PTC code with necessary imports and setup.
222
+
223
+ This prepends sys.path setup to ensure the tools package is importable.
224
+ When custom tools with bundled package sources are enabled, also adds
225
+ the packages/ directory to sys.path.
226
+
227
+ Args:
228
+ code: User-provided Python code.
229
+ include_packages_path: If True, also add packages/ dir to sys.path
230
+ for bundled package sources from custom LangChain tools.
231
+
232
+ Returns:
233
+ Wrapped code ready for sandbox execution.
234
+ """
235
+ preamble = """# PTC Code Wrapper - Auto-generated
236
+ import sys
237
+ import os
238
+
239
+ # Add tools package to path
240
+ _tools_dir = os.path.dirname(os.path.abspath(__file__)) if "__file__" in dir() else os.getcwd()
241
+ if _tools_dir not in sys.path:
242
+ sys.path.insert(0, _tools_dir)
243
+ """
244
+
245
+ if include_packages_path:
246
+ preamble += """
247
+ # Add packages directory to path for bundled custom tool sources
248
+ _packages_dir = os.path.join(_tools_dir, "packages")
249
+ if os.path.isdir(_packages_dir) and _packages_dir not in sys.path:
250
+ sys.path.insert(0, _packages_dir)
251
+ """
252
+
253
+ preamble += """
254
+ # User code below
255
+ """
256
+ return preamble + code
@@ -0,0 +1,38 @@
1
+ from aip_agents.mcp.client.base_mcp_client import BaseMCPClient as BaseMCPClient
2
+ from aip_agents.ptc.custom_tools import PTCCustomToolConfig as PTCCustomToolConfig, validate_custom_tool_config as validate_custom_tool_config
3
+ from aip_agents.ptc.custom_tools_payload import build_custom_tools_payload as build_custom_tools_payload
4
+ from aip_agents.ptc.doc_gen import render_tool_doc as render_tool_doc
5
+ from aip_agents.ptc.exceptions import PTCPayloadConflictError as PTCPayloadConflictError
6
+ from aip_agents.ptc.mcp.sandbox_bridge import build_mcp_payload as build_mcp_payload
7
+ from aip_agents.ptc.naming import sanitize_function_name as sanitize_function_name, schema_to_params as schema_to_params
8
+ from aip_agents.ptc.payload import SandboxPayload as SandboxPayload
9
+
10
+ async def build_sandbox_payload(mcp_client: BaseMCPClient | None = None, default_tool_timeout: float = 60.0, custom_tools_config: PTCCustomToolConfig | None = None, tool_configs: dict[str, dict] | None = None) -> SandboxPayload:
11
+ """Build sandbox payload from all configured tool sources.
12
+
13
+ Composes MCP and custom LangChain tool payloads into a single payload.
14
+
15
+ Args:
16
+ mcp_client: The MCP client with configured servers. Can be None if only custom tools.
17
+ default_tool_timeout: Default timeout for tool calls in seconds.
18
+ custom_tools_config: Optional custom LangChain tools configuration.
19
+ tool_configs: Optional per-tool config values for custom tools.
20
+
21
+ Returns:
22
+ SandboxPayload containing files and env vars for the sandbox.
23
+ """
24
+ def wrap_ptc_code(code: str, include_packages_path: bool = False) -> str:
25
+ """Wrap user PTC code with necessary imports and setup.
26
+
27
+ This prepends sys.path setup to ensure the tools package is importable.
28
+ When custom tools with bundled package sources are enabled, also adds
29
+ the packages/ directory to sys.path.
30
+
31
+ Args:
32
+ code: User-provided Python code.
33
+ include_packages_path: If True, also add packages/ dir to sys.path
34
+ for bundled package sources from custom LangChain tools.
35
+
36
+ Returns:
37
+ Wrapped code ready for sandbox execution.
38
+ """
@@ -0,0 +1,33 @@
1
+ """Template rendering helpers for PTC payloads.
2
+
3
+ Authors:
4
+ Putu Ravindra Wiguna (putu.r.wiguna@gdplabs.id)
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from collections.abc import Mapping
10
+ from importlib import resources
11
+ from string import Template
12
+
13
+
14
+ def render_template(
15
+ package: str,
16
+ template_name: str,
17
+ values: Mapping[str, str] | None = None,
18
+ ) -> str:
19
+ """Render a template from package resources with optional substitutions.
20
+
21
+ Args:
22
+ package: Package path containing the template.
23
+ template_name: Template filename.
24
+ values: Optional mapping of template variables.
25
+
26
+ Returns:
27
+ Rendered template content.
28
+ """
29
+ template_file = resources.files(package).joinpath(template_name)
30
+ template_text = template_file.read_text(encoding="utf-8")
31
+ if not values:
32
+ return template_text
33
+ return Template(template_text).substitute(values)
@@ -0,0 +1,13 @@
1
+ from collections.abc import Mapping
2
+
3
+ def render_template(package: str, template_name: str, values: Mapping[str, str] | None = None) -> str:
4
+ """Render a template from package resources with optional substitutions.
5
+
6
+ Args:
7
+ package: Package path containing the template.
8
+ template_name: Template filename.
9
+ values: Optional mapping of template variables.
10
+
11
+ Returns:
12
+ Rendered template content.
13
+ """
@@ -0,0 +1 @@
1
+ """Shared templates for PTC sandbox code generation."""
File without changes
@@ -0,0 +1,134 @@
1
+ """PTC Discovery Helper Module.
2
+
3
+ This module provides discovery functions for exploring available PTC tools
4
+ inside the PTC sandbox. Use these functions to find packages, list tools,
5
+ and get detailed documentation.
6
+
7
+ Usage:
8
+ from tools.ptc_helper import list_packages, list_tools, describe_tool
9
+
10
+ # List all available packages
11
+ packages = list_packages()
12
+
13
+ # List tools in a package
14
+ tools = list_tools("package_name")
15
+
16
+ # Get tool documentation
17
+ doc = describe_tool("package_name", "tool_name")
18
+ """
19
+
20
+ import difflib
21
+ import json
22
+ import os
23
+ from typing import Any
24
+
25
+ # Load the index at module import time
26
+ _INDEX_PATH = os.path.join(os.path.dirname(__file__), "ptc_index.json")
27
+ _DOCS_DIR = os.path.join(os.path.dirname(__file__), "docs")
28
+
29
+ _index: dict[str, Any] = {}
30
+ if os.path.exists(_INDEX_PATH):
31
+ with open(_INDEX_PATH, "r") as f:
32
+ _index = json.load(f)
33
+
34
+
35
+ def _suggest_closest(name: str, valid_names: list[str], kind: str = "name") -> str:
36
+ """Generate a suggestion message for closest match.
37
+
38
+ Args:
39
+ name: The name that was not found.
40
+ valid_names: List of valid names to match against.
41
+ kind: Type of name (package or tool) for the error message.
42
+
43
+ Returns:
44
+ Suggestion string or empty string if no close match.
45
+ """
46
+ matches = difflib.get_close_matches(name, valid_names, n=1, cutoff=0.6)
47
+ if matches:
48
+ return f" Did you mean '{matches[0]}'?"
49
+ return ""
50
+
51
+
52
+ def list_packages() -> list[str]:
53
+ """List all available package names.
54
+
55
+ Returns:
56
+ Sorted list of sanitized package names.
57
+ """
58
+ packages = _index.get("packages", {})
59
+ return sorted(packages.keys())
60
+
61
+
62
+ def list_tools(package: str) -> list[dict[str, str]]:
63
+ """List tools available in a package.
64
+
65
+ Args:
66
+ package: Sanitized package name (e.g., 'deepwiki').
67
+
68
+ Returns:
69
+ List of tool info dicts with 'name' keys.
70
+
71
+ Raises:
72
+ ValueError: If package is not found.
73
+ """
74
+ packages = _index.get("packages", {})
75
+ if package not in packages:
76
+ valid = list(packages.keys())
77
+ suggestion = _suggest_closest(package, valid, "package")
78
+ raise ValueError(f"Unknown package '{package}'.{suggestion}")
79
+
80
+ pkg_data = packages[package]
81
+ tools = pkg_data.get("tools", [])
82
+ return [{"name": t["name"]} for t in tools]
83
+
84
+
85
+ def describe_tool(package: str, tool: str) -> dict[str, Any]:
86
+ """Get detailed documentation for a tool.
87
+
88
+ Args:
89
+ package: Sanitized package name.
90
+ tool: Sanitized tool name.
91
+
92
+ Returns:
93
+ Dict with 'name', 'signature', 'doc', and 'doc_path' keys.
94
+
95
+ Raises:
96
+ ValueError: If package or tool is not found.
97
+ """
98
+ packages = _index.get("packages", {})
99
+ if package not in packages:
100
+ valid = list(packages.keys())
101
+ suggestion = _suggest_closest(package, valid, "package")
102
+ raise ValueError(f"Unknown package '{package}'.{suggestion}")
103
+
104
+ pkg_data = packages[package]
105
+ tools = pkg_data.get("tools", [])
106
+ tool_names = [t["name"] for t in tools]
107
+
108
+ if tool not in tool_names:
109
+ suggestion = _suggest_closest(tool, tool_names, "tool")
110
+ raise ValueError(f"Unknown tool '{tool}' in package '{package}'.{suggestion}")
111
+
112
+ tool_info = next(t for t in tools if t["name"] == tool)
113
+ doc_path = tool_info.get("doc_path", "")
114
+
115
+ doc_content = ""
116
+ if doc_path:
117
+ full_path = os.path.join(os.path.dirname(__file__), doc_path.replace("tools/", ""))
118
+ if os.path.exists(full_path):
119
+ with open(full_path, "r") as f:
120
+ doc_content = f.read()
121
+ else:
122
+ doc_content = f"# {tool}\n\n**Signature:** `{tool_info.get('signature', f'{tool}(**kwargs: Any)')}`"
123
+ else:
124
+ doc_content = f"# {tool}\n\n**Signature:** `{tool_info.get('signature', f'{tool}(**kwargs: Any)')}`"
125
+
126
+ return {
127
+ "name": tool,
128
+ "signature": tool_info.get("signature", f"{tool}(**kwargs)"),
129
+ "doc": doc_content,
130
+ "doc_path": doc_path,
131
+ }
132
+
133
+
134
+ __all__ = ["list_packages", "list_tools", "describe_tool"]
@@ -0,0 +1,101 @@
1
+ """Helper functions for constructing PTC tool definitions.
2
+
3
+ Author: Putu Ravindra Wiguna (putu.r.wiguna@gdplabs.id)
4
+
5
+ These helpers provide a cleaner API for defining custom tools in PTCCustomToolConfig.
6
+ They return properly-typed dictionaries that can be used in PTCCustomToolConfig.tools.
7
+
8
+ Example:
9
+ >>> from aip_agents.ptc import package_tool, file_tool, PTCCustomToolConfig
10
+ >>> config = PTCCustomToolConfig(
11
+ ... enabled=True,
12
+ ... tools=[
13
+ ... package_tool("time_tool", import_path="aip_agents.tools.time_tool", class_name="TimeTool"),
14
+ ... file_tool("multiply", file_path="/path/to/multiply.py", class_name="MultiplyTool"),
15
+ ... ]
16
+ ... )
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ from typing import Any
22
+
23
+ from aip_agents.ptc.custom_tools import PTCFileToolDef, PTCPackageToolDef
24
+
25
+
26
+ def package_tool(
27
+ name: str,
28
+ *,
29
+ import_path: str,
30
+ class_name: str,
31
+ package_path: str | None = None,
32
+ description: str | None = None,
33
+ input_schema: dict[str, Any] | None = None,
34
+ ) -> PTCPackageToolDef:
35
+ """Create a package-based tool definition.
36
+
37
+ Args:
38
+ name: Tool name (will be sanitized for Python compatibility).
39
+ import_path: Python import path (e.g., "aip_agents.tools.time_tool").
40
+ class_name: Tool class name to instantiate.
41
+ package_path: Optional local package path for bundling.
42
+ description: Optional tool description (auto-derived from tool if not provided).
43
+ input_schema: Optional JSON schema (auto-derived from tool if not provided).
44
+
45
+ Returns:
46
+ PTCPackageToolDef dict ready for use in PTCCustomToolConfig.tools.
47
+
48
+ Example:
49
+ >>> package_tool("time_tool", import_path="aip_agents.tools.time_tool", class_name="TimeTool")
50
+ {'name': 'time_tool', 'kind': 'package', 'import_path': 'aip_agents.tools.time_tool', 'class_name': 'TimeTool'}
51
+ """
52
+ result: PTCPackageToolDef = {
53
+ "name": name,
54
+ "kind": "package",
55
+ "import_path": import_path,
56
+ "class_name": class_name,
57
+ }
58
+ if package_path is not None:
59
+ result["package_path"] = package_path
60
+ if description is not None:
61
+ result["description"] = description
62
+ if input_schema is not None:
63
+ result["input_schema"] = input_schema
64
+ return result
65
+
66
+
67
+ def file_tool(
68
+ name: str,
69
+ *,
70
+ file_path: str,
71
+ class_name: str,
72
+ description: str | None = None,
73
+ input_schema: dict[str, Any] | None = None,
74
+ ) -> PTCFileToolDef:
75
+ """Create a file-based tool definition.
76
+
77
+ Args:
78
+ name: Tool name (will be sanitized for Python compatibility).
79
+ file_path: Absolute path to the Python file containing the tool.
80
+ class_name: Tool class name to instantiate.
81
+ description: Optional tool description (auto-derived from tool if not provided).
82
+ input_schema: Optional JSON schema (auto-derived from tool if not provided).
83
+
84
+ Returns:
85
+ PTCFileToolDef dict ready for use in PTCCustomToolConfig.tools.
86
+
87
+ Example:
88
+ >>> file_tool("multiply", file_path="/path/to/multiply_tool.py", class_name="MultiplyTool")
89
+ {'name': 'multiply', 'kind': 'file', 'file_path': '/path/to/multiply_tool.py', 'class_name': 'MultiplyTool'}
90
+ """
91
+ result: PTCFileToolDef = {
92
+ "name": name,
93
+ "kind": "file",
94
+ "file_path": file_path,
95
+ "class_name": class_name,
96
+ }
97
+ if description is not None:
98
+ result["description"] = description
99
+ if input_schema is not None:
100
+ result["input_schema"] = input_schema
101
+ return result