aip-agents-binary 0.6.0__py3-none-macosx_13_0_arm64.whl → 0.6.1__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.

Potentially problematic release.


This version of aip-agents-binary might be problematic. Click here for more details.

Files changed (54) hide show
  1. aip_agents/agent/langgraph_react_agent.py +194 -2
  2. aip_agents/agent/langgraph_react_agent.pyi +40 -1
  3. aip_agents/examples/hello_world_ptc.py +51 -0
  4. aip_agents/examples/hello_world_ptc.pyi +5 -0
  5. aip_agents/ptc/__init__.py +48 -0
  6. aip_agents/ptc/__init__.pyi +10 -0
  7. aip_agents/ptc/doc_gen.py +122 -0
  8. aip_agents/ptc/doc_gen.pyi +40 -0
  9. aip_agents/ptc/exceptions.py +39 -0
  10. aip_agents/ptc/exceptions.pyi +22 -0
  11. aip_agents/ptc/executor.py +143 -0
  12. aip_agents/ptc/executor.pyi +73 -0
  13. aip_agents/ptc/mcp/__init__.py +45 -0
  14. aip_agents/ptc/mcp/__init__.pyi +7 -0
  15. aip_agents/ptc/mcp/sandbox_bridge.py +668 -0
  16. aip_agents/ptc/mcp/sandbox_bridge.pyi +47 -0
  17. aip_agents/ptc/mcp/templates/__init__.py +1 -0
  18. aip_agents/ptc/mcp/templates/__init__.pyi +0 -0
  19. aip_agents/ptc/mcp/templates/mcp_client.py.template +239 -0
  20. aip_agents/ptc/naming.py +184 -0
  21. aip_agents/ptc/naming.pyi +76 -0
  22. aip_agents/ptc/payload.py +26 -0
  23. aip_agents/ptc/payload.pyi +15 -0
  24. aip_agents/ptc/prompt_builder.py +571 -0
  25. aip_agents/ptc/prompt_builder.pyi +55 -0
  26. aip_agents/ptc/ptc_helper.py +16 -0
  27. aip_agents/ptc/ptc_helper.pyi +1 -0
  28. aip_agents/ptc/sandbox_bridge.py +58 -0
  29. aip_agents/ptc/sandbox_bridge.pyi +25 -0
  30. aip_agents/ptc/template_utils.py +33 -0
  31. aip_agents/ptc/template_utils.pyi +13 -0
  32. aip_agents/ptc/templates/__init__.py +1 -0
  33. aip_agents/ptc/templates/__init__.pyi +0 -0
  34. aip_agents/ptc/templates/ptc_helper.py.template +134 -0
  35. aip_agents/sandbox/__init__.py +43 -0
  36. aip_agents/sandbox/__init__.pyi +5 -0
  37. aip_agents/sandbox/defaults.py +9 -0
  38. aip_agents/sandbox/defaults.pyi +2 -0
  39. aip_agents/sandbox/e2b_runtime.py +267 -0
  40. aip_agents/sandbox/e2b_runtime.pyi +51 -0
  41. aip_agents/sandbox/template_builder.py +131 -0
  42. aip_agents/sandbox/template_builder.pyi +36 -0
  43. aip_agents/sandbox/types.py +24 -0
  44. aip_agents/sandbox/types.pyi +14 -0
  45. aip_agents/sandbox/validation.py +50 -0
  46. aip_agents/sandbox/validation.pyi +20 -0
  47. aip_agents/tools/__init__.py +2 -0
  48. aip_agents/tools/__init__.pyi +2 -1
  49. aip_agents/tools/execute_ptc_code.py +305 -0
  50. aip_agents/tools/execute_ptc_code.pyi +87 -0
  51. {aip_agents_binary-0.6.0.dist-info → aip_agents_binary-0.6.1.dist-info}/METADATA +1 -1
  52. {aip_agents_binary-0.6.0.dist-info → aip_agents_binary-0.6.1.dist-info}/RECORD +54 -8
  53. {aip_agents_binary-0.6.0.dist-info → aip_agents_binary-0.6.1.dist-info}/WHEEL +0 -0
  54. {aip_agents_binary-0.6.0.dist-info → aip_agents_binary-0.6.1.dist-info}/top_level.txt +0 -0
@@ -87,6 +87,9 @@ from aip_agents.utils.token_usage_helper import (
87
87
  extract_token_usage_from_tool_output,
88
88
  )
89
89
 
90
+ if TYPE_CHECKING:
91
+ from aip_agents.ptc import PTCSandboxConfig
92
+
90
93
  logger = get_logger(__name__)
91
94
 
92
95
  # Default instruction for ReAct agents
@@ -165,6 +168,7 @@ class LangGraphReactAgent(LangGraphHitLMixin, BaseLangGraphAgent):
165
168
  middlewares: Sequence[AgentMiddleware] | None = None,
166
169
  guardrail: GuardrailManager | None = None,
167
170
  step_limit_config: StepLimitConfig | None = None,
171
+ ptc_config: PTCSandboxConfig | None = None,
168
172
  **kwargs: Any,
169
173
  ):
170
174
  """Initialize the LangGraph ReAct Agent.
@@ -193,6 +197,11 @@ class LangGraphReactAgent(LangGraphHitLMixin, BaseLangGraphAgent):
193
197
  input/output filtering during agent execution.
194
198
  enable_pii: Optional toggle to enable PII handling for tool inputs and outputs.
195
199
  step_limit_config: Optional configuration for step limits and delegation depth.
200
+ ptc_config: Optional configuration for PTC sandbox execution. See PTCSandboxConfig
201
+ for available options including enabled flag, sandbox timeout, and template settings.
202
+ PTC is enabled when ptc_config is not None and ptc_config.enabled is True.
203
+ When enabled, prompt guidance is automatically injected into the agent's instruction.
204
+ PTC runs in a sandbox only; there is no in-process trusted PTC path.
196
205
  **kwargs: Additional keyword arguments passed to BaseLangGraphAgent.
197
206
  """
198
207
  # Use LangGraph's standard AgentState for ReAct
@@ -239,6 +248,18 @@ class LangGraphReactAgent(LangGraphHitLMixin, BaseLangGraphAgent):
239
248
 
240
249
  self.step_limit_config = step_limit_config
241
250
 
251
+ # Initialize PTC state (Programmatic Tool Calling)
252
+ self._ptc_config: PTCSandboxConfig | None = None
253
+ self._ptc_tool_synced = False
254
+ self._ptc_tool: BaseTool | None = None
255
+ self._ptc_prompt_hash: str = ""
256
+ # Capture instruction after middleware setup so middleware prompts are preserved
257
+ self._original_instruction: str = self.instruction
258
+
259
+ # Enable PTC if requested via constructor
260
+ if ptc_config is not None and ptc_config.enabled:
261
+ self.enable_ptc(ptc_config)
262
+
242
263
  def _setup_middleware(
243
264
  self,
244
265
  planning: bool,
@@ -425,9 +446,9 @@ class LangGraphReactAgent(LangGraphHitLMixin, BaseLangGraphAgent):
425
446
  )
426
447
 
427
448
  def _rebuild_resolved_tools(self) -> None:
428
- """Rebuild resolved tools including middleware tools.
449
+ """Rebuild resolved tools including middleware and PTC tools.
429
450
 
430
- Overrides base class to ensure middleware tools are preserved
451
+ Overrides base class to ensure middleware tools and the PTC tool are preserved
431
452
  when tools are rebuilt (e.g., after update_regular_tools).
432
453
  """
433
454
  # Call base class to rebuild with regular, a2a, delegation, and mcp tools
@@ -437,6 +458,10 @@ class LangGraphReactAgent(LangGraphHitLMixin, BaseLangGraphAgent):
437
458
  if hasattr(self, "_middleware_tools") and self._middleware_tools:
438
459
  self.resolved_tools.extend(self._middleware_tools)
439
460
 
461
+ # Add PTC tool if synced
462
+ if hasattr(self, "_ptc_tool") and self._ptc_tool is not None:
463
+ self.resolved_tools.append(self._ptc_tool)
464
+
440
465
  def _handle_tool_artifacts(
441
466
  self, tool_output: Any, pending_artifacts: list[dict[str, Any]]
442
467
  ) -> tuple[str, list[dict[str, Any]]]:
@@ -2620,6 +2645,173 @@ class LangGraphReactAgent(LangGraphHitLMixin, BaseLangGraphAgent):
2620
2645
  if current_thread_id:
2621
2646
  self._pii_handlers_by_thread.pop(current_thread_id, None)
2622
2647
 
2648
+ # ==========================================================================
2649
+ # Programmatic Tool Calling (PTC) Methods
2650
+ # ==========================================================================
2651
+
2652
+ def add_mcp_server(self, mcp_config: dict[str, dict[str, Any]]) -> None:
2653
+ """Add MCP servers and refresh PTC tool state if needed."""
2654
+ super().add_mcp_server(mcp_config)
2655
+
2656
+ if not self._ptc_config or not self._ptc_config.enabled:
2657
+ return
2658
+
2659
+ if self._ptc_tool is not None:
2660
+ self._ptc_tool = None
2661
+
2662
+ self._ptc_tool_synced = False
2663
+ logger.debug(f"Agent '{self.name}': PTC tool will resync after MCP changes")
2664
+
2665
+ def enable_ptc(self, config: PTCSandboxConfig | None = None) -> None:
2666
+ """Enable Programmatic Tool Calling (PTC) for this agent.
2667
+
2668
+ PTC allows the LLM to execute Python code that calls MCP tools
2669
+ programmatically inside a sandboxed environment. This is useful for
2670
+ chaining multiple tool calls with local data processing.
2671
+
2672
+ The execute_ptc_code tool is automatically added to the agent's tools
2673
+ after MCP servers are configured. If no MCP servers are configured,
2674
+ the tool sync is deferred until servers are added.
2675
+
2676
+ Args:
2677
+ config: Optional configuration for PTC sandbox execution.
2678
+ See PTCSandboxConfig for options like enabled flag and sandbox_timeout.
2679
+ If None is passed, a default config with enabled=True will be created.
2680
+
2681
+ Example:
2682
+ agent.enable_ptc(PTCSandboxConfig(enabled=True))
2683
+ agent.add_mcp_server({"yfinance": {...}})
2684
+ # execute_ptc_code tool is now available
2685
+
2686
+ Note:
2687
+ PTC can also be enabled via the constructor by passing
2688
+ ptc_config=PTCSandboxConfig(enabled=True, ...).
2689
+ """
2690
+ # Lazy import to avoid circular dependencies
2691
+ from aip_agents.ptc.executor import PTCSandboxConfig
2692
+
2693
+ self._ptc_config = config or PTCSandboxConfig()
2694
+ self._ptc_config.enabled = True
2695
+ self._ptc_tool_synced = False
2696
+
2697
+ logger.info(f"Agent '{self.name}': PTC enabled")
2698
+
2699
+ # Attempt to sync PTC tool if MCP client is available
2700
+ self._sync_ptc_tool()
2701
+
2702
+ def _sync_ptc_tool(self) -> None:
2703
+ """Build and register the execute_ptc_code tool when MCP servers are available.
2704
+
2705
+ This method is called after enable_ptc() and after MCP servers are added.
2706
+ It creates the execute_ptc_code tool using the current MCP client
2707
+ configuration and adds it to the agent's resolved tools.
2708
+
2709
+ The tool is only created once. Subsequent calls are no-ops if the tool
2710
+ has already been synced.
2711
+ """
2712
+ if not self._ptc_config or not self._ptc_config.enabled:
2713
+ return
2714
+
2715
+ if self._ptc_tool_synced:
2716
+ return
2717
+
2718
+ # Check if we have MCP servers configured
2719
+ if not self.mcp_config:
2720
+ logger.debug(f"Agent '{self.name}': PTC tool sync deferred - no MCP servers configured")
2721
+ return
2722
+
2723
+ if not self.mcp_client:
2724
+ logger.debug(f"Agent '{self.name}': PTC tool sync deferred - no MCP client yet")
2725
+ return
2726
+
2727
+ if not self.mcp_client.is_initialized:
2728
+ logger.debug(f"Agent '{self.name}': PTC tool sync deferred - MCP client not initialized")
2729
+ return
2730
+
2731
+ # Lazy import to avoid circular dependencies
2732
+ from aip_agents.tools.execute_ptc_code import create_execute_ptc_code_tool
2733
+
2734
+ logger.info(f"Agent '{self.name}': Syncing PTC tool with MCP client")
2735
+
2736
+ # Create the execute_ptc_code tool with agent's tool configs
2737
+ self._ptc_tool = create_execute_ptc_code_tool(
2738
+ self.mcp_client, self._ptc_config, agent_tool_configs=self.tool_configs
2739
+ )
2740
+
2741
+ # Rebuild graph to include PTC tool
2742
+ self._rebuild_graph()
2743
+
2744
+ self._ptc_tool_synced = True
2745
+ logger.info(f"Agent '{self.name}': PTC tool synced successfully")
2746
+
2747
+ # Sync PTC prompt guidance
2748
+ self._sync_ptc_prompt()
2749
+
2750
+ def _sync_ptc_prompt(self) -> None:
2751
+ """Sync PTC usage guidance into the agent instruction.
2752
+
2753
+ This method builds and injects a PTC usage block into the agent's
2754
+ instruction when PTC is enabled. The prompt is refreshed when MCP
2755
+ configuration changes (detected via hash).
2756
+ """
2757
+ if not self._ptc_config or not self._ptc_config.enabled:
2758
+ return
2759
+
2760
+ if not self.mcp_client:
2761
+ return
2762
+
2763
+ # Lazy import to avoid circular dependencies
2764
+ from aip_agents.ptc.prompt_builder import build_ptc_prompt, compute_ptc_prompt_hash
2765
+
2766
+ # Get prompt config from PTC sandbox config
2767
+ prompt_config = self._ptc_config.prompt if self._ptc_config else None
2768
+
2769
+ # Check if MCP config has changed
2770
+ current_hash = compute_ptc_prompt_hash(self.mcp_client, config=prompt_config)
2771
+ if current_hash == self._ptc_prompt_hash:
2772
+ logger.debug(f"Agent '{self.name}': PTC prompt unchanged, skipping refresh")
2773
+ return
2774
+
2775
+ # Build and inject the prompt
2776
+ ptc_prompt = build_ptc_prompt(self.mcp_client, config=prompt_config)
2777
+
2778
+ # Rebuild instruction from original + PTC guidance
2779
+ self.instruction = f"{self._original_instruction}\n\n{ptc_prompt}"
2780
+ self._ptc_prompt_hash = current_hash
2781
+
2782
+ logger.info(f"Agent '{self.name}': PTC prompt guidance injected")
2783
+
2784
+ async def _register_mcp_tools(self) -> None:
2785
+ """Override to sync PTC tool after MCP tools are registered.
2786
+
2787
+ This extends the base implementation to ensure the execute_ptc_code
2788
+ tool is added after MCP servers are initialized.
2789
+ """
2790
+ await super()._register_mcp_tools()
2791
+
2792
+ # Sync PTC tool after MCP tools are registered
2793
+ if self._ptc_config and self._ptc_config.enabled and not self._ptc_tool_synced:
2794
+ self._sync_ptc_tool()
2795
+
2796
+ async def cleanup(self) -> None:
2797
+ """Cleanup agent resources including PTC sandbox.
2798
+
2799
+ Extends base cleanup to also cleanup the PTC sandbox runtime if
2800
+ execute_ptc_code tool was created.
2801
+ """
2802
+ # Cleanup PTC tool's sandbox runtime if present
2803
+ if self._ptc_tool is not None:
2804
+ try:
2805
+ cleanup_method = getattr(self._ptc_tool, "cleanup", None)
2806
+ if cleanup_method and callable(cleanup_method):
2807
+ await cleanup_method()
2808
+ logger.debug(f"Agent '{self.name}': PTC sandbox cleanup completed")
2809
+ except Exception as e:
2810
+ logger.warning(f"Agent '{self.name}': Error during PTC sandbox cleanup: {e}")
2811
+
2812
+ # Call parent cleanup for MCP client
2813
+ await super().cleanup()
2814
+
2623
2815
  def _format_graph_output(self, final_state_result: dict[str, Any]) -> Any:
2624
2816
  """Convert final graph state to user-friendly output.
2625
2817
 
@@ -6,6 +6,7 @@ from aip_agents.guardrails.manager import GuardrailManager as GuardrailManager
6
6
  from aip_agents.middleware.base import AgentMiddleware as AgentMiddleware, ModelRequest as ModelRequest
7
7
  from aip_agents.middleware.manager import MiddlewareManager as MiddlewareManager
8
8
  from aip_agents.middleware.todolist import TodoList as TodoList, TodoListMiddleware as TodoListMiddleware
9
+ from aip_agents.ptc import PTCSandboxConfig as PTCSandboxConfig
9
10
  from aip_agents.schema.a2a import A2AStreamEventType as A2AStreamEventType
10
11
  from aip_agents.schema.hitl import ApprovalDecision as ApprovalDecision, HitlMetadata as HitlMetadata
11
12
  from aip_agents.schema.langgraph import ToolCallResult as ToolCallResult, ToolStorageParams as ToolStorageParams
@@ -86,7 +87,7 @@ class LangGraphReactAgent(LangGraphHitLMixin, BaseLangGraphAgent):
86
87
  """
87
88
  tool_output_manager: Incomplete
88
89
  step_limit_config: Incomplete
89
- def __init__(self, name: str, instruction: str = ..., model: BaseChatModel | str | Any | None = None, tools: Sequence[BaseTool] | None = None, agents: Sequence[Any] | None = None, description: str | None = None, thread_id_key: str = 'thread_id', event_emitter: EventEmitter | None = None, tool_output_manager: ToolOutputManager | None = None, planning: bool = False, middlewares: Sequence[AgentMiddleware] | None = None, guardrail: GuardrailManager | None = None, step_limit_config: StepLimitConfig | None = None, **kwargs: Any) -> None:
90
+ def __init__(self, name: str, instruction: str = ..., model: BaseChatModel | str | Any | None = None, tools: Sequence[BaseTool] | None = None, agents: Sequence[Any] | None = None, description: str | None = None, thread_id_key: str = 'thread_id', event_emitter: EventEmitter | None = None, tool_output_manager: ToolOutputManager | None = None, planning: bool = False, middlewares: Sequence[AgentMiddleware] | None = None, guardrail: GuardrailManager | None = None, step_limit_config: StepLimitConfig | None = None, ptc_config: PTCSandboxConfig | None = None, **kwargs: Any) -> None:
90
91
  """Initialize the LangGraph ReAct Agent.
91
92
 
92
93
  Args:
@@ -113,6 +114,11 @@ class LangGraphReactAgent(LangGraphHitLMixin, BaseLangGraphAgent):
113
114
  input/output filtering during agent execution.
114
115
  enable_pii: Optional toggle to enable PII handling for tool inputs and outputs.
115
116
  step_limit_config: Optional configuration for step limits and delegation depth.
117
+ ptc_config: Optional configuration for PTC sandbox execution. See PTCSandboxConfig
118
+ for available options including enabled flag, sandbox timeout, and template settings.
119
+ PTC is enabled when ptc_config is not None and ptc_config.enabled is True.
120
+ When enabled, prompt guidance is automatically injected into the agent's instruction.
121
+ PTC runs in a sandbox only; there is no in-process trusted PTC path.
116
122
  **kwargs: Additional keyword arguments passed to BaseLangGraphAgent.
117
123
  """
118
124
  def define_graph(self, graph_builder: StateGraph) -> CompiledStateGraph:
@@ -124,6 +130,39 @@ class LangGraphReactAgent(LangGraphHitLMixin, BaseLangGraphAgent):
124
130
  Returns:
125
131
  Compiled LangGraph ready for execution.
126
132
  """
133
+ def add_mcp_server(self, mcp_config: dict[str, dict[str, Any]]) -> None:
134
+ """Add MCP servers and refresh PTC tool state if needed."""
135
+ def enable_ptc(self, config: PTCSandboxConfig | None = None) -> None:
136
+ '''Enable Programmatic Tool Calling (PTC) for this agent.
137
+
138
+ PTC allows the LLM to execute Python code that calls MCP tools
139
+ programmatically inside a sandboxed environment. This is useful for
140
+ chaining multiple tool calls with local data processing.
141
+
142
+ The execute_ptc_code tool is automatically added to the agent\'s tools
143
+ after MCP servers are configured. If no MCP servers are configured,
144
+ the tool sync is deferred until servers are added.
145
+
146
+ Args:
147
+ config: Optional configuration for PTC sandbox execution.
148
+ See PTCSandboxConfig for options like enabled flag and sandbox_timeout.
149
+ If None is passed, a default config with enabled=True will be created.
150
+
151
+ Example:
152
+ agent.enable_ptc(PTCSandboxConfig(enabled=True))
153
+ agent.add_mcp_server({"yfinance": {...}})
154
+ # execute_ptc_code tool is now available
155
+
156
+ Note:
157
+ PTC can also be enabled via the constructor by passing
158
+ ptc_config=PTCSandboxConfig(enabled=True, ...).
159
+ '''
160
+ async def cleanup(self) -> None:
161
+ """Cleanup agent resources including PTC sandbox.
162
+
163
+ Extends base cleanup to also cleanup the PTC sandbox runtime if
164
+ execute_ptc_code tool was created.
165
+ """
127
166
 
128
167
  class LangGraphAgent(LangGraphReactAgent):
129
168
  """Alias for LangGraphReactAgent."""
@@ -0,0 +1,51 @@
1
+ """Minimal PTC hello world example.
2
+
3
+ Required environment variables:
4
+ - OPENAI_API_KEY
5
+ - E2B_API_KEY
6
+ """
7
+
8
+ import asyncio
9
+
10
+ from langchain_openai import ChatOpenAI
11
+
12
+ from aip_agents.agent import LangGraphReactAgent
13
+ from aip_agents.ptc import PromptConfig, PTCSandboxConfig
14
+
15
+
16
+ async def main() -> None:
17
+ """Run a hello-world PTC flow."""
18
+ instruction = (
19
+ "You are a helpful assistant with access to execute_ptc_code. "
20
+ "Use execute_ptc_code to run Python and print output. "
21
+ "The tool returns JSON with ok/stdout/stderr/exit_code."
22
+ )
23
+
24
+ agent = LangGraphReactAgent(
25
+ name="ptc_hello_world",
26
+ instruction=instruction,
27
+ model=ChatOpenAI(model="gpt-5.2"),
28
+ ptc_config=PTCSandboxConfig(enabled=True, sandbox_timeout=180.0, prompt=PromptConfig(mode="index")),
29
+ )
30
+ agent.add_mcp_server(
31
+ {
32
+ "deepwiki": {
33
+ "transport": "streamable-http",
34
+ "url": "https://mcp.deepwiki.com/mcp",
35
+ "headers": {},
36
+ "timeout": 60.0,
37
+ }
38
+ }
39
+ )
40
+
41
+ try:
42
+ response = await agent.arun(
43
+ query="Use execute_ptc_code to print 'Hello, world!' and count the number of words in the output of deepwiki.read_wiki_structure('anthropics/claude-code')."
44
+ )
45
+ print("execute_ptc_code output:", response["output"])
46
+ finally:
47
+ await agent.cleanup()
48
+
49
+
50
+ if __name__ == "__main__":
51
+ asyncio.run(main())
@@ -0,0 +1,5 @@
1
+ from aip_agents.agent import LangGraphReactAgent as LangGraphReactAgent
2
+ from aip_agents.ptc import PTCSandboxConfig as PTCSandboxConfig, PromptConfig as PromptConfig
3
+
4
+ async def main() -> None:
5
+ """Run a hello-world PTC flow."""
@@ -0,0 +1,48 @@
1
+ """PTC (Programmatic Tool Calling) core module (MCP-only).
2
+
3
+ This module provides the core PTC functionality for MCP tools, including
4
+ executor, prompt builder, and sandbox bridge.
5
+
6
+ Authors:
7
+ Putu Ravindra Wiguna (putu.r.wiguna@gdplabs.id)
8
+ """
9
+
10
+ from aip_agents.ptc.exceptions import PTCError, PTCToolError
11
+ from aip_agents.ptc.prompt_builder import PromptConfig, build_ptc_prompt, compute_ptc_prompt_hash
12
+
13
+ __all__ = [
14
+ # Exceptions
15
+ "PTCError",
16
+ "PTCToolError",
17
+ # Executor
18
+ "PTCSandboxConfig",
19
+ "PTCSandboxExecutor",
20
+ # Prompt builder
21
+ "PromptConfig",
22
+ "build_ptc_prompt",
23
+ "compute_ptc_prompt_hash",
24
+ # Sandbox bridge
25
+ "build_sandbox_payload",
26
+ "wrap_ptc_code",
27
+ ]
28
+
29
+
30
+ def __getattr__(name: str):
31
+ """Lazy import to avoid circular dependencies."""
32
+ if name == "PTCSandboxConfig":
33
+ from aip_agents.ptc.executor import PTCSandboxConfig
34
+
35
+ return PTCSandboxConfig
36
+ elif name == "PTCSandboxExecutor":
37
+ from aip_agents.ptc.executor import PTCSandboxExecutor
38
+
39
+ return PTCSandboxExecutor
40
+ elif name == "build_sandbox_payload":
41
+ from aip_agents.ptc.sandbox_bridge import build_sandbox_payload
42
+
43
+ return build_sandbox_payload
44
+ elif name == "wrap_ptc_code":
45
+ from aip_agents.ptc.sandbox_bridge import wrap_ptc_code
46
+
47
+ return wrap_ptc_code
48
+ raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
@@ -0,0 +1,10 @@
1
+ from aip_agents.ptc.exceptions import PTCError as PTCError, PTCToolError as PTCToolError
2
+ from aip_agents.ptc.prompt_builder import PromptConfig as PromptConfig, build_ptc_prompt as build_ptc_prompt, compute_ptc_prompt_hash as compute_ptc_prompt_hash
3
+
4
+ __all__ = ['PTCError', 'PTCToolError', 'PTCSandboxConfig', 'PTCSandboxExecutor', 'PromptConfig', 'build_ptc_prompt', 'compute_ptc_prompt_hash', 'build_sandbox_payload', 'wrap_ptc_code']
5
+
6
+ # Names in __all__ with no definition:
7
+ # PTCSandboxConfig
8
+ # PTCSandboxExecutor
9
+ # build_sandbox_payload
10
+ # wrap_ptc_code
@@ -0,0 +1,122 @@
1
+ """Documentation generation utilities for PTC.
2
+
3
+ Shared constants and helpers for generating tool documentation in sandbox payloads.
4
+
5
+ Authors:
6
+ Putu Ravindra Wiguna (putu.r.wiguna@gdplabs.id)
7
+ """
8
+
9
+ from typing import Any
10
+
11
+ from aip_agents.ptc.naming import sanitize_function_name
12
+
13
+ # Documentation limits (fixed constants per plan)
14
+ DOC_DESC_LIMIT = 120 # Tool description trim limit
15
+ DOC_PARAM_DESC_LIMIT = 80 # Parameter description trim limit
16
+
17
+
18
+ def json_type_to_display(json_type: Any) -> str:
19
+ """Convert JSON type to display string.
20
+
21
+ Args:
22
+ json_type: JSON schema type.
23
+
24
+ Returns:
25
+ Human-readable type string.
26
+ """
27
+ if isinstance(json_type, list):
28
+ return "any"
29
+ type_map = {
30
+ "string": "str",
31
+ "integer": "int",
32
+ "number": "float",
33
+ "boolean": "bool",
34
+ "array": "list",
35
+ "object": "dict",
36
+ "null": "None",
37
+ }
38
+ return type_map.get(str(json_type), "any")
39
+
40
+
41
+ def trim_text(text: str | None, limit: int) -> str:
42
+ """Trim text to limit with ellipsis if needed.
43
+
44
+ Args:
45
+ text: Text to trim.
46
+ limit: Maximum length before trimming.
47
+
48
+ Returns:
49
+ Trimmed text.
50
+ """
51
+ if not text:
52
+ return ""
53
+ if len(text) > limit:
54
+ return text[:limit] + "..."
55
+ return text
56
+
57
+
58
+ def render_tool_doc(
59
+ func_name: str,
60
+ signature: str,
61
+ description: str,
62
+ schema: dict[str, Any],
63
+ is_stub: bool = False,
64
+ example_code: str | None = None,
65
+ example_heading: str = "## Example",
66
+ ) -> str:
67
+ """Render markdown documentation for a tool.
68
+
69
+ Args:
70
+ func_name: Sanitized function name.
71
+ signature: Full function signature.
72
+ description: Tool description.
73
+ schema: Input schema for parameters.
74
+ is_stub: Whether this is a stub documentation.
75
+ example_code: Optional example code block content (without ```python).
76
+ example_heading: Heading for the example section.
77
+
78
+ Returns:
79
+ Markdown documentation string.
80
+ """
81
+ if is_stub:
82
+ desc = "Details unavailable because tool definitions are not loaded yet."
83
+ else:
84
+ desc = trim_text(description, DOC_DESC_LIMIT) or "No description available."
85
+
86
+ lines = [
87
+ f"# {func_name}",
88
+ "",
89
+ f"**Description:** {desc}",
90
+ "",
91
+ f"**Signature:** `{signature}`",
92
+ "",
93
+ ]
94
+
95
+ # Add parameters section
96
+ properties = schema.get("properties", {})
97
+ required = set(schema.get("required", []))
98
+
99
+ if properties:
100
+ lines.append("## Parameters")
101
+ lines.append("")
102
+ for prop_name, prop_schema in sorted(properties.items()):
103
+ safe_param = sanitize_function_name(prop_name)
104
+ prop_type = json_type_to_display(prop_schema.get("type", "any"))
105
+ is_required = "required" if prop_name in required else "optional"
106
+
107
+ # Trim param description
108
+ raw_param_desc = prop_schema.get("description", "")
109
+ param_desc = trim_text(raw_param_desc, DOC_PARAM_DESC_LIMIT)
110
+
111
+ lines.append(f"- **{safe_param}** ({prop_type}, {is_required}): {param_desc}")
112
+ lines.append("")
113
+
114
+ # Add example section
115
+ if example_code:
116
+ lines.append(example_heading)
117
+ lines.append("")
118
+ lines.append("```python")
119
+ lines.append(example_code)
120
+ lines.append("```")
121
+
122
+ return "\n".join(lines)
@@ -0,0 +1,40 @@
1
+ from aip_agents.ptc.naming import sanitize_function_name as sanitize_function_name
2
+ from typing import Any
3
+
4
+ DOC_DESC_LIMIT: int
5
+ DOC_PARAM_DESC_LIMIT: int
6
+
7
+ def json_type_to_display(json_type: Any) -> str:
8
+ """Convert JSON type to display string.
9
+
10
+ Args:
11
+ json_type: JSON schema type.
12
+
13
+ Returns:
14
+ Human-readable type string.
15
+ """
16
+ def trim_text(text: str | None, limit: int) -> str:
17
+ """Trim text to limit with ellipsis if needed.
18
+
19
+ Args:
20
+ text: Text to trim.
21
+ limit: Maximum length before trimming.
22
+
23
+ Returns:
24
+ Trimmed text.
25
+ """
26
+ def render_tool_doc(func_name: str, signature: str, description: str, schema: dict[str, Any], is_stub: bool = False, example_code: str | None = None, example_heading: str = '## Example') -> str:
27
+ """Render markdown documentation for a tool.
28
+
29
+ Args:
30
+ func_name: Sanitized function name.
31
+ signature: Full function signature.
32
+ description: Tool description.
33
+ schema: Input schema for parameters.
34
+ is_stub: Whether this is a stub documentation.
35
+ example_code: Optional example code block content (without ```python).
36
+ example_heading: Heading for the example section.
37
+
38
+ Returns:
39
+ Markdown documentation string.
40
+ """
@@ -0,0 +1,39 @@
1
+ """PTC-specific exceptions.
2
+
3
+ This module defines exceptions for Programmatic Tool Calling operations.
4
+
5
+ Authors:
6
+ Putu Ravindra Wiguna (putu.r.wiguna@gdplabs.id)
7
+ """
8
+
9
+
10
+ class PTCError(Exception):
11
+ """Base exception for PTC errors."""
12
+
13
+ pass
14
+
15
+
16
+ class PTCToolError(PTCError):
17
+ """Error during tool execution.
18
+
19
+ Attributes:
20
+ server_name: The MCP server where the error occurred.
21
+ tool_name: The tool that failed.
22
+ """
23
+
24
+ def __init__(
25
+ self,
26
+ message: str,
27
+ server_name: str | None = None,
28
+ tool_name: str | None = None,
29
+ ) -> None:
30
+ """Initialize PTCToolError.
31
+
32
+ Args:
33
+ message: Error message.
34
+ server_name: The MCP server name (optional).
35
+ tool_name: The tool name (optional).
36
+ """
37
+ super().__init__(message)
38
+ self.server_name = server_name
39
+ self.tool_name = tool_name
@@ -0,0 +1,22 @@
1
+ from _typeshed import Incomplete
2
+
3
+ class PTCError(Exception):
4
+ """Base exception for PTC errors."""
5
+
6
+ class PTCToolError(PTCError):
7
+ """Error during tool execution.
8
+
9
+ Attributes:
10
+ server_name: The MCP server where the error occurred.
11
+ tool_name: The tool that failed.
12
+ """
13
+ server_name: Incomplete
14
+ tool_name: Incomplete
15
+ def __init__(self, message: str, server_name: str | None = None, tool_name: str | None = None) -> None:
16
+ """Initialize PTCToolError.
17
+
18
+ Args:
19
+ message: Error message.
20
+ server_name: The MCP server name (optional).
21
+ tool_name: The tool name (optional).
22
+ """