deepset-mcp 0.0.4__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.
@@ -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 Explorable, ObjectRef, ObjectStore
7
+ from .object_store import InMemoryBackend, ObjectStore
64
8
 
65
9
  __all__ = [
66
10
  # Core classes
67
- "Explorable",
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 Explorable, ObjectRef, ObjectStore
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 ObjectRef.parse(value) is not None
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, param_info: dict[str, dict[str, Any]], func_name: str) -> str:
53
- """Add reference documentation to function docstring.
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: Enhanced docstring.
57
+ :return: Complete docstring for LLM tool.
59
58
  """
60
59
  if not original:
61
- original = f"{func_name} function with reference support."
60
+ original = f"{func_name} function."
62
61
 
63
- # Build the reference section
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
- if param_info:
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
- """Add explorable documentation to function docstring.
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: Enhanced docstring.
86
+ :return: Complete docstring for LLM tool.
106
87
  """
107
88
  if not original:
108
- original = f"{func_name} function with stored output."
89
+ original = f"{func_name} function."
109
90
 
110
- section = [
91
+ enhancement = [
111
92
  "",
112
- "**Output Storage**",
113
- "",
114
- "The output of this function is automatically stored and can be referenced in other functions.",
115
- "The function returns a formatted preview of the result along with an object ID (e.g., ``@obj_123``).",
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(section)
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 "@obj_001"
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) -> Explorable[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
- return Explorable(obj_id, result, preview)
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) -> Explorable[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 Explorable(obj_id, result, preview)
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 '@obj_001' or '@obj_001.path.to.value'
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("@obj_001", "@obj_002.config.threshold")
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
- ref = ObjectRef.parse(ref_str)
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(ref.obj_id)
192
+ obj = object_store.get(obj_id)
213
193
  if obj is None:
214
- raise ValueError(f"Object @{ref.obj_id} not found or expired")
194
+ raise ValueError(f"Object @{obj_id} not found or expired")
215
195
 
216
- if ref.path:
196
+ if path:
217
197
  try:
218
- explorer._validate_path(ref.path)
219
- return glom(obj, explorer._parse_path(ref.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 {ref.path}: {exc}") from exc
201
+ raise ValueError(f"Navigation error at {path}: {exc}") from exc
222
202
  except ValueError as exc:
223
- raise ValueError(f"Invalid path {ref.path}: {exc}") from exc
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 "", param_info, func.__name__)
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 "", param_info, func.__name__)
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("@obj_001", {"new": "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((i for i, line in enumerate(exp_lines) if "**Output Storage**" in line), 0)
385
-
386
- # Combine: original + reference section + explorable section
387
- # Take everything from ref_func including reference section but excluding examples
388
- # Find end of reference section (before examples)
389
- ref_end = len(ref_lines)
390
- for i in range(ref_start, len(ref_lines)):
391
- if "Examples::" in ref_lines[i]:
392
- ref_end = i
393
- break
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