deepset-mcp 0.0.4rc1__py3-none-any.whl → 0.0.5rc1__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.
- deepset_mcp/__init__.py +5 -5
- deepset_mcp/api/transport.py +5 -2
- deepset_mcp/config.py +9 -1
- deepset_mcp/main.py +186 -169
- deepset_mcp/py.typed +0 -0
- deepset_mcp/server.py +154 -0
- deepset_mcp/store.py +51 -2
- deepset_mcp/tool_factory.py +301 -428
- deepset_mcp/tool_models.py +42 -0
- deepset_mcp/tool_registry.py +208 -0
- deepset_mcp/tools/object_store.py +49 -0
- deepset_mcp/tools/tokonomics/__init__.py +2 -61
- deepset_mcp/tools/tokonomics/decorators.py +60 -87
- deepset_mcp/tools/tokonomics/explorer.py +32 -17
- deepset_mcp/tools/tokonomics/object_store.py +116 -139
- {deepset_mcp-0.0.4rc1.dist-info → deepset_mcp-0.0.5rc1.dist-info}/METADATA +59 -13
- {deepset_mcp-0.0.4rc1.dist-info → deepset_mcp-0.0.5rc1.dist-info}/RECORD +20 -15
- deepset_mcp-0.0.5rc1.dist-info/entry_points.txt +2 -0
- deepset_mcp-0.0.4rc1.dist-info/entry_points.txt +0 -2
- {deepset_mcp-0.0.4rc1.dist-info → deepset_mcp-0.0.5rc1.dist-info}/WHEEL +0 -0
- {deepset_mcp-0.0.4rc1.dist-info → deepset_mcp-0.0.5rc1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2025-present deepset GmbH <info@deepset.ai>
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from enum import StrEnum
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class MemoryType(StrEnum):
|
|
11
|
+
"""Configuration for how memory is provided to tools."""
|
|
12
|
+
|
|
13
|
+
EXPLORABLE = "explorable"
|
|
14
|
+
REFERENCEABLE = "referenceable"
|
|
15
|
+
BOTH = "both"
|
|
16
|
+
NO_MEMORY = "no_memory"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class ToolConfig:
|
|
21
|
+
"""Configuration for tool registration."""
|
|
22
|
+
|
|
23
|
+
needs_client: bool = False
|
|
24
|
+
needs_workspace: bool = False
|
|
25
|
+
memory_type: MemoryType = MemoryType.NO_MEMORY
|
|
26
|
+
custom_args: dict[str, Any] = field(default_factory=dict)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class WorkspaceMode(StrEnum):
|
|
30
|
+
"""Configuration for how workspace is provided to tools."""
|
|
31
|
+
|
|
32
|
+
STATIC = "static" # workspace from env, no parameter in tool signature
|
|
33
|
+
DYNAMIC = "dynamic" # workspace as required parameter in tool signature
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class DeepsetDocsConfig:
|
|
38
|
+
"""Configuration for deepset documentation search tool."""
|
|
39
|
+
|
|
40
|
+
pipeline_name: str
|
|
41
|
+
api_key: str
|
|
42
|
+
workspace_name: str
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2025-present deepset GmbH <info@deepset.ai>
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from deepset_mcp.api.client import AsyncDeepsetClient
|
|
9
|
+
from deepset_mcp.config import DEFAULT_CLIENT_HEADER, DOCS_SEARCH_TOOL_NAME
|
|
10
|
+
from deepset_mcp.initialize_embedding_model import get_initialized_model
|
|
11
|
+
from deepset_mcp.tool_models import DeepsetDocsConfig, MemoryType, ToolConfig
|
|
12
|
+
from deepset_mcp.tools.custom_components import (
|
|
13
|
+
get_latest_custom_component_installation_logs as get_latest_custom_component_installation_logs_tool,
|
|
14
|
+
list_custom_component_installations as list_custom_component_installations_tool,
|
|
15
|
+
)
|
|
16
|
+
from deepset_mcp.tools.doc_search import search_docs as search_docs_tool
|
|
17
|
+
from deepset_mcp.tools.haystack_service import (
|
|
18
|
+
get_component_definition as get_component_definition_tool,
|
|
19
|
+
get_custom_components as get_custom_components_tool,
|
|
20
|
+
list_component_families as list_component_families_tool,
|
|
21
|
+
search_component_definition as search_component_definition_tool,
|
|
22
|
+
)
|
|
23
|
+
from deepset_mcp.tools.indexes import (
|
|
24
|
+
create_index as create_index_tool,
|
|
25
|
+
deploy_index as deploy_index_tool,
|
|
26
|
+
get_index as get_index_tool,
|
|
27
|
+
list_indexes as list_indexes_tool,
|
|
28
|
+
update_index as update_index_tool,
|
|
29
|
+
)
|
|
30
|
+
from deepset_mcp.tools.object_store import create_get_from_object_store, create_get_slice_from_object_store
|
|
31
|
+
from deepset_mcp.tools.pipeline import (
|
|
32
|
+
create_pipeline as create_pipeline_tool,
|
|
33
|
+
deploy_pipeline as deploy_pipeline_tool,
|
|
34
|
+
get_pipeline as get_pipeline_tool,
|
|
35
|
+
get_pipeline_logs as get_pipeline_logs_tool,
|
|
36
|
+
list_pipelines as list_pipelines_tool,
|
|
37
|
+
search_pipeline as search_pipeline_tool,
|
|
38
|
+
update_pipeline as update_pipeline_tool,
|
|
39
|
+
validate_pipeline as validate_pipeline_tool,
|
|
40
|
+
)
|
|
41
|
+
from deepset_mcp.tools.pipeline_template import (
|
|
42
|
+
get_template as get_pipeline_template_tool,
|
|
43
|
+
list_templates as list_pipeline_templates_tool,
|
|
44
|
+
search_templates as search_pipeline_templates_tool,
|
|
45
|
+
)
|
|
46
|
+
from deepset_mcp.tools.secrets import get_secret as get_secret_tool, list_secrets as list_secrets_tool
|
|
47
|
+
from deepset_mcp.tools.workspace import (
|
|
48
|
+
create_workspace as create_workspace_tool,
|
|
49
|
+
get_workspace as get_workspace_tool,
|
|
50
|
+
list_workspaces as list_workspaces_tool,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def get_docs_search_tool(config: DeepsetDocsConfig) -> Callable[..., Any]:
|
|
55
|
+
"""Get a docs search tool configured with the provided config."""
|
|
56
|
+
|
|
57
|
+
async def search_docs(query: str) -> str:
|
|
58
|
+
"""Search the deepset platform documentation.
|
|
59
|
+
|
|
60
|
+
This tool allows you to search through deepset's official documentation to find
|
|
61
|
+
information about features, API usage, best practices, and troubleshooting guides.
|
|
62
|
+
Use this when you need to look up specific deepset functionality or help users
|
|
63
|
+
understand how to use deepset features.
|
|
64
|
+
|
|
65
|
+
:param query: The search query to execute against the documentation.
|
|
66
|
+
:returns: The formatted search results from the documentation.
|
|
67
|
+
"""
|
|
68
|
+
async with AsyncDeepsetClient(api_key=config.api_key, transport_config=DEFAULT_CLIENT_HEADER) as client:
|
|
69
|
+
response = await search_docs_tool(
|
|
70
|
+
client=client,
|
|
71
|
+
workspace=config.workspace_name,
|
|
72
|
+
pipeline_name=config.pipeline_name,
|
|
73
|
+
query=query,
|
|
74
|
+
)
|
|
75
|
+
return response
|
|
76
|
+
|
|
77
|
+
return search_docs
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
TOOL_REGISTRY: dict[str, tuple[Callable[..., Any], ToolConfig]] = {
|
|
81
|
+
# Workspace tools
|
|
82
|
+
"list_pipelines": (
|
|
83
|
+
list_pipelines_tool,
|
|
84
|
+
ToolConfig(needs_client=True, needs_workspace=True, memory_type=MemoryType.EXPLORABLE),
|
|
85
|
+
),
|
|
86
|
+
"create_pipeline": (
|
|
87
|
+
create_pipeline_tool,
|
|
88
|
+
ToolConfig(
|
|
89
|
+
needs_client=True,
|
|
90
|
+
needs_workspace=True,
|
|
91
|
+
memory_type=MemoryType.BOTH,
|
|
92
|
+
custom_args={"skip_validation_errors": True},
|
|
93
|
+
),
|
|
94
|
+
),
|
|
95
|
+
"update_pipeline": (
|
|
96
|
+
update_pipeline_tool,
|
|
97
|
+
ToolConfig(
|
|
98
|
+
needs_client=True,
|
|
99
|
+
needs_workspace=True,
|
|
100
|
+
memory_type=MemoryType.BOTH,
|
|
101
|
+
custom_args={"skip_validation_errors": True},
|
|
102
|
+
),
|
|
103
|
+
),
|
|
104
|
+
"get_pipeline": (
|
|
105
|
+
get_pipeline_tool,
|
|
106
|
+
ToolConfig(needs_client=True, needs_workspace=True, memory_type=MemoryType.EXPLORABLE),
|
|
107
|
+
),
|
|
108
|
+
"deploy_pipeline": (
|
|
109
|
+
deploy_pipeline_tool,
|
|
110
|
+
ToolConfig(
|
|
111
|
+
needs_client=True,
|
|
112
|
+
needs_workspace=True,
|
|
113
|
+
memory_type=MemoryType.EXPLORABLE,
|
|
114
|
+
custom_args={"wait_for_deployment": True, "timeout_seconds": 600, "poll_interval": 5},
|
|
115
|
+
),
|
|
116
|
+
),
|
|
117
|
+
"validate_pipeline": (
|
|
118
|
+
validate_pipeline_tool,
|
|
119
|
+
ToolConfig(needs_client=True, needs_workspace=True, memory_type=MemoryType.BOTH),
|
|
120
|
+
),
|
|
121
|
+
"get_pipeline_logs": (
|
|
122
|
+
get_pipeline_logs_tool,
|
|
123
|
+
ToolConfig(needs_client=True, needs_workspace=True, memory_type=MemoryType.EXPLORABLE),
|
|
124
|
+
),
|
|
125
|
+
"search_pipeline": (
|
|
126
|
+
search_pipeline_tool,
|
|
127
|
+
ToolConfig(needs_client=True, needs_workspace=True, memory_type=MemoryType.EXPLORABLE),
|
|
128
|
+
),
|
|
129
|
+
"list_indexes": (
|
|
130
|
+
list_indexes_tool,
|
|
131
|
+
ToolConfig(needs_client=True, needs_workspace=True, memory_type=MemoryType.EXPLORABLE),
|
|
132
|
+
),
|
|
133
|
+
"get_index": (
|
|
134
|
+
get_index_tool,
|
|
135
|
+
ToolConfig(needs_client=True, needs_workspace=True, memory_type=MemoryType.EXPLORABLE),
|
|
136
|
+
),
|
|
137
|
+
"create_index": (
|
|
138
|
+
create_index_tool,
|
|
139
|
+
ToolConfig(needs_client=True, needs_workspace=True, memory_type=MemoryType.BOTH),
|
|
140
|
+
),
|
|
141
|
+
"update_index": (
|
|
142
|
+
update_index_tool,
|
|
143
|
+
ToolConfig(needs_client=True, needs_workspace=True, memory_type=MemoryType.BOTH),
|
|
144
|
+
),
|
|
145
|
+
"deploy_index": (
|
|
146
|
+
deploy_index_tool,
|
|
147
|
+
ToolConfig(needs_client=True, needs_workspace=True, memory_type=MemoryType.EXPLORABLE),
|
|
148
|
+
),
|
|
149
|
+
"list_templates": (
|
|
150
|
+
list_pipeline_templates_tool,
|
|
151
|
+
ToolConfig(
|
|
152
|
+
needs_client=True,
|
|
153
|
+
needs_workspace=True,
|
|
154
|
+
memory_type=MemoryType.EXPLORABLE,
|
|
155
|
+
custom_args={"field": "created_at", "order": "DESC", "limit": 100},
|
|
156
|
+
),
|
|
157
|
+
),
|
|
158
|
+
"get_template": (
|
|
159
|
+
get_pipeline_template_tool,
|
|
160
|
+
ToolConfig(needs_client=True, needs_workspace=True, memory_type=MemoryType.EXPLORABLE),
|
|
161
|
+
),
|
|
162
|
+
"search_templates": (
|
|
163
|
+
search_pipeline_templates_tool,
|
|
164
|
+
ToolConfig(
|
|
165
|
+
needs_client=True,
|
|
166
|
+
needs_workspace=True,
|
|
167
|
+
memory_type=MemoryType.EXPLORABLE,
|
|
168
|
+
custom_args={"model": get_initialized_model()},
|
|
169
|
+
),
|
|
170
|
+
),
|
|
171
|
+
"list_custom_component_installations": (
|
|
172
|
+
list_custom_component_installations_tool,
|
|
173
|
+
ToolConfig(needs_client=True, needs_workspace=True, memory_type=MemoryType.EXPLORABLE),
|
|
174
|
+
),
|
|
175
|
+
"get_latest_custom_component_installation_logs": (
|
|
176
|
+
get_latest_custom_component_installation_logs_tool,
|
|
177
|
+
ToolConfig(needs_client=True, needs_workspace=True, memory_type=MemoryType.EXPLORABLE),
|
|
178
|
+
),
|
|
179
|
+
# Non-workspace tools
|
|
180
|
+
"list_component_families": (
|
|
181
|
+
list_component_families_tool,
|
|
182
|
+
ToolConfig(needs_client=True, memory_type=MemoryType.EXPLORABLE),
|
|
183
|
+
),
|
|
184
|
+
"get_component_definition": (
|
|
185
|
+
get_component_definition_tool,
|
|
186
|
+
ToolConfig(needs_client=True, memory_type=MemoryType.EXPLORABLE),
|
|
187
|
+
),
|
|
188
|
+
"search_component_definitions": (
|
|
189
|
+
search_component_definition_tool,
|
|
190
|
+
ToolConfig(
|
|
191
|
+
needs_client=True, memory_type=MemoryType.EXPLORABLE, custom_args={"model": get_initialized_model()}
|
|
192
|
+
),
|
|
193
|
+
),
|
|
194
|
+
"get_custom_components": (
|
|
195
|
+
get_custom_components_tool,
|
|
196
|
+
ToolConfig(needs_client=True, memory_type=MemoryType.EXPLORABLE),
|
|
197
|
+
),
|
|
198
|
+
"list_secrets": (list_secrets_tool, ToolConfig(needs_client=True, memory_type=MemoryType.EXPLORABLE)),
|
|
199
|
+
"get_secret": (get_secret_tool, ToolConfig(needs_client=True, memory_type=MemoryType.EXPLORABLE)),
|
|
200
|
+
"list_workspaces": (list_workspaces_tool, ToolConfig(needs_client=True, memory_type=MemoryType.EXPLORABLE)),
|
|
201
|
+
"get_workspace": (get_workspace_tool, ToolConfig(needs_client=True, memory_type=MemoryType.EXPLORABLE)),
|
|
202
|
+
"create_workspace": (create_workspace_tool, ToolConfig(needs_client=True, memory_type=MemoryType.EXPLORABLE)),
|
|
203
|
+
"get_from_object_store": (create_get_from_object_store, ToolConfig(memory_type=MemoryType.NO_MEMORY)),
|
|
204
|
+
"get_slice_from_object_store": (create_get_slice_from_object_store, ToolConfig(memory_type=MemoryType.NO_MEMORY)),
|
|
205
|
+
DOCS_SEARCH_TOOL_NAME: (get_docs_search_tool, ToolConfig(memory_type=MemoryType.NO_MEMORY)),
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
ALL_DEEPSET_TOOLS = set(TOOL_REGISTRY.keys())
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2025-present deepset GmbH <info@deepset.ai>
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from deepset_mcp.tools.tokonomics import RichExplorer
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def create_get_from_object_store(explorer: RichExplorer) -> Callable[..., Any]:
|
|
12
|
+
"""Creates the `get_from_object_store` tool."""
|
|
13
|
+
|
|
14
|
+
def get_from_object_store(object_id: str, path: str = "") -> str:
|
|
15
|
+
"""Use this tool to fetch an object from the object store.
|
|
16
|
+
|
|
17
|
+
You can fetch a specific object by using the object's id (e.g. `@obj_001`).
|
|
18
|
+
You can also fetch any nested path by using the path-parameter
|
|
19
|
+
(e.g. `{"object_id": "@obj_001", "path": "user_info.given_name"}`
|
|
20
|
+
-> returns the content at obj.user_info.given_name).
|
|
21
|
+
|
|
22
|
+
:param object_id: The id of the object to fetch in the format `@obj_001`.
|
|
23
|
+
:param path: The path of the object to fetch in the format of `access.to.attr` or `["access"]["to"]["attr"]`.
|
|
24
|
+
"""
|
|
25
|
+
return explorer.explore(obj_id=object_id, path=path)
|
|
26
|
+
|
|
27
|
+
return get_from_object_store
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def create_get_slice_from_object_store(explorer: RichExplorer) -> Callable[..., Any]:
|
|
31
|
+
"""Creates the `get_slice_from_object_store` tool."""
|
|
32
|
+
|
|
33
|
+
def get_slice_from_object_store(
|
|
34
|
+
object_id: str,
|
|
35
|
+
start: int = 0,
|
|
36
|
+
end: int | None = None,
|
|
37
|
+
path: str = "",
|
|
38
|
+
) -> str:
|
|
39
|
+
"""Extract a slice from a string or list object that is stored in the object store.
|
|
40
|
+
|
|
41
|
+
:param object_id: Identifier of the object.
|
|
42
|
+
:param start: Start index for slicing.
|
|
43
|
+
:param end: End index for slicing (optional - leave empty to get slice from start to end of sequence).
|
|
44
|
+
:param path: Navigation path to object to slice (optional).
|
|
45
|
+
:return: String representation of the slice.
|
|
46
|
+
"""
|
|
47
|
+
return explorer.slice(obj_id=object_id, start=start, end=end, path=path)
|
|
48
|
+
|
|
49
|
+
return get_slice_from_object_store
|
|
@@ -2,70 +2,13 @@
|
|
|
2
2
|
#
|
|
3
3
|
# SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
|
|
5
|
-
"""
|
|
6
|
-
Tokonomics: Explorable and Referenceable Tools for LLM Agents.
|
|
7
|
-
|
|
8
|
-
=============================================================
|
|
9
|
-
|
|
10
|
-
A library that provides token-efficient object exploration and reference
|
|
11
|
-
passing capabilities for LLM agents.
|
|
12
|
-
|
|
13
|
-
Key Features:
|
|
14
|
-
- TTL-based object storage for temporary results
|
|
15
|
-
- Rich object exploration with multiple rendering modes
|
|
16
|
-
- Reference-based parameter passing (@obj_001.path.to.value)
|
|
17
|
-
- Type-safe decorators that preserve function signatures
|
|
18
|
-
- Configurable preview truncation and custom rendering callbacks
|
|
19
|
-
|
|
20
|
-
Usage:
|
|
21
|
-
------
|
|
22
|
-
|
|
23
|
-
Basic explorable tool:
|
|
24
|
-
|
|
25
|
-
>>> from deepset_mcp.tools.tokonomics import explorable
|
|
26
|
-
>>>
|
|
27
|
-
>>> @explorable
|
|
28
|
-
>>> def get_data():
|
|
29
|
-
... return {"users": [{"name": "Alice", "age": 30}]}
|
|
30
|
-
>>>
|
|
31
|
-
>>> result = get_data()
|
|
32
|
-
>>> print(result) # Shows rich preview
|
|
33
|
-
>>> result.obj_id # "obj_001"
|
|
34
|
-
>>> result.value # Original data
|
|
35
|
-
|
|
36
|
-
Referenceable tool that accepts references:
|
|
37
|
-
|
|
38
|
-
>>> from deepset_mcp.tools.tokonomics import referenceable
|
|
39
|
-
>>>
|
|
40
|
-
>>> @referenceable
|
|
41
|
-
>>> def process_users(users: list) -> str:
|
|
42
|
-
... return f"Processed {len(users)} users"
|
|
43
|
-
>>>
|
|
44
|
-
>>> # Use with direct data
|
|
45
|
-
>>> process_users([{"name": "Bob"}])
|
|
46
|
-
>>>
|
|
47
|
-
>>> # Use with reference
|
|
48
|
-
>>> process_users("@obj_001.users")
|
|
49
|
-
|
|
50
|
-
Exploration utilities:
|
|
51
|
-
|
|
52
|
-
>>> from deepset_mcp.tools.tokonomics import explore, search
|
|
53
|
-
>>>
|
|
54
|
-
>>> # Explore object structure
|
|
55
|
-
>>> explore("obj_001", mode="tree")
|
|
56
|
-
>>>
|
|
57
|
-
>>> # Search within objects
|
|
58
|
-
>>> search("obj_001", "Alice")
|
|
59
|
-
"""
|
|
60
|
-
|
|
61
5
|
from .decorators import explorable, explorable_and_referenceable, referenceable
|
|
62
6
|
from .explorer import RichExplorer
|
|
63
|
-
from .object_store import
|
|
7
|
+
from .object_store import InMemoryBackend, ObjectStore
|
|
64
8
|
|
|
65
9
|
__all__ = [
|
|
66
10
|
# Core classes
|
|
67
|
-
"
|
|
68
|
-
"ObjectRef",
|
|
11
|
+
"InMemoryBackend",
|
|
69
12
|
"ObjectStore",
|
|
70
13
|
"RichExplorer",
|
|
71
14
|
# Decorators
|
|
@@ -73,5 +16,3 @@ __all__ = [
|
|
|
73
16
|
"referenceable",
|
|
74
17
|
"explorable_and_referenceable",
|
|
75
18
|
]
|
|
76
|
-
|
|
77
|
-
__version__ = "0.1.0"
|
|
@@ -19,15 +19,15 @@ from typing import Any, TypeVar, Union, get_args, get_origin
|
|
|
19
19
|
|
|
20
20
|
from glom import GlomError, glom
|
|
21
21
|
|
|
22
|
-
from .explorer import RichExplorer
|
|
23
|
-
from .object_store import
|
|
22
|
+
from deepset_mcp.tools.tokonomics.explorer import RichExplorer
|
|
23
|
+
from deepset_mcp.tools.tokonomics.object_store import ObjectStore
|
|
24
24
|
|
|
25
25
|
F = TypeVar("F", bound=Callable[..., Any])
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
def _is_reference(value: Any) -> bool:
|
|
29
29
|
"""Check if a value is a reference string."""
|
|
30
|
-
return isinstance(value, str) and
|
|
30
|
+
return isinstance(value, str) and value.startswith("@") and len(value) > 1
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
def _type_allows_str(annotation: Any) -> bool:
|
|
@@ -49,75 +49,54 @@ def _add_str_to_type(annotation: Any) -> Any:
|
|
|
49
49
|
return annotation | str
|
|
50
50
|
|
|
51
51
|
|
|
52
|
-
def _enhance_docstring_for_references(original: str,
|
|
53
|
-
"""
|
|
52
|
+
def _enhance_docstring_for_references(original: str, func_name: str) -> str:
|
|
53
|
+
"""Create complete docstring for LLM tool with reference support.
|
|
54
54
|
|
|
55
55
|
:param original: Original docstring.
|
|
56
|
-
:param param_info: Parameter modification info.
|
|
57
56
|
:param func_name: Function name for examples.
|
|
58
|
-
:return:
|
|
57
|
+
:return: Complete docstring for LLM tool.
|
|
59
58
|
"""
|
|
60
59
|
if not original:
|
|
61
|
-
original = f"{func_name} function
|
|
60
|
+
original = f"{func_name} function."
|
|
62
61
|
|
|
63
|
-
|
|
64
|
-
ref_section = [
|
|
65
|
-
"",
|
|
66
|
-
"**Reference Support**",
|
|
62
|
+
enhancement = [
|
|
67
63
|
"",
|
|
68
64
|
"All parameters accept object references in the form ``@obj_id`` or ``@obj_id.path.to.value``.",
|
|
69
65
|
"",
|
|
66
|
+
"Examples::",
|
|
67
|
+
"",
|
|
68
|
+
" # Direct call with values",
|
|
69
|
+
f" {func_name}(data={{'key': 'value'}}, threshold=10)",
|
|
70
|
+
"",
|
|
71
|
+
" # Call with references",
|
|
72
|
+
f" {func_name}(data='@obj_123', threshold='@obj_456.config.threshold')",
|
|
73
|
+
"",
|
|
74
|
+
" # Mixed call",
|
|
75
|
+
f" {func_name}(data='@obj_123.items', threshold=10)",
|
|
70
76
|
]
|
|
71
77
|
|
|
72
|
-
|
|
73
|
-
ref_section.append("Parameter types after decoration:")
|
|
74
|
-
ref_section.append("")
|
|
75
|
-
for name, info in param_info.items():
|
|
76
|
-
if info["accepts_str"]:
|
|
77
|
-
ref_section.append(f"- ``{name}``: {info['original']} (already accepts strings)")
|
|
78
|
-
else:
|
|
79
|
-
ref_section.append(f"- ``{name}``: {info['original']} → {info['modified']} (now accepts references)")
|
|
80
|
-
ref_section.append("")
|
|
81
|
-
|
|
82
|
-
ref_section.extend(
|
|
83
|
-
[
|
|
84
|
-
"Examples::",
|
|
85
|
-
"",
|
|
86
|
-
" # Direct call with values",
|
|
87
|
-
f" {func_name}(data={{'key': 'value'}}, threshold=10)",
|
|
88
|
-
"",
|
|
89
|
-
" # Call with references",
|
|
90
|
-
f" {func_name}(data='@obj_001', threshold='@obj_002.config.threshold')",
|
|
91
|
-
"",
|
|
92
|
-
" # Mixed call",
|
|
93
|
-
f" {func_name}(data='@obj_001.items', threshold=10)",
|
|
94
|
-
]
|
|
95
|
-
)
|
|
96
|
-
|
|
97
|
-
return original.rstrip() + "\n" + "\n".join(ref_section)
|
|
78
|
+
return original.rstrip() + "\n" + "\n".join(enhancement)
|
|
98
79
|
|
|
99
80
|
|
|
100
81
|
def _enhance_docstring_for_explorable(original: str, func_name: str) -> str:
|
|
101
|
-
"""
|
|
82
|
+
"""Create complete docstring for LLM tool with output storage.
|
|
102
83
|
|
|
103
84
|
:param original: Original docstring.
|
|
104
85
|
:param func_name: Function name.
|
|
105
|
-
:return:
|
|
86
|
+
:return: Complete docstring for LLM tool.
|
|
106
87
|
"""
|
|
107
88
|
if not original:
|
|
108
|
-
original = f"{func_name} function
|
|
89
|
+
original = f"{func_name} function."
|
|
109
90
|
|
|
110
|
-
|
|
91
|
+
enhancement = [
|
|
111
92
|
"",
|
|
112
|
-
"
|
|
113
|
-
"",
|
|
114
|
-
"
|
|
115
|
-
"
|
|
116
|
-
"",
|
|
117
|
-
"Use the returned object ID to pass this result to other functions that accept references.",
|
|
93
|
+
"The output is automatically stored and can be referenced in other functions.",
|
|
94
|
+
"Returns a formatted preview with an object ID (e.g., ``@obj_123``).",
|
|
95
|
+
"Use the object store tools in combination with the object ID to view nested properties of the object.",
|
|
96
|
+
"Use the returned object ID to pass this result to other functions.",
|
|
118
97
|
]
|
|
119
98
|
|
|
120
|
-
return original.rstrip() + "\n" + "\n".join(
|
|
99
|
+
return original.rstrip() + "\n" + "\n".join(enhancement)
|
|
121
100
|
|
|
122
101
|
|
|
123
102
|
def explorable(
|
|
@@ -141,33 +120,36 @@ def explorable(
|
|
|
141
120
|
... return {"processed": data}
|
|
142
121
|
...
|
|
143
122
|
>>> result = process_data({"input": "value"})
|
|
144
|
-
>>> # result contains a preview and object ID like "@
|
|
123
|
+
>>> # result contains a preview and object ID like "@obj_123"
|
|
145
124
|
"""
|
|
146
125
|
|
|
147
126
|
def decorator(func: F) -> F:
|
|
148
127
|
if inspect.iscoroutinefunction(func):
|
|
149
128
|
|
|
150
129
|
@wraps(func)
|
|
151
|
-
async def async_wrapper(*args: Any, **kwargs: Any) ->
|
|
130
|
+
async def async_wrapper(*args: Any, **kwargs: Any) -> str:
|
|
152
131
|
result = await func(*args, **kwargs)
|
|
153
132
|
obj_id = object_store.put(result)
|
|
154
133
|
preview = explorer.explore(obj_id)
|
|
155
|
-
|
|
134
|
+
|
|
135
|
+
return preview
|
|
156
136
|
|
|
157
137
|
# Enhance docstring
|
|
158
138
|
async_wrapper.__doc__ = _enhance_docstring_for_explorable(func.__doc__ or "", func.__name__)
|
|
139
|
+
async_wrapper.__annotations__["return"] = str
|
|
159
140
|
return async_wrapper # type: ignore[return-value]
|
|
160
141
|
else:
|
|
161
142
|
|
|
162
143
|
@wraps(func)
|
|
163
|
-
def sync_wrapper(*args: Any, **kwargs: Any) ->
|
|
144
|
+
def sync_wrapper(*args: Any, **kwargs: Any) -> str:
|
|
164
145
|
result = func(*args, **kwargs)
|
|
165
146
|
obj_id = object_store.put(result)
|
|
166
147
|
preview = explorer.explore(obj_id)
|
|
167
|
-
return
|
|
148
|
+
return preview
|
|
168
149
|
|
|
169
150
|
# Enhance docstring
|
|
170
151
|
sync_wrapper.__doc__ = _enhance_docstring_for_explorable(func.__doc__ or "", func.__name__)
|
|
152
|
+
sync_wrapper.__annotations__["return"] = str
|
|
171
153
|
return sync_wrapper # type: ignore[return-value]
|
|
172
154
|
|
|
173
155
|
return decorator
|
|
@@ -180,7 +162,7 @@ def referenceable(
|
|
|
180
162
|
) -> Callable[[F], F]:
|
|
181
163
|
"""Decorator factory that enables parameters to accept object references.
|
|
182
164
|
|
|
183
|
-
Parameters can accept reference strings like '@
|
|
165
|
+
Parameters can accept reference strings like '@obj_id' or '@obj_id.path.to.value'
|
|
184
166
|
which are automatically resolved before calling the function.
|
|
185
167
|
|
|
186
168
|
:param object_store: The object store instance to use for lookups.
|
|
@@ -200,27 +182,25 @@ def referenceable(
|
|
|
200
182
|
>>> process_data({"a": 1, "b": 2}, 10)
|
|
201
183
|
>>>
|
|
202
184
|
>>> # Call with references
|
|
203
|
-
>>> process_data("@
|
|
185
|
+
>>> process_data("@obj_123", "@obj_456.config.threshold")
|
|
204
186
|
"""
|
|
205
187
|
|
|
206
188
|
def resolve_reference(ref_str: str) -> Any:
|
|
207
189
|
"""Resolve a reference string to its actual value."""
|
|
208
|
-
|
|
209
|
-
if ref is None:
|
|
210
|
-
raise ValueError(f"Invalid reference format: {ref_str}")
|
|
190
|
+
obj_id, path = explorer.parse_reference(ref_str)
|
|
211
191
|
|
|
212
|
-
obj = object_store.get(
|
|
192
|
+
obj = object_store.get(obj_id)
|
|
213
193
|
if obj is None:
|
|
214
|
-
raise ValueError(f"Object @{
|
|
194
|
+
raise ValueError(f"Object @{obj_id} not found or expired")
|
|
215
195
|
|
|
216
|
-
if
|
|
196
|
+
if path:
|
|
217
197
|
try:
|
|
218
|
-
explorer._validate_path(
|
|
219
|
-
return glom(obj, explorer._parse_path(
|
|
198
|
+
explorer._validate_path(path)
|
|
199
|
+
return glom(obj, explorer._parse_path(path))
|
|
220
200
|
except GlomError as exc:
|
|
221
|
-
raise ValueError(f"Navigation error at {
|
|
201
|
+
raise ValueError(f"Navigation error at {path}: {exc}") from exc
|
|
222
202
|
except ValueError as exc:
|
|
223
|
-
raise ValueError(f"Invalid path {
|
|
203
|
+
raise ValueError(f"Invalid path {path}: {exc}") from exc
|
|
224
204
|
|
|
225
205
|
return obj
|
|
226
206
|
|
|
@@ -289,7 +269,7 @@ def referenceable(
|
|
|
289
269
|
|
|
290
270
|
# Update signature and docstring
|
|
291
271
|
async_wrapper.__signature__ = new_sig # type: ignore[attr-defined]
|
|
292
|
-
async_wrapper.__doc__ = _enhance_docstring_for_references(func.__doc__ or "",
|
|
272
|
+
async_wrapper.__doc__ = _enhance_docstring_for_references(func.__doc__ or "", func.__name__)
|
|
293
273
|
return async_wrapper # type: ignore[return-value]
|
|
294
274
|
else:
|
|
295
275
|
|
|
@@ -332,7 +312,7 @@ def referenceable(
|
|
|
332
312
|
|
|
333
313
|
# Update signature and docstring
|
|
334
314
|
sync_wrapper.__signature__ = new_sig # type: ignore[attr-defined]
|
|
335
|
-
sync_wrapper.__doc__ = _enhance_docstring_for_references(func.__doc__ or "",
|
|
315
|
+
sync_wrapper.__doc__ = _enhance_docstring_for_references(func.__doc__ or "", func.__name__)
|
|
336
316
|
return sync_wrapper # type: ignore[return-value]
|
|
337
317
|
|
|
338
318
|
return decorator
|
|
@@ -362,7 +342,7 @@ def explorable_and_referenceable(
|
|
|
362
342
|
... return {**data1, **data2}
|
|
363
343
|
...
|
|
364
344
|
>>> # Accepts references and returns preview with object ID
|
|
365
|
-
>>> result = merge_data("@
|
|
345
|
+
>>> result = merge_data("@obj_123", {"new": "data"})
|
|
366
346
|
>>> # result contains a preview and can be referenced as "@obj_002"
|
|
367
347
|
"""
|
|
368
348
|
|
|
@@ -374,26 +354,19 @@ def explorable_and_referenceable(
|
|
|
374
354
|
|
|
375
355
|
# Combine docstrings (remove duplicate function name line)
|
|
376
356
|
if ref_func.__doc__ and exp_func.__doc__:
|
|
377
|
-
# Take the reference part from ref_func and explorable part from exp_func
|
|
378
|
-
ref_lines = ref_func.__doc__.split("\n")
|
|
379
357
|
exp_lines = exp_func.__doc__.split("\n")
|
|
380
|
-
|
|
381
|
-
# Find where the reference section starts
|
|
382
|
-
ref_start = next((i for i, line in enumerate(ref_lines) if "**Reference Support**" in line), len(ref_lines))
|
|
383
358
|
# Find where the explorable section starts
|
|
384
|
-
exp_start = next(
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
combined = ref_lines[:ref_end] + exp_lines[exp_start:]
|
|
396
|
-
exp_func.__doc__ = "\n".join(combined)
|
|
359
|
+
exp_start = next(
|
|
360
|
+
(
|
|
361
|
+
i
|
|
362
|
+
for i, line in enumerate(exp_lines)
|
|
363
|
+
if "The output is automatically stored and can be referenced" in line
|
|
364
|
+
),
|
|
365
|
+
0,
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
combined = ref_func.__doc__ + "\n".join(exp_lines[exp_start:])
|
|
369
|
+
exp_func.__doc__ = combined
|
|
397
370
|
|
|
398
371
|
return exp_func
|
|
399
372
|
|