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.
- aip_agents/agent/__init__.py +44 -4
- aip_agents/agent/base_langgraph_agent.py +163 -74
- aip_agents/agent/base_langgraph_agent.pyi +3 -2
- aip_agents/agent/langgraph_memory_enhancer_agent.py +368 -34
- aip_agents/agent/langgraph_memory_enhancer_agent.pyi +3 -2
- aip_agents/agent/langgraph_react_agent.py +329 -22
- aip_agents/agent/langgraph_react_agent.pyi +41 -2
- aip_agents/examples/hello_world_ptc.py +49 -0
- aip_agents/examples/hello_world_ptc.pyi +5 -0
- aip_agents/examples/hello_world_ptc_custom_tools.py +83 -0
- aip_agents/examples/hello_world_ptc_custom_tools.pyi +7 -0
- aip_agents/examples/hello_world_tool_output_client.py +9 -0
- aip_agents/examples/tools/multiply_tool.py +43 -0
- aip_agents/examples/tools/multiply_tool.pyi +18 -0
- aip_agents/guardrails/engines/base.py +6 -6
- aip_agents/mcp/client/__init__.py +38 -2
- aip_agents/mcp/client/connection_manager.py +36 -1
- aip_agents/mcp/client/connection_manager.pyi +3 -0
- aip_agents/mcp/client/persistent_session.py +318 -68
- aip_agents/mcp/client/persistent_session.pyi +9 -0
- aip_agents/mcp/client/transports.py +37 -2
- aip_agents/mcp/client/transports.pyi +9 -0
- aip_agents/memory/adapters/base_adapter.py +98 -0
- aip_agents/memory/adapters/base_adapter.pyi +25 -0
- aip_agents/ptc/__init__.py +87 -0
- aip_agents/ptc/__init__.pyi +14 -0
- aip_agents/ptc/custom_tools.py +473 -0
- aip_agents/ptc/custom_tools.pyi +184 -0
- aip_agents/ptc/custom_tools_payload.py +400 -0
- aip_agents/ptc/custom_tools_payload.pyi +31 -0
- aip_agents/ptc/custom_tools_templates/__init__.py +1 -0
- aip_agents/ptc/custom_tools_templates/__init__.pyi +0 -0
- aip_agents/ptc/custom_tools_templates/custom_build_function.py.template +23 -0
- aip_agents/ptc/custom_tools_templates/custom_init.py.template +15 -0
- aip_agents/ptc/custom_tools_templates/custom_invoke.py.template +60 -0
- aip_agents/ptc/custom_tools_templates/custom_registry.py.template +87 -0
- aip_agents/ptc/custom_tools_templates/custom_sources_init.py.template +7 -0
- aip_agents/ptc/custom_tools_templates/custom_wrapper.py.template +19 -0
- aip_agents/ptc/doc_gen.py +122 -0
- aip_agents/ptc/doc_gen.pyi +40 -0
- aip_agents/ptc/exceptions.py +57 -0
- aip_agents/ptc/exceptions.pyi +37 -0
- aip_agents/ptc/executor.py +261 -0
- aip_agents/ptc/executor.pyi +99 -0
- aip_agents/ptc/mcp/__init__.py +45 -0
- aip_agents/ptc/mcp/__init__.pyi +7 -0
- aip_agents/ptc/mcp/sandbox_bridge.py +668 -0
- aip_agents/ptc/mcp/sandbox_bridge.pyi +47 -0
- aip_agents/ptc/mcp/templates/__init__.py +1 -0
- aip_agents/ptc/mcp/templates/__init__.pyi +0 -0
- aip_agents/ptc/mcp/templates/mcp_client.py.template +239 -0
- aip_agents/ptc/naming.py +196 -0
- aip_agents/ptc/naming.pyi +85 -0
- aip_agents/ptc/payload.py +26 -0
- aip_agents/ptc/payload.pyi +15 -0
- aip_agents/ptc/prompt_builder.py +673 -0
- aip_agents/ptc/prompt_builder.pyi +59 -0
- aip_agents/ptc/ptc_helper.py +16 -0
- aip_agents/ptc/ptc_helper.pyi +1 -0
- aip_agents/ptc/sandbox_bridge.py +256 -0
- aip_agents/ptc/sandbox_bridge.pyi +38 -0
- aip_agents/ptc/template_utils.py +33 -0
- aip_agents/ptc/template_utils.pyi +13 -0
- aip_agents/ptc/templates/__init__.py +1 -0
- aip_agents/ptc/templates/__init__.pyi +0 -0
- aip_agents/ptc/templates/ptc_helper.py.template +134 -0
- aip_agents/ptc/tool_def_helpers.py +101 -0
- aip_agents/ptc/tool_def_helpers.pyi +38 -0
- aip_agents/ptc/tool_enrichment.py +163 -0
- aip_agents/ptc/tool_enrichment.pyi +60 -0
- aip_agents/sandbox/__init__.py +43 -0
- aip_agents/sandbox/__init__.pyi +5 -0
- aip_agents/sandbox/defaults.py +205 -0
- aip_agents/sandbox/defaults.pyi +30 -0
- aip_agents/sandbox/e2b_runtime.py +295 -0
- aip_agents/sandbox/e2b_runtime.pyi +57 -0
- aip_agents/sandbox/template_builder.py +131 -0
- aip_agents/sandbox/template_builder.pyi +36 -0
- aip_agents/sandbox/types.py +24 -0
- aip_agents/sandbox/types.pyi +14 -0
- aip_agents/sandbox/validation.py +50 -0
- aip_agents/sandbox/validation.pyi +20 -0
- aip_agents/sentry/sentry.py +29 -8
- aip_agents/sentry/sentry.pyi +3 -2
- aip_agents/tools/__init__.py +13 -2
- aip_agents/tools/__init__.pyi +3 -1
- aip_agents/tools/browser_use/browser_use_tool.py +8 -0
- aip_agents/tools/browser_use/streaming.py +2 -0
- aip_agents/tools/date_range_tool.py +554 -0
- aip_agents/tools/date_range_tool.pyi +21 -0
- aip_agents/tools/execute_ptc_code.py +357 -0
- aip_agents/tools/execute_ptc_code.pyi +90 -0
- aip_agents/tools/memory_search/__init__.py +8 -1
- aip_agents/tools/memory_search/__init__.pyi +3 -3
- aip_agents/tools/memory_search/mem0.py +114 -1
- aip_agents/tools/memory_search/mem0.pyi +11 -1
- aip_agents/tools/memory_search/schema.py +33 -0
- aip_agents/tools/memory_search/schema.pyi +10 -0
- aip_agents/tools/memory_search_tool.py +8 -0
- aip_agents/tools/memory_search_tool.pyi +2 -2
- aip_agents/utils/langgraph/tool_managers/delegation_tool_manager.py +26 -1
- aip_agents/utils/langgraph/tool_output_management.py +80 -0
- aip_agents/utils/langgraph/tool_output_management.pyi +37 -0
- {aip_agents_binary-0.5.25.dist-info → aip_agents_binary-0.6.8.dist-info}/METADATA +9 -19
- {aip_agents_binary-0.5.25.dist-info → aip_agents_binary-0.6.8.dist-info}/RECORD +107 -41
- {aip_agents_binary-0.5.25.dist-info → aip_agents_binary-0.6.8.dist-info}/WHEEL +1 -1
- aip_agents/examples/demo_memory_recall.py +0 -401
- aip_agents/examples/demo_memory_recall.pyi +0 -58
- {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
|