glaip-sdk 0.1.2__py3-none-any.whl → 0.7.17__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- glaip_sdk/__init__.py +44 -4
- glaip_sdk/_version.py +9 -0
- glaip_sdk/agents/__init__.py +27 -0
- glaip_sdk/agents/base.py +1413 -0
- glaip_sdk/branding.py +126 -2
- glaip_sdk/cli/account_store.py +555 -0
- glaip_sdk/cli/auth.py +260 -15
- glaip_sdk/cli/commands/__init__.py +2 -2
- glaip_sdk/cli/commands/accounts.py +746 -0
- glaip_sdk/cli/commands/agents/__init__.py +116 -0
- glaip_sdk/cli/commands/agents/_common.py +562 -0
- glaip_sdk/cli/commands/agents/create.py +155 -0
- glaip_sdk/cli/commands/agents/delete.py +64 -0
- glaip_sdk/cli/commands/agents/get.py +89 -0
- glaip_sdk/cli/commands/agents/list.py +129 -0
- glaip_sdk/cli/commands/agents/run.py +264 -0
- glaip_sdk/cli/commands/agents/sync_langflow.py +72 -0
- glaip_sdk/cli/commands/agents/update.py +112 -0
- glaip_sdk/cli/commands/common_config.py +104 -0
- glaip_sdk/cli/commands/configure.py +728 -113
- glaip_sdk/cli/commands/mcps/__init__.py +94 -0
- glaip_sdk/cli/commands/mcps/_common.py +459 -0
- glaip_sdk/cli/commands/mcps/connect.py +82 -0
- glaip_sdk/cli/commands/mcps/create.py +152 -0
- glaip_sdk/cli/commands/mcps/delete.py +73 -0
- glaip_sdk/cli/commands/mcps/get.py +212 -0
- glaip_sdk/cli/commands/mcps/list.py +69 -0
- glaip_sdk/cli/commands/mcps/tools.py +235 -0
- glaip_sdk/cli/commands/mcps/update.py +190 -0
- glaip_sdk/cli/commands/models.py +12 -8
- glaip_sdk/cli/commands/shared/__init__.py +21 -0
- glaip_sdk/cli/commands/shared/formatters.py +91 -0
- glaip_sdk/cli/commands/tools/__init__.py +69 -0
- glaip_sdk/cli/commands/tools/_common.py +80 -0
- glaip_sdk/cli/commands/tools/create.py +228 -0
- glaip_sdk/cli/commands/tools/delete.py +61 -0
- glaip_sdk/cli/commands/tools/get.py +103 -0
- glaip_sdk/cli/commands/tools/list.py +69 -0
- glaip_sdk/cli/commands/tools/script.py +49 -0
- glaip_sdk/cli/commands/tools/update.py +102 -0
- glaip_sdk/cli/commands/transcripts/__init__.py +90 -0
- glaip_sdk/cli/commands/transcripts/_common.py +9 -0
- glaip_sdk/cli/commands/transcripts/clear.py +5 -0
- glaip_sdk/cli/commands/transcripts/detail.py +5 -0
- glaip_sdk/cli/commands/transcripts_original.py +756 -0
- glaip_sdk/cli/commands/update.py +163 -17
- glaip_sdk/cli/config.py +49 -4
- glaip_sdk/cli/constants.py +38 -0
- glaip_sdk/cli/context.py +8 -0
- glaip_sdk/cli/core/__init__.py +79 -0
- glaip_sdk/cli/core/context.py +124 -0
- glaip_sdk/cli/core/output.py +851 -0
- glaip_sdk/cli/core/prompting.py +649 -0
- glaip_sdk/cli/core/rendering.py +187 -0
- glaip_sdk/cli/display.py +41 -20
- glaip_sdk/cli/entrypoint.py +20 -0
- glaip_sdk/cli/hints.py +57 -0
- glaip_sdk/cli/io.py +6 -3
- glaip_sdk/cli/main.py +340 -143
- glaip_sdk/cli/masking.py +21 -33
- glaip_sdk/cli/pager.py +12 -13
- glaip_sdk/cli/parsers/__init__.py +1 -3
- glaip_sdk/cli/resolution.py +2 -1
- glaip_sdk/cli/slash/__init__.py +0 -9
- glaip_sdk/cli/slash/accounts_controller.py +580 -0
- glaip_sdk/cli/slash/accounts_shared.py +75 -0
- glaip_sdk/cli/slash/agent_session.py +62 -21
- glaip_sdk/cli/slash/prompt.py +21 -0
- glaip_sdk/cli/slash/remote_runs_controller.py +568 -0
- glaip_sdk/cli/slash/session.py +1105 -153
- glaip_sdk/cli/slash/tui/__init__.py +36 -0
- glaip_sdk/cli/slash/tui/accounts.tcss +177 -0
- glaip_sdk/cli/slash/tui/accounts_app.py +1853 -0
- glaip_sdk/cli/slash/tui/background_tasks.py +72 -0
- glaip_sdk/cli/slash/tui/clipboard.py +195 -0
- glaip_sdk/cli/slash/tui/context.py +92 -0
- glaip_sdk/cli/slash/tui/indicators.py +341 -0
- glaip_sdk/cli/slash/tui/keybind_registry.py +235 -0
- glaip_sdk/cli/slash/tui/layouts/__init__.py +14 -0
- glaip_sdk/cli/slash/tui/layouts/harlequin.py +184 -0
- glaip_sdk/cli/slash/tui/loading.py +80 -0
- glaip_sdk/cli/slash/tui/remote_runs_app.py +760 -0
- glaip_sdk/cli/slash/tui/terminal.py +407 -0
- glaip_sdk/cli/slash/tui/theme/__init__.py +15 -0
- glaip_sdk/cli/slash/tui/theme/catalog.py +79 -0
- glaip_sdk/cli/slash/tui/theme/manager.py +112 -0
- glaip_sdk/cli/slash/tui/theme/tokens.py +55 -0
- glaip_sdk/cli/slash/tui/toast.py +388 -0
- glaip_sdk/cli/transcript/__init__.py +12 -52
- glaip_sdk/cli/transcript/cache.py +255 -44
- glaip_sdk/cli/transcript/capture.py +66 -1
- glaip_sdk/cli/transcript/history.py +815 -0
- glaip_sdk/cli/transcript/viewer.py +72 -463
- glaip_sdk/cli/tui_settings.py +125 -0
- glaip_sdk/cli/update_notifier.py +227 -10
- glaip_sdk/cli/validators.py +5 -6
- glaip_sdk/client/__init__.py +3 -1
- glaip_sdk/client/_schedule_payloads.py +89 -0
- glaip_sdk/client/agent_runs.py +147 -0
- glaip_sdk/client/agents.py +576 -44
- glaip_sdk/client/base.py +26 -0
- glaip_sdk/client/hitl.py +136 -0
- glaip_sdk/client/main.py +25 -14
- glaip_sdk/client/mcps.py +165 -24
- glaip_sdk/client/payloads/agent/__init__.py +23 -0
- glaip_sdk/client/{_agent_payloads.py → payloads/agent/requests.py} +63 -47
- glaip_sdk/client/payloads/agent/responses.py +43 -0
- glaip_sdk/client/run_rendering.py +546 -92
- glaip_sdk/client/schedules.py +439 -0
- glaip_sdk/client/shared.py +21 -0
- glaip_sdk/client/tools.py +206 -32
- glaip_sdk/config/constants.py +33 -2
- glaip_sdk/guardrails/__init__.py +80 -0
- glaip_sdk/guardrails/serializer.py +89 -0
- glaip_sdk/hitl/__init__.py +48 -0
- glaip_sdk/hitl/base.py +64 -0
- glaip_sdk/hitl/callback.py +43 -0
- glaip_sdk/hitl/local.py +121 -0
- glaip_sdk/hitl/remote.py +523 -0
- glaip_sdk/mcps/__init__.py +21 -0
- glaip_sdk/mcps/base.py +345 -0
- glaip_sdk/models/__init__.py +136 -0
- glaip_sdk/models/_provider_mappings.py +101 -0
- glaip_sdk/models/_validation.py +97 -0
- glaip_sdk/models/agent.py +48 -0
- glaip_sdk/models/agent_runs.py +117 -0
- glaip_sdk/models/common.py +42 -0
- glaip_sdk/models/constants.py +141 -0
- glaip_sdk/models/mcp.py +33 -0
- glaip_sdk/models/model.py +170 -0
- glaip_sdk/models/schedule.py +224 -0
- glaip_sdk/models/tool.py +33 -0
- glaip_sdk/payload_schemas/__init__.py +1 -13
- glaip_sdk/payload_schemas/agent.py +1 -0
- glaip_sdk/payload_schemas/guardrails.py +34 -0
- glaip_sdk/registry/__init__.py +55 -0
- glaip_sdk/registry/agent.py +164 -0
- glaip_sdk/registry/base.py +139 -0
- glaip_sdk/registry/mcp.py +253 -0
- glaip_sdk/registry/tool.py +445 -0
- glaip_sdk/rich_components.py +58 -2
- glaip_sdk/runner/__init__.py +76 -0
- glaip_sdk/runner/base.py +84 -0
- glaip_sdk/runner/deps.py +115 -0
- glaip_sdk/runner/langgraph.py +1055 -0
- glaip_sdk/runner/logging_config.py +77 -0
- glaip_sdk/runner/mcp_adapter/__init__.py +13 -0
- glaip_sdk/runner/mcp_adapter/base_mcp_adapter.py +43 -0
- glaip_sdk/runner/mcp_adapter/langchain_mcp_adapter.py +257 -0
- glaip_sdk/runner/mcp_adapter/mcp_config_builder.py +116 -0
- glaip_sdk/runner/tool_adapter/__init__.py +18 -0
- glaip_sdk/runner/tool_adapter/base_tool_adapter.py +44 -0
- glaip_sdk/runner/tool_adapter/langchain_tool_adapter.py +242 -0
- glaip_sdk/schedules/__init__.py +22 -0
- glaip_sdk/schedules/base.py +291 -0
- glaip_sdk/tools/__init__.py +22 -0
- glaip_sdk/tools/base.py +488 -0
- glaip_sdk/utils/__init__.py +59 -12
- glaip_sdk/utils/a2a/__init__.py +34 -0
- glaip_sdk/utils/a2a/event_processor.py +188 -0
- glaip_sdk/utils/agent_config.py +8 -2
- glaip_sdk/utils/bundler.py +403 -0
- glaip_sdk/utils/client.py +111 -0
- glaip_sdk/utils/client_utils.py +39 -7
- glaip_sdk/utils/datetime_helpers.py +58 -0
- glaip_sdk/utils/discovery.py +78 -0
- glaip_sdk/utils/display.py +23 -15
- glaip_sdk/utils/export.py +143 -0
- glaip_sdk/utils/general.py +0 -33
- glaip_sdk/utils/import_export.py +12 -7
- glaip_sdk/utils/import_resolver.py +524 -0
- glaip_sdk/utils/instructions.py +101 -0
- glaip_sdk/utils/rendering/__init__.py +115 -1
- glaip_sdk/utils/rendering/formatting.py +5 -30
- glaip_sdk/utils/rendering/layout/__init__.py +64 -0
- glaip_sdk/utils/rendering/{renderer → layout}/panels.py +9 -0
- glaip_sdk/utils/rendering/{renderer → layout}/progress.py +70 -1
- glaip_sdk/utils/rendering/layout/summary.py +74 -0
- glaip_sdk/utils/rendering/layout/transcript.py +606 -0
- glaip_sdk/utils/rendering/models.py +1 -0
- glaip_sdk/utils/rendering/renderer/__init__.py +9 -47
- glaip_sdk/utils/rendering/renderer/base.py +299 -1434
- glaip_sdk/utils/rendering/renderer/config.py +1 -5
- glaip_sdk/utils/rendering/renderer/debug.py +26 -20
- glaip_sdk/utils/rendering/renderer/factory.py +138 -0
- glaip_sdk/utils/rendering/renderer/stream.py +4 -33
- glaip_sdk/utils/rendering/renderer/summary_window.py +79 -0
- glaip_sdk/utils/rendering/renderer/thinking.py +273 -0
- glaip_sdk/utils/rendering/renderer/tool_panels.py +442 -0
- glaip_sdk/utils/rendering/renderer/transcript_mode.py +162 -0
- glaip_sdk/utils/rendering/state.py +204 -0
- glaip_sdk/utils/rendering/steps/__init__.py +34 -0
- glaip_sdk/utils/rendering/{steps.py → steps/event_processor.py} +53 -440
- glaip_sdk/utils/rendering/steps/format.py +176 -0
- glaip_sdk/utils/rendering/steps/manager.py +387 -0
- glaip_sdk/utils/rendering/timing.py +36 -0
- glaip_sdk/utils/rendering/viewer/__init__.py +21 -0
- glaip_sdk/utils/rendering/viewer/presenter.py +184 -0
- glaip_sdk/utils/resource_refs.py +25 -13
- glaip_sdk/utils/runtime_config.py +426 -0
- glaip_sdk/utils/serialization.py +18 -0
- glaip_sdk/utils/sync.py +162 -0
- glaip_sdk/utils/tool_detection.py +301 -0
- glaip_sdk/utils/tool_storage_provider.py +140 -0
- glaip_sdk/utils/validation.py +16 -24
- {glaip_sdk-0.1.2.dist-info → glaip_sdk-0.7.17.dist-info}/METADATA +69 -23
- glaip_sdk-0.7.17.dist-info/RECORD +224 -0
- {glaip_sdk-0.1.2.dist-info → glaip_sdk-0.7.17.dist-info}/WHEEL +2 -1
- glaip_sdk-0.7.17.dist-info/entry_points.txt +2 -0
- glaip_sdk-0.7.17.dist-info/top_level.txt +1 -0
- glaip_sdk/cli/commands/agents.py +0 -1369
- glaip_sdk/cli/commands/mcps.py +0 -1187
- glaip_sdk/cli/commands/tools.py +0 -584
- glaip_sdk/cli/utils.py +0 -1278
- glaip_sdk/models.py +0 -240
- glaip_sdk-0.1.2.dist-info/RECORD +0 -82
- glaip_sdk-0.1.2.dist-info/entry_points.txt +0 -3
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
"""LangChain tool adapter for local agent runtime.
|
|
2
|
+
|
|
3
|
+
This module handles adaptation of glaip-sdk tool references to LangChain
|
|
4
|
+
BaseTool instances for local execution with aip-agents (LangGraph backend).
|
|
5
|
+
|
|
6
|
+
Authors:
|
|
7
|
+
Christian Trisno Sen Long Chen (christian.t.s.l.chen@gdplabs.id)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from gllm_core.utils import LoggerManager
|
|
13
|
+
|
|
14
|
+
from glaip_sdk.runner.tool_adapter.base_tool_adapter import BaseToolAdapter
|
|
15
|
+
|
|
16
|
+
logger = LoggerManager().get_logger(__name__)
|
|
17
|
+
|
|
18
|
+
# Constant for unknown tool name placeholder
|
|
19
|
+
_UNKNOWN_TOOL_NAME = "<unknown>"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class LangChainToolAdapter(BaseToolAdapter):
|
|
23
|
+
"""Adapts glaip-sdk tools to LangChain BaseTool format for aip-agents.
|
|
24
|
+
|
|
25
|
+
Handles:
|
|
26
|
+
- LangChain BaseTool classes → instantiate
|
|
27
|
+
- LangChain BaseTool instances → return as-is
|
|
28
|
+
- Tool.from_langchain() → extract underlying tool
|
|
29
|
+
- @tool_plugin decorator → ignore (just metadata)
|
|
30
|
+
|
|
31
|
+
Rejects:
|
|
32
|
+
- Tool.from_native() → platform-specific
|
|
33
|
+
- String tool names → platform-specific
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def adapt_tools(self, tool_refs: list[Any]) -> list[Any]:
|
|
37
|
+
"""Adapt tool references to LangChain BaseTool instances.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
tool_refs: List of tool references from Agent definition.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
List of LangChain BaseTool instances.
|
|
44
|
+
|
|
45
|
+
Raises:
|
|
46
|
+
ValueError: If tool is not supported in local mode.
|
|
47
|
+
"""
|
|
48
|
+
langchain_tools = []
|
|
49
|
+
|
|
50
|
+
for tool_ref in tool_refs:
|
|
51
|
+
langchain_tool = self._adapt_single_tool(tool_ref)
|
|
52
|
+
langchain_tools.append(langchain_tool)
|
|
53
|
+
|
|
54
|
+
logger.debug("Adapted %d tools to LangChain format", len(langchain_tools))
|
|
55
|
+
return langchain_tools
|
|
56
|
+
|
|
57
|
+
def _adapt_single_tool(self, tool_ref: Any) -> Any:
|
|
58
|
+
"""Adapt a single tool reference.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
tool_ref: Single tool reference to adapt.
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
LangChain BaseTool instance.
|
|
65
|
+
|
|
66
|
+
Raises:
|
|
67
|
+
ValueError: If tool is not supported.
|
|
68
|
+
"""
|
|
69
|
+
# 1. Tool.from_langchain() wrapper
|
|
70
|
+
if self._is_tool_wrapper(tool_ref):
|
|
71
|
+
return self._extract_from_wrapper(tool_ref)
|
|
72
|
+
|
|
73
|
+
# 2. Direct LangChain BaseTool
|
|
74
|
+
if self._is_langchain_tool(tool_ref):
|
|
75
|
+
return self._instantiate_langchain_tool(tool_ref)
|
|
76
|
+
|
|
77
|
+
# 3. Native tools with discovered class
|
|
78
|
+
if self._is_platform_tool(tool_ref):
|
|
79
|
+
# Try to discover local implementation for native tool
|
|
80
|
+
from glaip_sdk.utils.tool_detection import ( # noqa: PLC0415
|
|
81
|
+
find_aip_agents_tool_class,
|
|
82
|
+
get_tool_name,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# Get tool name from reference
|
|
86
|
+
tool_name = get_tool_name(tool_ref) if not isinstance(tool_ref, str) else tool_ref
|
|
87
|
+
|
|
88
|
+
if tool_name:
|
|
89
|
+
discovered_class = find_aip_agents_tool_class(tool_name)
|
|
90
|
+
if discovered_class:
|
|
91
|
+
logger.info("Instantiating native tool locally: %s", tool_name)
|
|
92
|
+
try:
|
|
93
|
+
return discovered_class()
|
|
94
|
+
except TypeError as exc:
|
|
95
|
+
raise ValueError(
|
|
96
|
+
f"Could not instantiate native tool '{tool_name}'. "
|
|
97
|
+
"Ensure it has a zero-argument constructor or adjust the instantiation logic."
|
|
98
|
+
) from exc
|
|
99
|
+
|
|
100
|
+
# If no local class found, raise platform tool error
|
|
101
|
+
raise ValueError(self._get_platform_tool_error(tool_ref))
|
|
102
|
+
|
|
103
|
+
# 4. Unknown type
|
|
104
|
+
raise ValueError(
|
|
105
|
+
f"Unsupported tool type for local mode: {type(tool_ref)}. "
|
|
106
|
+
"Local mode only supports LangChain BaseTool classes/instances."
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
def _has_explicit_attr(self, ref: Any, attr: str) -> bool:
|
|
110
|
+
"""Check if attribute is explicitly set on the object.
|
|
111
|
+
|
|
112
|
+
This avoids false positives from objects like MagicMock, where hasattr()
|
|
113
|
+
can return True even if the attribute was never set.
|
|
114
|
+
"""
|
|
115
|
+
ref_dict = getattr(ref, "__dict__", None)
|
|
116
|
+
return isinstance(ref_dict, dict) and attr in ref_dict
|
|
117
|
+
|
|
118
|
+
def _is_tool_wrapper(self, ref: Any) -> bool:
|
|
119
|
+
"""Check if ref is a Tool.from_langchain() wrapper.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
ref: Object to check.
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
True if ref is a Tool.from_langchain() wrapper.
|
|
126
|
+
"""
|
|
127
|
+
if self._has_explicit_attr(ref, "langchain_tool") and hasattr(ref, "id") and hasattr(ref, "name"):
|
|
128
|
+
return True
|
|
129
|
+
|
|
130
|
+
if self._has_explicit_attr(ref, "tool_class"):
|
|
131
|
+
return getattr(ref, "tool_class", None) is not None
|
|
132
|
+
|
|
133
|
+
return False
|
|
134
|
+
|
|
135
|
+
def _extract_from_wrapper(self, wrapper: Any) -> Any:
|
|
136
|
+
"""Extract underlying LangChain tool from Tool.from_langchain().
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
wrapper: Tool.from_langchain() wrapper object.
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
LangChain BaseTool instance.
|
|
143
|
+
|
|
144
|
+
Raises:
|
|
145
|
+
ValueError: If the wrapper's underlying tool is not a valid LangChain tool.
|
|
146
|
+
"""
|
|
147
|
+
langchain_tool = getattr(wrapper, "langchain_tool", None)
|
|
148
|
+
if langchain_tool is None:
|
|
149
|
+
langchain_tool = getattr(wrapper, "tool_class", None)
|
|
150
|
+
|
|
151
|
+
# Validate the extracted object is a valid LangChain tool
|
|
152
|
+
if langchain_tool is None:
|
|
153
|
+
wrapper_name = getattr(wrapper, "name", _UNKNOWN_TOOL_NAME)
|
|
154
|
+
raise ValueError(
|
|
155
|
+
f"Tool wrapper '{wrapper_name}' does not contain a valid LangChain tool. "
|
|
156
|
+
"Ensure Tool.from_langchain() was called with a LangChain BaseTool class or instance."
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
# Validate it's actually a LangChain tool (class or instance)
|
|
160
|
+
if not self._is_langchain_tool(langchain_tool):
|
|
161
|
+
wrapper_name = getattr(wrapper, "name", _UNKNOWN_TOOL_NAME)
|
|
162
|
+
raise ValueError(
|
|
163
|
+
f"Tool wrapper '{wrapper_name}' contains an invalid tool type: {type(langchain_tool)}. "
|
|
164
|
+
"Expected a LangChain BaseTool class or instance."
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
# If it's a class, instantiate it
|
|
168
|
+
if isinstance(langchain_tool, type):
|
|
169
|
+
langchain_tool = langchain_tool()
|
|
170
|
+
|
|
171
|
+
logger.debug(
|
|
172
|
+
"Extracted LangChain tool from wrapper: %s",
|
|
173
|
+
getattr(langchain_tool, "name", _UNKNOWN_TOOL_NAME),
|
|
174
|
+
)
|
|
175
|
+
return langchain_tool
|
|
176
|
+
|
|
177
|
+
def _is_langchain_tool(self, ref: Any) -> bool:
|
|
178
|
+
"""Check if ref is a LangChain BaseTool class or instance.
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
ref: Object to check.
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
True if ref is a LangChain BaseTool.
|
|
185
|
+
"""
|
|
186
|
+
from glaip_sdk.utils.tool_detection import is_langchain_tool # noqa: PLC0415
|
|
187
|
+
|
|
188
|
+
return is_langchain_tool(ref)
|
|
189
|
+
|
|
190
|
+
def _instantiate_langchain_tool(self, ref: Any) -> Any:
|
|
191
|
+
"""Instantiate LangChain tool if class, return as-is if instance.
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
ref: LangChain BaseTool class or instance.
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
LangChain BaseTool instance.
|
|
198
|
+
"""
|
|
199
|
+
if isinstance(ref, type):
|
|
200
|
+
# It's a class, instantiate it
|
|
201
|
+
# Note: @tool_plugin decorator doesn't affect instantiation
|
|
202
|
+
return ref()
|
|
203
|
+
return ref
|
|
204
|
+
|
|
205
|
+
def _is_platform_tool(self, ref: Any) -> bool:
|
|
206
|
+
"""Check if ref is platform-specific (not supported locally).
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
ref: Object to check.
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
True if ref is a platform-specific tool.
|
|
213
|
+
"""
|
|
214
|
+
# String tool names
|
|
215
|
+
if isinstance(ref, str):
|
|
216
|
+
return True
|
|
217
|
+
|
|
218
|
+
# Tool.from_native() instances
|
|
219
|
+
if hasattr(ref, "id") and hasattr(ref, "name") and not self._has_explicit_attr(ref, "langchain_tool"):
|
|
220
|
+
tool_class = getattr(ref, "tool_class", None) if self._has_explicit_attr(ref, "tool_class") else None
|
|
221
|
+
if tool_class is None:
|
|
222
|
+
return True
|
|
223
|
+
|
|
224
|
+
return False
|
|
225
|
+
|
|
226
|
+
def _get_platform_tool_error(self, ref: Any) -> str:
|
|
227
|
+
"""Get error message for platform tools.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
ref: Platform tool reference.
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
Error message string.
|
|
234
|
+
"""
|
|
235
|
+
from glaip_sdk.runner.deps import ( # noqa: PLC0415
|
|
236
|
+
get_local_mode_not_supported_for_tool_message,
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
tool_name = ref if isinstance(ref, str) else getattr(ref, "name", None)
|
|
240
|
+
if tool_name is None:
|
|
241
|
+
tool_name = getattr(getattr(ref, "tool_class", None), "__name__", _UNKNOWN_TOOL_NAME)
|
|
242
|
+
return get_local_mode_not_supported_for_tool_message(tool_name)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Schedules runtime package.
|
|
2
|
+
|
|
3
|
+
This package contains runtime schedule resource objects (class-based) that
|
|
4
|
+
encapsulate behavior and API interactions via attached clients.
|
|
5
|
+
|
|
6
|
+
Authors:
|
|
7
|
+
Christian Trisno Sen Long Chen (christian.t.s.l.chen@gdplabs.id)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from glaip_sdk.schedules.base import (
|
|
11
|
+
Schedule,
|
|
12
|
+
ScheduleListResult,
|
|
13
|
+
ScheduleRun,
|
|
14
|
+
ScheduleRunListResult,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"Schedule",
|
|
19
|
+
"ScheduleListResult",
|
|
20
|
+
"ScheduleRun",
|
|
21
|
+
"ScheduleRunListResult",
|
|
22
|
+
]
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
"""Schedule runtime resources.
|
|
2
|
+
|
|
3
|
+
This module contains class-based runtime resources for schedules.
|
|
4
|
+
|
|
5
|
+
The runtime resources:
|
|
6
|
+
- Are not Pydantic models.
|
|
7
|
+
- Are returned from public client APIs.
|
|
8
|
+
- Delegate API operations to a bound ScheduleClient.
|
|
9
|
+
|
|
10
|
+
Authors:
|
|
11
|
+
Christian Trisno Sen Long Chen (christian.t.s.l.chen@gdplabs.id)
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from dataclasses import dataclass, field
|
|
17
|
+
from datetime import datetime
|
|
18
|
+
from typing import TYPE_CHECKING
|
|
19
|
+
|
|
20
|
+
from glaip_sdk.models.agent_runs import RunStatus
|
|
21
|
+
from glaip_sdk.models.schedule import (
|
|
22
|
+
ScheduleConfig,
|
|
23
|
+
ScheduleMetadata,
|
|
24
|
+
ScheduleResponse,
|
|
25
|
+
ScheduleRunResponse,
|
|
26
|
+
ScheduleRunResult,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
if TYPE_CHECKING: # pragma: no cover
|
|
30
|
+
from glaip_sdk.client.schedules import ScheduleClient
|
|
31
|
+
|
|
32
|
+
_SCHEDULE_CLIENT_REQUIRED_MSG = "No client available. Use client.schedules.get() to get a client-connected schedule."
|
|
33
|
+
_SCHEDULE_RUN_CLIENT_REQUIRED_MSG = (
|
|
34
|
+
"No client available. Use client.schedules.list_runs() to get a client-connected schedule run."
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class Schedule:
|
|
39
|
+
"""Runtime schedule resource.
|
|
40
|
+
|
|
41
|
+
Attributes:
|
|
42
|
+
id (str): The schedule ID.
|
|
43
|
+
next_run_time (str | None): Next run time as returned by the API.
|
|
44
|
+
time_until_next_run (str | None): Human readable duration until next run.
|
|
45
|
+
metadata (ScheduleMetadata | None): Schedule metadata.
|
|
46
|
+
created_at (datetime | None): Creation timestamp.
|
|
47
|
+
updated_at (datetime | None): Update timestamp.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
def __init__(
|
|
51
|
+
self,
|
|
52
|
+
*,
|
|
53
|
+
id: str,
|
|
54
|
+
next_run_time: str | None = None,
|
|
55
|
+
time_until_next_run: str | None = None,
|
|
56
|
+
metadata: ScheduleMetadata | None = None,
|
|
57
|
+
created_at: datetime | None = None,
|
|
58
|
+
updated_at: datetime | None = None,
|
|
59
|
+
_client: ScheduleClient | None = None,
|
|
60
|
+
) -> None:
|
|
61
|
+
"""Initialize a runtime Schedule."""
|
|
62
|
+
self.id = id
|
|
63
|
+
self.next_run_time = next_run_time
|
|
64
|
+
self.time_until_next_run = time_until_next_run
|
|
65
|
+
self.metadata = metadata
|
|
66
|
+
self.created_at = created_at
|
|
67
|
+
self.updated_at = updated_at
|
|
68
|
+
self._client = _client
|
|
69
|
+
|
|
70
|
+
@classmethod
|
|
71
|
+
def from_response(cls, response: ScheduleResponse, *, client: ScheduleClient) -> Schedule:
|
|
72
|
+
"""Build a runtime Schedule from a DTO response.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
response: Parsed schedule response DTO.
|
|
76
|
+
client: ScheduleClient to bind.
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
Runtime Schedule.
|
|
80
|
+
"""
|
|
81
|
+
return cls(
|
|
82
|
+
id=response.id,
|
|
83
|
+
next_run_time=response.next_run_time,
|
|
84
|
+
time_until_next_run=response.time_until_next_run,
|
|
85
|
+
metadata=response.metadata,
|
|
86
|
+
created_at=response.created_at,
|
|
87
|
+
updated_at=response.updated_at,
|
|
88
|
+
_client=client,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def agent_id(self) -> str | None:
|
|
93
|
+
"""Agent ID derived from metadata."""
|
|
94
|
+
return self.metadata.agent_id if self.metadata else None
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def input(self) -> str | None:
|
|
98
|
+
"""Input text derived from metadata."""
|
|
99
|
+
return self.metadata.input if self.metadata else None
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def schedule_config(self) -> ScheduleConfig | None:
|
|
103
|
+
"""Schedule configuration derived from metadata."""
|
|
104
|
+
return self.metadata.schedule if self.metadata else None
|
|
105
|
+
|
|
106
|
+
def update(
|
|
107
|
+
self,
|
|
108
|
+
*,
|
|
109
|
+
input: str | None = None,
|
|
110
|
+
schedule: ScheduleConfig | dict[str, str] | str | None = None,
|
|
111
|
+
) -> Schedule:
|
|
112
|
+
"""Update this schedule."""
|
|
113
|
+
if self._client is None:
|
|
114
|
+
raise RuntimeError(_SCHEDULE_CLIENT_REQUIRED_MSG)
|
|
115
|
+
return self._client.update(self.id, input=input, schedule=schedule)
|
|
116
|
+
|
|
117
|
+
def delete(self) -> None:
|
|
118
|
+
"""Delete this schedule."""
|
|
119
|
+
if self._client is None:
|
|
120
|
+
raise RuntimeError(_SCHEDULE_CLIENT_REQUIRED_MSG)
|
|
121
|
+
self._client.delete(self.id)
|
|
122
|
+
|
|
123
|
+
def list_runs(
|
|
124
|
+
self,
|
|
125
|
+
*,
|
|
126
|
+
status: RunStatus | None = None,
|
|
127
|
+
limit: int | None = None,
|
|
128
|
+
page: int | None = None,
|
|
129
|
+
) -> ScheduleRunListResult:
|
|
130
|
+
"""List runs for this schedule."""
|
|
131
|
+
if self._client is None:
|
|
132
|
+
raise RuntimeError(_SCHEDULE_CLIENT_REQUIRED_MSG)
|
|
133
|
+
if self.agent_id is None:
|
|
134
|
+
raise ValueError("Schedule has no agent_id")
|
|
135
|
+
return self._client.list_runs(
|
|
136
|
+
self.agent_id,
|
|
137
|
+
schedule_id=self.id,
|
|
138
|
+
status=status,
|
|
139
|
+
limit=limit,
|
|
140
|
+
page=page,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
def __repr__(self) -> str:
|
|
144
|
+
"""Return a developer-friendly representation."""
|
|
145
|
+
parts: list[str] = [f"id={self.id!r}"]
|
|
146
|
+
if self.agent_id is not None:
|
|
147
|
+
parts.append(f"agent_id={self.agent_id!r}")
|
|
148
|
+
if self.next_run_time is not None:
|
|
149
|
+
parts.append(f"next_run_time={self.next_run_time!r}")
|
|
150
|
+
if self.time_until_next_run is not None:
|
|
151
|
+
parts.append(f"time_until_next_run={self.time_until_next_run!r}")
|
|
152
|
+
if self.created_at is not None:
|
|
153
|
+
parts.append(f"created_at={self.created_at!r}")
|
|
154
|
+
return f"Schedule({', '.join(parts)})"
|
|
155
|
+
|
|
156
|
+
def __str__(self) -> str:
|
|
157
|
+
"""Return a readable string representation."""
|
|
158
|
+
return self.__repr__()
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
class ScheduleRun:
|
|
162
|
+
"""Runtime schedule run resource."""
|
|
163
|
+
|
|
164
|
+
def __init__(
|
|
165
|
+
self,
|
|
166
|
+
*,
|
|
167
|
+
id: str,
|
|
168
|
+
agent_id: str,
|
|
169
|
+
schedule_id: str | None = None,
|
|
170
|
+
status: RunStatus,
|
|
171
|
+
run_type: str | None = None,
|
|
172
|
+
started_at: datetime | None = None,
|
|
173
|
+
completed_at: datetime | None = None,
|
|
174
|
+
input: str | None = None,
|
|
175
|
+
config: ScheduleConfig | dict[str, str] | None = None,
|
|
176
|
+
created_at: datetime | None = None,
|
|
177
|
+
updated_at: datetime | None = None,
|
|
178
|
+
_client: ScheduleClient | None = None,
|
|
179
|
+
) -> None:
|
|
180
|
+
"""Initialize a runtime ScheduleRun."""
|
|
181
|
+
self.id = id
|
|
182
|
+
self.agent_id = agent_id
|
|
183
|
+
self.schedule_id = schedule_id
|
|
184
|
+
self.status = status
|
|
185
|
+
self.run_type = run_type
|
|
186
|
+
self.started_at = started_at
|
|
187
|
+
self.completed_at = completed_at
|
|
188
|
+
self.input = input
|
|
189
|
+
self.config = config
|
|
190
|
+
self.created_at = created_at
|
|
191
|
+
self.updated_at = updated_at
|
|
192
|
+
self._client = _client
|
|
193
|
+
|
|
194
|
+
@classmethod
|
|
195
|
+
def from_response(cls, response: ScheduleRunResponse, *, client: ScheduleClient) -> ScheduleRun:
|
|
196
|
+
"""Build a runtime ScheduleRun from a DTO response."""
|
|
197
|
+
return cls(
|
|
198
|
+
id=response.id,
|
|
199
|
+
agent_id=response.agent_id,
|
|
200
|
+
schedule_id=response.schedule_id,
|
|
201
|
+
status=response.status,
|
|
202
|
+
run_type=response.run_type,
|
|
203
|
+
started_at=response.started_at,
|
|
204
|
+
completed_at=response.completed_at,
|
|
205
|
+
input=response.input,
|
|
206
|
+
config=response.config,
|
|
207
|
+
created_at=response.created_at,
|
|
208
|
+
updated_at=response.updated_at,
|
|
209
|
+
_client=client,
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
def get_result(self) -> ScheduleRunResult:
|
|
213
|
+
"""Retrieve the full output payload for this run."""
|
|
214
|
+
if self._client is None:
|
|
215
|
+
raise RuntimeError(_SCHEDULE_RUN_CLIENT_REQUIRED_MSG)
|
|
216
|
+
if self.agent_id is None:
|
|
217
|
+
raise ValueError("Schedule run has no agent_id")
|
|
218
|
+
return self._client.get_run_result(self.agent_id, self.id)
|
|
219
|
+
|
|
220
|
+
@property
|
|
221
|
+
def duration(self) -> str | None:
|
|
222
|
+
"""Formatted duration (HH:MM:SS) when both timestamps are available."""
|
|
223
|
+
if not self.started_at or not self.completed_at:
|
|
224
|
+
return None
|
|
225
|
+
|
|
226
|
+
total_seconds = int((self.completed_at - self.started_at).total_seconds())
|
|
227
|
+
minutes, seconds = divmod(total_seconds, 60)
|
|
228
|
+
hours, minutes = divmod(minutes, 60)
|
|
229
|
+
return f"{hours:02d}:{minutes:02d}:{seconds:02d}"
|
|
230
|
+
|
|
231
|
+
def __repr__(self) -> str:
|
|
232
|
+
"""Return a developer-friendly representation."""
|
|
233
|
+
parts: list[str] = [f"id={self.id!r}", f"status={self.status!r}"]
|
|
234
|
+
if self.started_at is not None:
|
|
235
|
+
parts.append(f"started_at={self.started_at.isoformat()!r}")
|
|
236
|
+
duration = self.duration
|
|
237
|
+
if duration is not None:
|
|
238
|
+
parts.append(f"duration={duration!r}")
|
|
239
|
+
return f"ScheduleRun({', '.join(parts)})"
|
|
240
|
+
|
|
241
|
+
def __str__(self) -> str:
|
|
242
|
+
"""Return a readable string representation."""
|
|
243
|
+
return self.__repr__()
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
@dataclass
|
|
247
|
+
class ScheduleListResult:
|
|
248
|
+
"""Paginated list wrapper for runtime schedules."""
|
|
249
|
+
|
|
250
|
+
items: list[Schedule]
|
|
251
|
+
total: int | None = field(default=None)
|
|
252
|
+
page: int | None = field(default=None)
|
|
253
|
+
limit: int | None = field(default=None)
|
|
254
|
+
has_next: bool | None = field(default=None)
|
|
255
|
+
has_prev: bool | None = field(default=None)
|
|
256
|
+
|
|
257
|
+
def __iter__(self):
|
|
258
|
+
"""Iterate over schedules."""
|
|
259
|
+
yield from self.items
|
|
260
|
+
|
|
261
|
+
def __len__(self) -> int:
|
|
262
|
+
"""Return the number of schedules in this page."""
|
|
263
|
+
return self.items.__len__()
|
|
264
|
+
|
|
265
|
+
def __getitem__(self, index: int) -> Schedule:
|
|
266
|
+
"""Return the schedule at the given index."""
|
|
267
|
+
return self.items[index]
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
@dataclass
|
|
271
|
+
class ScheduleRunListResult:
|
|
272
|
+
"""Paginated list wrapper for runtime schedule runs."""
|
|
273
|
+
|
|
274
|
+
items: list[ScheduleRun]
|
|
275
|
+
total: int | None = field(default=None)
|
|
276
|
+
page: int | None = field(default=None)
|
|
277
|
+
limit: int | None = field(default=None)
|
|
278
|
+
has_next: bool | None = field(default=None)
|
|
279
|
+
has_prev: bool | None = field(default=None)
|
|
280
|
+
|
|
281
|
+
def __iter__(self):
|
|
282
|
+
"""Iterate over schedule runs."""
|
|
283
|
+
yield from self.items
|
|
284
|
+
|
|
285
|
+
def __len__(self) -> int:
|
|
286
|
+
"""Return the number of runs in this page."""
|
|
287
|
+
return self.items.__len__()
|
|
288
|
+
|
|
289
|
+
def __getitem__(self, index: int) -> ScheduleRun:
|
|
290
|
+
"""Return the run at the given index."""
|
|
291
|
+
return self.items[index]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Tool package for GL AIP platform.
|
|
2
|
+
|
|
3
|
+
This package provides the Tool class, ToolType enum, and ToolRegistry
|
|
4
|
+
for managing tools on the GL AIP platform.
|
|
5
|
+
|
|
6
|
+
Example:
|
|
7
|
+
>>> from glaip_sdk.tools import Tool, ToolType, get_tool_registry
|
|
8
|
+
>>> native_tool = Tool.from_native("web_search")
|
|
9
|
+
>>> custom_tool = Tool.from_langchain(MyCustomTool)
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from glaip_sdk.registry.tool import ToolRegistry, get_tool_registry
|
|
15
|
+
from glaip_sdk.tools.base import Tool, ToolType
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"Tool",
|
|
19
|
+
"ToolType",
|
|
20
|
+
"ToolRegistry",
|
|
21
|
+
"get_tool_registry",
|
|
22
|
+
]
|