glaip-sdk 0.6.12__py3-none-any.whl → 0.6.15__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 +42 -5
- {glaip_sdk-0.6.12.dist-info → glaip_sdk-0.6.15.dist-info}/METADATA +32 -37
- glaip_sdk-0.6.15.dist-info/RECORD +12 -0
- {glaip_sdk-0.6.12.dist-info → glaip_sdk-0.6.15.dist-info}/WHEEL +2 -1
- glaip_sdk-0.6.15.dist-info/entry_points.txt +2 -0
- glaip_sdk-0.6.15.dist-info/top_level.txt +1 -0
- glaip_sdk/agents/__init__.py +0 -27
- glaip_sdk/agents/base.py +0 -1191
- glaip_sdk/cli/__init__.py +0 -9
- glaip_sdk/cli/account_store.py +0 -540
- glaip_sdk/cli/agent_config.py +0 -78
- glaip_sdk/cli/auth.py +0 -699
- glaip_sdk/cli/commands/__init__.py +0 -5
- glaip_sdk/cli/commands/accounts.py +0 -746
- glaip_sdk/cli/commands/agents.py +0 -1509
- glaip_sdk/cli/commands/common_config.py +0 -101
- glaip_sdk/cli/commands/configure.py +0 -896
- glaip_sdk/cli/commands/mcps.py +0 -1356
- glaip_sdk/cli/commands/models.py +0 -69
- glaip_sdk/cli/commands/tools.py +0 -576
- glaip_sdk/cli/commands/transcripts.py +0 -755
- glaip_sdk/cli/commands/update.py +0 -61
- glaip_sdk/cli/config.py +0 -95
- glaip_sdk/cli/constants.py +0 -38
- glaip_sdk/cli/context.py +0 -150
- glaip_sdk/cli/core/__init__.py +0 -79
- glaip_sdk/cli/core/context.py +0 -124
- glaip_sdk/cli/core/output.py +0 -846
- glaip_sdk/cli/core/prompting.py +0 -649
- glaip_sdk/cli/core/rendering.py +0 -187
- glaip_sdk/cli/display.py +0 -355
- glaip_sdk/cli/hints.py +0 -57
- glaip_sdk/cli/io.py +0 -112
- glaip_sdk/cli/main.py +0 -604
- glaip_sdk/cli/masking.py +0 -136
- glaip_sdk/cli/mcp_validators.py +0 -287
- glaip_sdk/cli/pager.py +0 -266
- glaip_sdk/cli/parsers/__init__.py +0 -7
- glaip_sdk/cli/parsers/json_input.py +0 -177
- glaip_sdk/cli/resolution.py +0 -67
- glaip_sdk/cli/rich_helpers.py +0 -27
- glaip_sdk/cli/slash/__init__.py +0 -15
- glaip_sdk/cli/slash/accounts_controller.py +0 -578
- glaip_sdk/cli/slash/accounts_shared.py +0 -75
- glaip_sdk/cli/slash/agent_session.py +0 -285
- glaip_sdk/cli/slash/prompt.py +0 -256
- glaip_sdk/cli/slash/remote_runs_controller.py +0 -566
- glaip_sdk/cli/slash/session.py +0 -1708
- glaip_sdk/cli/slash/tui/__init__.py +0 -9
- glaip_sdk/cli/slash/tui/accounts_app.py +0 -876
- glaip_sdk/cli/slash/tui/background_tasks.py +0 -72
- glaip_sdk/cli/slash/tui/loading.py +0 -58
- glaip_sdk/cli/slash/tui/remote_runs_app.py +0 -628
- glaip_sdk/cli/transcript/__init__.py +0 -31
- glaip_sdk/cli/transcript/cache.py +0 -536
- glaip_sdk/cli/transcript/capture.py +0 -329
- glaip_sdk/cli/transcript/export.py +0 -38
- glaip_sdk/cli/transcript/history.py +0 -815
- glaip_sdk/cli/transcript/launcher.py +0 -77
- glaip_sdk/cli/transcript/viewer.py +0 -374
- glaip_sdk/cli/update_notifier.py +0 -290
- glaip_sdk/cli/utils.py +0 -263
- glaip_sdk/cli/validators.py +0 -238
- glaip_sdk/client/__init__.py +0 -11
- glaip_sdk/client/_agent_payloads.py +0 -520
- glaip_sdk/client/agent_runs.py +0 -147
- glaip_sdk/client/agents.py +0 -1335
- glaip_sdk/client/base.py +0 -502
- glaip_sdk/client/main.py +0 -249
- glaip_sdk/client/mcps.py +0 -370
- glaip_sdk/client/run_rendering.py +0 -700
- glaip_sdk/client/shared.py +0 -21
- glaip_sdk/client/tools.py +0 -661
- glaip_sdk/client/validators.py +0 -198
- glaip_sdk/config/constants.py +0 -52
- glaip_sdk/mcps/__init__.py +0 -21
- glaip_sdk/mcps/base.py +0 -345
- glaip_sdk/models/__init__.py +0 -90
- glaip_sdk/models/agent.py +0 -47
- glaip_sdk/models/agent_runs.py +0 -116
- glaip_sdk/models/common.py +0 -42
- glaip_sdk/models/mcp.py +0 -33
- glaip_sdk/models/tool.py +0 -33
- glaip_sdk/payload_schemas/__init__.py +0 -7
- glaip_sdk/payload_schemas/agent.py +0 -85
- glaip_sdk/registry/__init__.py +0 -55
- glaip_sdk/registry/agent.py +0 -164
- glaip_sdk/registry/base.py +0 -139
- glaip_sdk/registry/mcp.py +0 -253
- glaip_sdk/registry/tool.py +0 -232
- glaip_sdk/runner/__init__.py +0 -59
- glaip_sdk/runner/base.py +0 -84
- glaip_sdk/runner/deps.py +0 -115
- glaip_sdk/runner/langgraph.py +0 -782
- glaip_sdk/runner/mcp_adapter/__init__.py +0 -13
- glaip_sdk/runner/mcp_adapter/base_mcp_adapter.py +0 -43
- glaip_sdk/runner/mcp_adapter/langchain_mcp_adapter.py +0 -257
- glaip_sdk/runner/mcp_adapter/mcp_config_builder.py +0 -95
- glaip_sdk/runner/tool_adapter/__init__.py +0 -18
- glaip_sdk/runner/tool_adapter/base_tool_adapter.py +0 -44
- glaip_sdk/runner/tool_adapter/langchain_tool_adapter.py +0 -219
- glaip_sdk/tools/__init__.py +0 -22
- glaip_sdk/tools/base.py +0 -435
- glaip_sdk/utils/__init__.py +0 -86
- glaip_sdk/utils/a2a/__init__.py +0 -34
- glaip_sdk/utils/a2a/event_processor.py +0 -188
- glaip_sdk/utils/agent_config.py +0 -194
- glaip_sdk/utils/bundler.py +0 -267
- glaip_sdk/utils/client.py +0 -111
- glaip_sdk/utils/client_utils.py +0 -486
- glaip_sdk/utils/datetime_helpers.py +0 -58
- glaip_sdk/utils/discovery.py +0 -78
- glaip_sdk/utils/display.py +0 -135
- glaip_sdk/utils/export.py +0 -143
- glaip_sdk/utils/general.py +0 -61
- glaip_sdk/utils/import_export.py +0 -168
- glaip_sdk/utils/import_resolver.py +0 -492
- glaip_sdk/utils/instructions.py +0 -101
- glaip_sdk/utils/rendering/__init__.py +0 -115
- glaip_sdk/utils/rendering/formatting.py +0 -264
- glaip_sdk/utils/rendering/layout/__init__.py +0 -64
- glaip_sdk/utils/rendering/layout/panels.py +0 -156
- glaip_sdk/utils/rendering/layout/progress.py +0 -202
- glaip_sdk/utils/rendering/layout/summary.py +0 -74
- glaip_sdk/utils/rendering/layout/transcript.py +0 -606
- glaip_sdk/utils/rendering/models.py +0 -85
- glaip_sdk/utils/rendering/renderer/__init__.py +0 -55
- glaip_sdk/utils/rendering/renderer/base.py +0 -1024
- glaip_sdk/utils/rendering/renderer/config.py +0 -27
- glaip_sdk/utils/rendering/renderer/console.py +0 -55
- glaip_sdk/utils/rendering/renderer/debug.py +0 -178
- glaip_sdk/utils/rendering/renderer/factory.py +0 -138
- glaip_sdk/utils/rendering/renderer/stream.py +0 -202
- glaip_sdk/utils/rendering/renderer/summary_window.py +0 -79
- glaip_sdk/utils/rendering/renderer/thinking.py +0 -273
- glaip_sdk/utils/rendering/renderer/toggle.py +0 -182
- glaip_sdk/utils/rendering/renderer/tool_panels.py +0 -442
- glaip_sdk/utils/rendering/renderer/transcript_mode.py +0 -162
- glaip_sdk/utils/rendering/state.py +0 -204
- glaip_sdk/utils/rendering/step_tree_state.py +0 -100
- glaip_sdk/utils/rendering/steps/__init__.py +0 -34
- glaip_sdk/utils/rendering/steps/event_processor.py +0 -778
- glaip_sdk/utils/rendering/steps/format.py +0 -176
- glaip_sdk/utils/rendering/steps/manager.py +0 -387
- glaip_sdk/utils/rendering/timing.py +0 -36
- glaip_sdk/utils/rendering/viewer/__init__.py +0 -21
- glaip_sdk/utils/rendering/viewer/presenter.py +0 -184
- glaip_sdk/utils/resource_refs.py +0 -195
- glaip_sdk/utils/run_renderer.py +0 -41
- glaip_sdk/utils/runtime_config.py +0 -425
- glaip_sdk/utils/serialization.py +0 -424
- glaip_sdk/utils/sync.py +0 -142
- glaip_sdk/utils/tool_detection.py +0 -33
- glaip_sdk/utils/validation.py +0 -264
- glaip_sdk-0.6.12.dist-info/RECORD +0 -159
- glaip_sdk-0.6.12.dist-info/entry_points.txt +0 -3
glaip_sdk/client/shared.py
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
"""Shared helpers for client configuration wiring.
|
|
2
|
-
|
|
3
|
-
Authors:
|
|
4
|
-
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
from typing import Any
|
|
10
|
-
|
|
11
|
-
from glaip_sdk.client.base import BaseClient
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def build_shared_config(client: BaseClient) -> dict[str, Any]:
|
|
15
|
-
"""Return the keyword arguments used to initialize sub-clients."""
|
|
16
|
-
return {
|
|
17
|
-
"parent_client": client,
|
|
18
|
-
"api_url": client.api_url,
|
|
19
|
-
"api_key": client.api_key,
|
|
20
|
-
"timeout": client._timeout,
|
|
21
|
-
}
|
glaip_sdk/client/tools.py
DELETED
|
@@ -1,661 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""Tool client for AIP SDK.
|
|
3
|
-
|
|
4
|
-
Authors:
|
|
5
|
-
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
6
|
-
Christian Trisno Sen Long Chen (christian.t.s.l.chen@gdplabs.id)
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
import logging
|
|
10
|
-
import os
|
|
11
|
-
import tempfile
|
|
12
|
-
from typing import Any
|
|
13
|
-
|
|
14
|
-
from glaip_sdk.client.base import BaseClient
|
|
15
|
-
from glaip_sdk.config.constants import (
|
|
16
|
-
DEFAULT_TOOL_FRAMEWORK,
|
|
17
|
-
DEFAULT_TOOL_TYPE,
|
|
18
|
-
DEFAULT_TOOL_VERSION,
|
|
19
|
-
)
|
|
20
|
-
from glaip_sdk.models import ToolResponse
|
|
21
|
-
from glaip_sdk.tools import Tool
|
|
22
|
-
from glaip_sdk.utils.client_utils import (
|
|
23
|
-
add_kwargs_to_payload,
|
|
24
|
-
create_model_instances,
|
|
25
|
-
find_by_name,
|
|
26
|
-
)
|
|
27
|
-
from glaip_sdk.utils.resource_refs import is_uuid
|
|
28
|
-
|
|
29
|
-
# API endpoints
|
|
30
|
-
TOOLS_ENDPOINT = "/tools/"
|
|
31
|
-
TOOLS_UPLOAD_ENDPOINT = "/tools/upload"
|
|
32
|
-
TOOLS_UPLOAD_BY_ID_ENDPOINT_FMT = "/tools/{tool_id}/upload"
|
|
33
|
-
|
|
34
|
-
# Set up module-level logger
|
|
35
|
-
logger = logging.getLogger("glaip_sdk.tools")
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
class ToolClient(BaseClient):
|
|
39
|
-
"""Client for tool operations."""
|
|
40
|
-
|
|
41
|
-
def __init__(self, *, parent_client: BaseClient | None = None, **kwargs):
|
|
42
|
-
"""Initialize the tool client.
|
|
43
|
-
|
|
44
|
-
Args:
|
|
45
|
-
parent_client: Parent client to adopt session/config from
|
|
46
|
-
**kwargs: Additional arguments for standalone initialization
|
|
47
|
-
"""
|
|
48
|
-
super().__init__(parent_client=parent_client, **kwargs)
|
|
49
|
-
|
|
50
|
-
def list_tools(self, tool_type: str | None = None) -> list[Tool]:
|
|
51
|
-
"""List all tools, optionally filtered by type.
|
|
52
|
-
|
|
53
|
-
Args:
|
|
54
|
-
tool_type: Filter tools by type (e.g., "custom", "native")
|
|
55
|
-
"""
|
|
56
|
-
endpoint = TOOLS_ENDPOINT
|
|
57
|
-
if tool_type:
|
|
58
|
-
endpoint += f"?type={tool_type}"
|
|
59
|
-
data = self._request("GET", endpoint)
|
|
60
|
-
return create_model_instances(data, Tool, self)
|
|
61
|
-
|
|
62
|
-
def get_tool_by_id(self, tool_id: str) -> Tool:
|
|
63
|
-
"""Get tool by ID."""
|
|
64
|
-
data = self._request("GET", f"{TOOLS_ENDPOINT}{tool_id}")
|
|
65
|
-
response = ToolResponse(**data)
|
|
66
|
-
return Tool.from_response(response, client=self)
|
|
67
|
-
|
|
68
|
-
def find_tools(self, name: str | None = None) -> list[Tool]:
|
|
69
|
-
"""Find tools by name."""
|
|
70
|
-
data = self._request("GET", TOOLS_ENDPOINT)
|
|
71
|
-
tools = create_model_instances(data, Tool, self)
|
|
72
|
-
return find_by_name(tools, name, case_sensitive=False)
|
|
73
|
-
|
|
74
|
-
def _validate_and_read_file(self, file_path: str) -> str:
|
|
75
|
-
"""Validate file exists and read its content.
|
|
76
|
-
|
|
77
|
-
Args:
|
|
78
|
-
file_path: Path to the file to read
|
|
79
|
-
|
|
80
|
-
Returns:
|
|
81
|
-
str: File content
|
|
82
|
-
|
|
83
|
-
Raises:
|
|
84
|
-
FileNotFoundError: If file doesn't exist
|
|
85
|
-
"""
|
|
86
|
-
if not os.path.exists(file_path):
|
|
87
|
-
raise FileNotFoundError(f"Tool file not found: {file_path}")
|
|
88
|
-
|
|
89
|
-
with open(file_path, encoding="utf-8") as f:
|
|
90
|
-
return f.read()
|
|
91
|
-
|
|
92
|
-
def _extract_name_from_file(self, file_path: str) -> str:
|
|
93
|
-
"""Extract tool name from file path.
|
|
94
|
-
|
|
95
|
-
Args:
|
|
96
|
-
file_path: Path to the file
|
|
97
|
-
|
|
98
|
-
Returns:
|
|
99
|
-
str: Extracted name (filename without extension)
|
|
100
|
-
"""
|
|
101
|
-
return os.path.splitext(os.path.basename(file_path))[0]
|
|
102
|
-
|
|
103
|
-
def _prepare_upload_data(self, name: str, framework: str, description: str | None = None, **kwargs) -> dict:
|
|
104
|
-
"""Prepare upload data dictionary.
|
|
105
|
-
|
|
106
|
-
Args:
|
|
107
|
-
name: Tool name
|
|
108
|
-
framework: Tool framework
|
|
109
|
-
description: Optional description
|
|
110
|
-
**kwargs: Additional parameters
|
|
111
|
-
|
|
112
|
-
Returns:
|
|
113
|
-
dict: Upload data dictionary
|
|
114
|
-
"""
|
|
115
|
-
data = {
|
|
116
|
-
"name": name,
|
|
117
|
-
"framework": framework,
|
|
118
|
-
"type": kwargs.pop("tool_type", DEFAULT_TOOL_TYPE), # Default to custom
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
if description:
|
|
122
|
-
data["description"] = description
|
|
123
|
-
|
|
124
|
-
# Handle tags if provided in kwargs
|
|
125
|
-
if kwargs.get("tags"):
|
|
126
|
-
if isinstance(kwargs["tags"], list):
|
|
127
|
-
data["tags"] = ",".join(kwargs["tags"])
|
|
128
|
-
else:
|
|
129
|
-
data["tags"] = kwargs["tags"]
|
|
130
|
-
|
|
131
|
-
# Include any other kwargs in the upload data
|
|
132
|
-
for key, value in kwargs.items():
|
|
133
|
-
if key not in ["tags"]: # tags already handled above
|
|
134
|
-
data[key] = value
|
|
135
|
-
|
|
136
|
-
return data
|
|
137
|
-
|
|
138
|
-
def _upload_tool_file(self, file_path: str, upload_data: dict) -> Tool:
|
|
139
|
-
"""Upload tool file to server.
|
|
140
|
-
|
|
141
|
-
Args:
|
|
142
|
-
file_path: Path to temporary file to upload
|
|
143
|
-
upload_data: Dictionary with upload metadata
|
|
144
|
-
|
|
145
|
-
Returns:
|
|
146
|
-
Tool: Created tool object
|
|
147
|
-
"""
|
|
148
|
-
with open(file_path, "rb") as fb:
|
|
149
|
-
files = {
|
|
150
|
-
"file": (os.path.basename(file_path), fb, "application/octet-stream"),
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
response = self._request(
|
|
154
|
-
"POST",
|
|
155
|
-
TOOLS_UPLOAD_ENDPOINT,
|
|
156
|
-
files=files,
|
|
157
|
-
data=upload_data,
|
|
158
|
-
)
|
|
159
|
-
|
|
160
|
-
tool_response = ToolResponse(**response)
|
|
161
|
-
return Tool.from_response(tool_response, client=self)
|
|
162
|
-
|
|
163
|
-
def _build_create_payload(
|
|
164
|
-
self,
|
|
165
|
-
name: str,
|
|
166
|
-
description: str | None = None,
|
|
167
|
-
framework: str = DEFAULT_TOOL_FRAMEWORK,
|
|
168
|
-
tool_type: str = DEFAULT_TOOL_TYPE,
|
|
169
|
-
**kwargs,
|
|
170
|
-
) -> dict[str, Any]:
|
|
171
|
-
"""Build payload for tool creation with proper metadata handling.
|
|
172
|
-
|
|
173
|
-
CENTRALIZED PAYLOAD BUILDING LOGIC:
|
|
174
|
-
- Handles file vs metadata-only tool creation
|
|
175
|
-
- Sets proper defaults and required fields
|
|
176
|
-
- Processes tags and other metadata consistently
|
|
177
|
-
|
|
178
|
-
Args:
|
|
179
|
-
name: Tool name
|
|
180
|
-
description: Tool description
|
|
181
|
-
framework: Tool framework (defaults to langchain)
|
|
182
|
-
tool_type: Tool type (defaults to custom)
|
|
183
|
-
**kwargs: Additional parameters (tags, version, etc.)
|
|
184
|
-
|
|
185
|
-
Returns:
|
|
186
|
-
Complete payload dictionary for tool creation
|
|
187
|
-
"""
|
|
188
|
-
# Prepare the creation payload with required fields
|
|
189
|
-
payload: dict[str, any] = {
|
|
190
|
-
"name": name.strip(),
|
|
191
|
-
"type": tool_type,
|
|
192
|
-
"framework": framework,
|
|
193
|
-
"version": kwargs.get("version", DEFAULT_TOOL_VERSION),
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
# Add description if provided
|
|
197
|
-
if description:
|
|
198
|
-
payload["description"] = description.strip()
|
|
199
|
-
|
|
200
|
-
# Handle tags - convert list to comma-separated string for API
|
|
201
|
-
if kwargs.get("tags"):
|
|
202
|
-
if isinstance(kwargs["tags"], list):
|
|
203
|
-
payload["tags"] = ",".join(str(tag).strip() for tag in kwargs["tags"])
|
|
204
|
-
else:
|
|
205
|
-
payload["tags"] = str(kwargs["tags"])
|
|
206
|
-
|
|
207
|
-
# Add any other kwargs (excluding already handled ones)
|
|
208
|
-
excluded_keys = {"tags", "version"}
|
|
209
|
-
add_kwargs_to_payload(payload, kwargs, excluded_keys)
|
|
210
|
-
|
|
211
|
-
return payload
|
|
212
|
-
|
|
213
|
-
def _handle_description_update(
|
|
214
|
-
self, update_data: dict[str, Any], description: str | None, current_tool: Tool
|
|
215
|
-
) -> None:
|
|
216
|
-
"""Handle description field in update payload."""
|
|
217
|
-
if description is not None:
|
|
218
|
-
update_data["description"] = description.strip()
|
|
219
|
-
elif hasattr(current_tool, "description") and current_tool.description:
|
|
220
|
-
update_data["description"] = current_tool.description
|
|
221
|
-
|
|
222
|
-
def _handle_tags_update(self, update_data: dict[str, Any], kwargs: dict[str, Any], current_tool: Tool) -> None:
|
|
223
|
-
"""Handle tags field in update payload."""
|
|
224
|
-
if kwargs.get("tags"):
|
|
225
|
-
if isinstance(kwargs["tags"], list):
|
|
226
|
-
update_data["tags"] = ",".join(str(tag).strip() for tag in kwargs["tags"])
|
|
227
|
-
else:
|
|
228
|
-
update_data["tags"] = str(kwargs["tags"])
|
|
229
|
-
elif hasattr(current_tool, "tags") and current_tool.tags:
|
|
230
|
-
# Preserve existing tags if present
|
|
231
|
-
if isinstance(current_tool.tags, list):
|
|
232
|
-
update_data["tags"] = ",".join(str(tag).strip() for tag in current_tool.tags)
|
|
233
|
-
else:
|
|
234
|
-
update_data["tags"] = str(current_tool.tags)
|
|
235
|
-
|
|
236
|
-
def _handle_additional_kwargs(self, update_data: dict[str, Any], kwargs: dict[str, Any]) -> None:
|
|
237
|
-
"""Handle additional kwargs in update payload."""
|
|
238
|
-
excluded_keys = {
|
|
239
|
-
"tags",
|
|
240
|
-
"framework",
|
|
241
|
-
"version",
|
|
242
|
-
"type",
|
|
243
|
-
"tool_type",
|
|
244
|
-
"name",
|
|
245
|
-
"description",
|
|
246
|
-
}
|
|
247
|
-
for key, value in kwargs.items():
|
|
248
|
-
if key not in excluded_keys:
|
|
249
|
-
update_data[key] = value
|
|
250
|
-
|
|
251
|
-
def _build_update_payload(
|
|
252
|
-
self,
|
|
253
|
-
current_tool: Tool,
|
|
254
|
-
name: str | None = None,
|
|
255
|
-
description: str | None = None,
|
|
256
|
-
**kwargs,
|
|
257
|
-
) -> dict[str, Any]:
|
|
258
|
-
"""Build payload for tool update with proper current state preservation.
|
|
259
|
-
|
|
260
|
-
Args:
|
|
261
|
-
current_tool: Current tool object to update
|
|
262
|
-
name: New tool name (None to keep current)
|
|
263
|
-
description: New description (None to keep current)
|
|
264
|
-
**kwargs: Additional parameters (tags, framework, etc.)
|
|
265
|
-
|
|
266
|
-
Returns:
|
|
267
|
-
Complete payload dictionary for tool update
|
|
268
|
-
|
|
269
|
-
Notes:
|
|
270
|
-
- Preserves current values as defaults when new values not provided
|
|
271
|
-
- Handles metadata updates properly
|
|
272
|
-
"""
|
|
273
|
-
# Prepare the update payload with current values as defaults
|
|
274
|
-
type_override = kwargs.pop("type", None)
|
|
275
|
-
if type_override is None:
|
|
276
|
-
type_override = kwargs.pop("tool_type", None)
|
|
277
|
-
current_type = (
|
|
278
|
-
type_override
|
|
279
|
-
or getattr(current_tool, "tool_type", None)
|
|
280
|
-
or getattr(current_tool, "type", None)
|
|
281
|
-
or DEFAULT_TOOL_TYPE
|
|
282
|
-
)
|
|
283
|
-
# Convert enum to string value for API payload
|
|
284
|
-
if hasattr(current_type, "value"):
|
|
285
|
-
current_type = current_type.value
|
|
286
|
-
|
|
287
|
-
update_data = {
|
|
288
|
-
"name": name if name is not None else current_tool.name,
|
|
289
|
-
"type": current_type,
|
|
290
|
-
"framework": kwargs.get("framework", getattr(current_tool, "framework", DEFAULT_TOOL_FRAMEWORK)),
|
|
291
|
-
"version": kwargs.get("version", getattr(current_tool, "version", DEFAULT_TOOL_VERSION)),
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
# Handle description update
|
|
295
|
-
self._handle_description_update(update_data, description, current_tool)
|
|
296
|
-
|
|
297
|
-
# Handle tags update
|
|
298
|
-
self._handle_tags_update(update_data, kwargs, current_tool)
|
|
299
|
-
|
|
300
|
-
# Handle additional kwargs
|
|
301
|
-
self._handle_additional_kwargs(update_data, kwargs)
|
|
302
|
-
|
|
303
|
-
return update_data
|
|
304
|
-
|
|
305
|
-
def _create_tool_from_file(
|
|
306
|
-
self,
|
|
307
|
-
file_path: str,
|
|
308
|
-
name: str | None = None,
|
|
309
|
-
description: str | None = None,
|
|
310
|
-
framework: str = "langchain",
|
|
311
|
-
**kwargs,
|
|
312
|
-
) -> Tool:
|
|
313
|
-
"""Create tool from file content using upload endpoint.
|
|
314
|
-
|
|
315
|
-
Args:
|
|
316
|
-
file_path: Path to tool file
|
|
317
|
-
name: Optional tool name (auto-detected if not provided)
|
|
318
|
-
description: Optional tool description
|
|
319
|
-
framework: Tool framework
|
|
320
|
-
**kwargs: Additional parameters
|
|
321
|
-
|
|
322
|
-
Returns:
|
|
323
|
-
Tool: Created tool object
|
|
324
|
-
"""
|
|
325
|
-
# Read and validate file
|
|
326
|
-
file_content = self._validate_and_read_file(file_path)
|
|
327
|
-
|
|
328
|
-
# Auto-detect name if not provided
|
|
329
|
-
if not name:
|
|
330
|
-
name = self._extract_name_from_file(file_path)
|
|
331
|
-
|
|
332
|
-
# Handle description - generate default if not provided or empty
|
|
333
|
-
if description is None or description == "":
|
|
334
|
-
# Generate default description based on tool_type if available
|
|
335
|
-
tool_type = kwargs.get("tool_type", "custom")
|
|
336
|
-
description = f"A {tool_type} tool"
|
|
337
|
-
|
|
338
|
-
# Create temporary file for upload
|
|
339
|
-
with tempfile.NamedTemporaryFile(
|
|
340
|
-
mode="w",
|
|
341
|
-
suffix=".py",
|
|
342
|
-
prefix=f"{name}_",
|
|
343
|
-
delete=False,
|
|
344
|
-
encoding="utf-8",
|
|
345
|
-
) as temp_file:
|
|
346
|
-
temp_file.write(file_content)
|
|
347
|
-
temp_file_path = temp_file.name
|
|
348
|
-
|
|
349
|
-
try:
|
|
350
|
-
# Prepare upload data
|
|
351
|
-
upload_data = self._prepare_upload_data(name=name, framework=framework, description=description, **kwargs)
|
|
352
|
-
|
|
353
|
-
# Upload file
|
|
354
|
-
return self._upload_tool_file(temp_file_path, upload_data)
|
|
355
|
-
|
|
356
|
-
finally:
|
|
357
|
-
# Clean up temporary file
|
|
358
|
-
try:
|
|
359
|
-
os.unlink(temp_file_path)
|
|
360
|
-
except OSError:
|
|
361
|
-
pass # Ignore cleanup errors
|
|
362
|
-
|
|
363
|
-
def create_tool(
|
|
364
|
-
self,
|
|
365
|
-
file_path: str,
|
|
366
|
-
name: str | None = None,
|
|
367
|
-
description: str | None = None,
|
|
368
|
-
framework: str = "langchain",
|
|
369
|
-
**kwargs,
|
|
370
|
-
) -> Tool:
|
|
371
|
-
"""Create a new tool from a file.
|
|
372
|
-
|
|
373
|
-
Args:
|
|
374
|
-
file_path: File path to tool script (required) - file content will be read and processed as plugin
|
|
375
|
-
name: Tool name (auto-detected from file if not provided)
|
|
376
|
-
description: Tool description (auto-generated if not provided)
|
|
377
|
-
framework: Tool framework (defaults to "langchain")
|
|
378
|
-
**kwargs: Additional tool parameters
|
|
379
|
-
"""
|
|
380
|
-
return self._create_tool_from_file(
|
|
381
|
-
file_path=file_path,
|
|
382
|
-
name=name,
|
|
383
|
-
description=description,
|
|
384
|
-
framework=framework,
|
|
385
|
-
**kwargs,
|
|
386
|
-
)
|
|
387
|
-
|
|
388
|
-
def create_tool_from_code(
|
|
389
|
-
self,
|
|
390
|
-
name: str,
|
|
391
|
-
code: str,
|
|
392
|
-
framework: str = "langchain",
|
|
393
|
-
description: str | None = None,
|
|
394
|
-
tags: list[str] | None = None,
|
|
395
|
-
) -> Tool:
|
|
396
|
-
"""Create a new tool plugin from code string.
|
|
397
|
-
|
|
398
|
-
This method uses the /tools/upload endpoint which properly processes
|
|
399
|
-
and registers tool plugins, unlike the regular create_tool method
|
|
400
|
-
which only creates metadata.
|
|
401
|
-
|
|
402
|
-
Args:
|
|
403
|
-
name: Name for the tool (used for temporary file naming)
|
|
404
|
-
code: Python code containing the tool plugin
|
|
405
|
-
framework: Tool framework (defaults to "langchain")
|
|
406
|
-
description: Optional tool description
|
|
407
|
-
tags: Optional list of tags
|
|
408
|
-
|
|
409
|
-
Returns:
|
|
410
|
-
Tool: The created tool object
|
|
411
|
-
"""
|
|
412
|
-
# Create a temporary file with the tool code
|
|
413
|
-
with tempfile.NamedTemporaryFile(
|
|
414
|
-
mode="w",
|
|
415
|
-
suffix=".py",
|
|
416
|
-
prefix=f"{name}_",
|
|
417
|
-
delete=False,
|
|
418
|
-
encoding="utf-8",
|
|
419
|
-
) as temp_file:
|
|
420
|
-
temp_file.write(code)
|
|
421
|
-
temp_file_path = temp_file.name
|
|
422
|
-
|
|
423
|
-
try:
|
|
424
|
-
# Prepare upload data using shared helper
|
|
425
|
-
upload_data = self._prepare_upload_data(
|
|
426
|
-
name=name,
|
|
427
|
-
framework=framework,
|
|
428
|
-
description=description,
|
|
429
|
-
tags=tags if tags else None,
|
|
430
|
-
)
|
|
431
|
-
|
|
432
|
-
# Upload file using shared helper
|
|
433
|
-
return self._upload_tool_file(temp_file_path, upload_data)
|
|
434
|
-
|
|
435
|
-
finally:
|
|
436
|
-
# Clean up the temporary file
|
|
437
|
-
try:
|
|
438
|
-
os.unlink(temp_file_path)
|
|
439
|
-
except OSError:
|
|
440
|
-
pass # Ignore cleanup errors
|
|
441
|
-
|
|
442
|
-
def update_tool(self, tool_id: str, **kwargs) -> Tool:
|
|
443
|
-
"""Update an existing tool."""
|
|
444
|
-
data = self._request("PUT", f"{TOOLS_ENDPOINT}{tool_id}", json=kwargs)
|
|
445
|
-
response = ToolResponse(**data)
|
|
446
|
-
return Tool.from_response(response, client=self)
|
|
447
|
-
|
|
448
|
-
def delete_tool(self, tool_id: str) -> None:
|
|
449
|
-
"""Delete a tool."""
|
|
450
|
-
self._request("DELETE", f"{TOOLS_ENDPOINT}{tool_id}")
|
|
451
|
-
|
|
452
|
-
def upsert_tool(
|
|
453
|
-
self,
|
|
454
|
-
identifier: str | Tool,
|
|
455
|
-
code: str | None = None,
|
|
456
|
-
description: str | None = None,
|
|
457
|
-
framework: str = "langchain",
|
|
458
|
-
**kwargs,
|
|
459
|
-
) -> Tool:
|
|
460
|
-
"""Create or update a tool by instance, ID, or name.
|
|
461
|
-
|
|
462
|
-
Args:
|
|
463
|
-
identifier: Tool instance, ID (UUID string), or name
|
|
464
|
-
code: Python code containing the tool plugin (required for create)
|
|
465
|
-
description: Tool description
|
|
466
|
-
framework: Tool framework (defaults to "langchain")
|
|
467
|
-
**kwargs: Additional parameters (tags, version, etc.)
|
|
468
|
-
|
|
469
|
-
Returns:
|
|
470
|
-
The created or updated tool.
|
|
471
|
-
|
|
472
|
-
Example:
|
|
473
|
-
>>> # By name with code (creates if not exists)
|
|
474
|
-
>>> tool = client.tools.upsert_tool(
|
|
475
|
-
... "greeting",
|
|
476
|
-
... code=bundled_source,
|
|
477
|
-
... description="A greeting tool",
|
|
478
|
-
... )
|
|
479
|
-
>>> # By instance
|
|
480
|
-
>>> tool = client.tools.upsert_tool(existing_tool, code=new_code)
|
|
481
|
-
>>> # By ID
|
|
482
|
-
>>> tool = client.tools.upsert_tool("uuid-here", code=new_code)
|
|
483
|
-
"""
|
|
484
|
-
# Handle Tool instance
|
|
485
|
-
if isinstance(identifier, Tool):
|
|
486
|
-
if identifier.id:
|
|
487
|
-
logger.info("Updating tool by instance: %s", identifier.name)
|
|
488
|
-
return self._do_tool_upsert_update(
|
|
489
|
-
identifier.id,
|
|
490
|
-
identifier.name,
|
|
491
|
-
code,
|
|
492
|
-
description,
|
|
493
|
-
framework,
|
|
494
|
-
**kwargs,
|
|
495
|
-
)
|
|
496
|
-
identifier = identifier.name
|
|
497
|
-
|
|
498
|
-
# Handle string (ID or name)
|
|
499
|
-
if isinstance(identifier, str):
|
|
500
|
-
if is_uuid(identifier):
|
|
501
|
-
logger.info("Updating tool by ID: %s", identifier)
|
|
502
|
-
existing = self.get_tool_by_id(identifier)
|
|
503
|
-
return self._do_tool_upsert_update(identifier, existing.name, code, description, framework, **kwargs)
|
|
504
|
-
|
|
505
|
-
# It's a name - find or create
|
|
506
|
-
return self._upsert_tool_by_name(identifier, code, description, framework, **kwargs)
|
|
507
|
-
|
|
508
|
-
raise ValueError(f"Invalid identifier type: {type(identifier)}")
|
|
509
|
-
|
|
510
|
-
def _do_tool_upsert_update(
|
|
511
|
-
self,
|
|
512
|
-
tool_id: str,
|
|
513
|
-
name: str | None,
|
|
514
|
-
code: str | None,
|
|
515
|
-
description: str | None,
|
|
516
|
-
framework: str,
|
|
517
|
-
**kwargs,
|
|
518
|
-
) -> Tool:
|
|
519
|
-
"""Perform the update part of tool upsert."""
|
|
520
|
-
if code:
|
|
521
|
-
# Update via file upload
|
|
522
|
-
with tempfile.NamedTemporaryFile(
|
|
523
|
-
mode="w",
|
|
524
|
-
suffix=".py",
|
|
525
|
-
prefix=f"{name or 'tool'}_",
|
|
526
|
-
delete=False,
|
|
527
|
-
encoding="utf-8",
|
|
528
|
-
) as temp_file:
|
|
529
|
-
temp_file.write(code)
|
|
530
|
-
temp_file_path = temp_file.name
|
|
531
|
-
|
|
532
|
-
try:
|
|
533
|
-
return self.update_tool_via_file(
|
|
534
|
-
tool_id,
|
|
535
|
-
temp_file_path,
|
|
536
|
-
name=name,
|
|
537
|
-
description=description,
|
|
538
|
-
framework=framework,
|
|
539
|
-
**kwargs,
|
|
540
|
-
)
|
|
541
|
-
finally:
|
|
542
|
-
try:
|
|
543
|
-
os.unlink(temp_file_path)
|
|
544
|
-
except OSError:
|
|
545
|
-
pass
|
|
546
|
-
else:
|
|
547
|
-
# Metadata-only update
|
|
548
|
-
update_kwargs = {"framework": framework, **kwargs}
|
|
549
|
-
if name:
|
|
550
|
-
update_kwargs["name"] = name
|
|
551
|
-
if description:
|
|
552
|
-
update_kwargs["description"] = description
|
|
553
|
-
return self.update_tool(tool_id, **update_kwargs)
|
|
554
|
-
|
|
555
|
-
def _upsert_tool_by_name(
|
|
556
|
-
self,
|
|
557
|
-
name: str,
|
|
558
|
-
code: str | None,
|
|
559
|
-
description: str | None,
|
|
560
|
-
framework: str,
|
|
561
|
-
**kwargs,
|
|
562
|
-
) -> Tool:
|
|
563
|
-
"""Find tool by name and update, or create if not found."""
|
|
564
|
-
existing = self.find_tools(name)
|
|
565
|
-
name_lower = name.lower()
|
|
566
|
-
exact_matches = [tool for tool in existing if tool.name and tool.name.lower() == name_lower]
|
|
567
|
-
|
|
568
|
-
if len(exact_matches) == 1:
|
|
569
|
-
logger.info("Updating existing tool: %s", name)
|
|
570
|
-
return self._do_tool_upsert_update(exact_matches[0].id, name, code, description, framework, **kwargs)
|
|
571
|
-
|
|
572
|
-
if len(exact_matches) > 1:
|
|
573
|
-
raise ValueError(f"Multiple tools found with name '{name}'")
|
|
574
|
-
|
|
575
|
-
# Create new tool - code is required
|
|
576
|
-
if not code:
|
|
577
|
-
raise ValueError(f"Tool '{name}' not found and no code provided for creation")
|
|
578
|
-
|
|
579
|
-
logger.info("Creating new tool: %s", name)
|
|
580
|
-
return self.create_tool_from_code(
|
|
581
|
-
name=name,
|
|
582
|
-
code=code,
|
|
583
|
-
framework=framework,
|
|
584
|
-
description=description,
|
|
585
|
-
**kwargs,
|
|
586
|
-
)
|
|
587
|
-
|
|
588
|
-
def get_tool_script(self, tool_id: str) -> str:
|
|
589
|
-
"""Get the tool script content.
|
|
590
|
-
|
|
591
|
-
Args:
|
|
592
|
-
tool_id: The ID of the tool
|
|
593
|
-
|
|
594
|
-
Returns:
|
|
595
|
-
str: The tool script content
|
|
596
|
-
|
|
597
|
-
Raises:
|
|
598
|
-
Exception: If the tool script cannot be retrieved
|
|
599
|
-
"""
|
|
600
|
-
try:
|
|
601
|
-
response = self._request("GET", f"{TOOLS_ENDPOINT}{tool_id}/script")
|
|
602
|
-
return response.get("script", "") or response.get("content", "")
|
|
603
|
-
except Exception as e:
|
|
604
|
-
logger.error(f"Failed to get tool script for {tool_id}: {e}")
|
|
605
|
-
raise
|
|
606
|
-
|
|
607
|
-
def update_tool_via_file(self, tool_id: str, file_path: str, **kwargs) -> Tool:
|
|
608
|
-
"""Update a tool plugin via file upload.
|
|
609
|
-
|
|
610
|
-
Args:
|
|
611
|
-
tool_id: The ID of the tool to update
|
|
612
|
-
file_path: Path to the new tool file
|
|
613
|
-
**kwargs: Additional metadata to update (name, description, tags, etc.)
|
|
614
|
-
|
|
615
|
-
Returns:
|
|
616
|
-
Tool: The updated tool object
|
|
617
|
-
|
|
618
|
-
Raises:
|
|
619
|
-
FileNotFoundError: If the file doesn't exist
|
|
620
|
-
Exception: If the update fails
|
|
621
|
-
"""
|
|
622
|
-
# Validate file exists
|
|
623
|
-
self._validate_and_read_file(file_path)
|
|
624
|
-
|
|
625
|
-
# Fetch current metadata to ensure required fields are preserved
|
|
626
|
-
current_tool = self.get_tool_by_id(tool_id)
|
|
627
|
-
|
|
628
|
-
payload_kwargs = kwargs.copy()
|
|
629
|
-
name = payload_kwargs.pop("name", None)
|
|
630
|
-
description = payload_kwargs.pop("description", None)
|
|
631
|
-
update_payload = self._build_update_payload(
|
|
632
|
-
current_tool=current_tool,
|
|
633
|
-
name=name,
|
|
634
|
-
description=description,
|
|
635
|
-
**payload_kwargs,
|
|
636
|
-
)
|
|
637
|
-
|
|
638
|
-
try:
|
|
639
|
-
# Prepare multipart upload
|
|
640
|
-
with open(file_path, "rb") as fb:
|
|
641
|
-
files = {
|
|
642
|
-
"file": (
|
|
643
|
-
os.path.basename(file_path),
|
|
644
|
-
fb,
|
|
645
|
-
"application/octet-stream",
|
|
646
|
-
),
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
response = self._request(
|
|
650
|
-
"PUT",
|
|
651
|
-
TOOLS_UPLOAD_BY_ID_ENDPOINT_FMT.format(tool_id=tool_id),
|
|
652
|
-
files=files,
|
|
653
|
-
data=update_payload,
|
|
654
|
-
)
|
|
655
|
-
|
|
656
|
-
tool_response = ToolResponse(**response)
|
|
657
|
-
return Tool.from_response(tool_response, client=self)
|
|
658
|
-
|
|
659
|
-
except Exception as e:
|
|
660
|
-
logger.error("Failed to update tool %s via file: %s", tool_id, e)
|
|
661
|
-
raise
|