deepset-mcp 0.0.6__py3-none-any.whl → 0.0.7__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 +3 -4
- deepset_mcp/api/__init__.py +3 -0
- deepset_mcp/api/client.py +126 -107
- deepset_mcp/api/custom_components/__init__.py +3 -0
- deepset_mcp/api/custom_components/models.py +7 -8
- deepset_mcp/api/custom_components/protocols.py +4 -3
- deepset_mcp/api/custom_components/resource.py +39 -13
- deepset_mcp/api/haystack_service/__init__.py +3 -0
- deepset_mcp/api/haystack_service/protocols.py +21 -0
- deepset_mcp/api/haystack_service/resource.py +46 -0
- deepset_mcp/api/indexes/__init__.py +3 -0
- deepset_mcp/api/indexes/models.py +23 -11
- deepset_mcp/api/indexes/protocols.py +13 -4
- deepset_mcp/api/indexes/resource.py +86 -22
- deepset_mcp/api/integrations/__init__.py +4 -0
- deepset_mcp/api/integrations/models.py +4 -13
- deepset_mcp/api/integrations/protocols.py +3 -3
- deepset_mcp/api/integrations/resource.py +5 -5
- deepset_mcp/api/pipeline/__init__.py +1 -15
- deepset_mcp/api/pipeline/models.py +66 -28
- deepset_mcp/api/pipeline/protocols.py +6 -10
- deepset_mcp/api/pipeline/resource.py +101 -58
- deepset_mcp/api/pipeline_template/__init__.py +3 -0
- deepset_mcp/api/pipeline_template/models.py +12 -23
- deepset_mcp/api/pipeline_template/protocols.py +11 -5
- deepset_mcp/api/pipeline_template/resource.py +51 -39
- deepset_mcp/api/protocols.py +13 -11
- deepset_mcp/api/secrets/__init__.py +3 -0
- deepset_mcp/api/secrets/models.py +2 -8
- deepset_mcp/api/secrets/protocols.py +4 -3
- deepset_mcp/api/secrets/resource.py +32 -7
- deepset_mcp/api/shared_models.py +111 -1
- deepset_mcp/api/transport.py +30 -58
- deepset_mcp/api/user/__init__.py +3 -0
- deepset_mcp/api/workspace/__init__.py +1 -3
- deepset_mcp/api/workspace/models.py +4 -8
- deepset_mcp/api/workspace/protocols.py +3 -3
- deepset_mcp/api/workspace/resource.py +5 -9
- deepset_mcp/main.py +5 -20
- deepset_mcp/mcp/__init__.py +10 -0
- deepset_mcp/{server.py → mcp/server.py} +8 -18
- deepset_mcp/{store.py → mcp/store.py} +3 -3
- deepset_mcp/{tool_factory.py → mcp/tool_factory.py} +20 -37
- deepset_mcp/mcp/tool_models.py +57 -0
- deepset_mcp/{tool_registry.py → mcp/tool_registry.py} +16 -6
- deepset_mcp/{tools/tokonomics → tokonomics}/__init__.py +3 -1
- deepset_mcp/{tools/tokonomics → tokonomics}/decorators.py +2 -2
- deepset_mcp/{tools/tokonomics → tokonomics}/explorer.py +1 -1
- deepset_mcp/tools/__init__.py +58 -0
- deepset_mcp/tools/custom_components.py +7 -4
- deepset_mcp/tools/haystack_service.py +64 -22
- deepset_mcp/tools/haystack_service_models.py +40 -0
- deepset_mcp/tools/indexes.py +131 -32
- deepset_mcp/tools/object_store.py +1 -1
- deepset_mcp/tools/pipeline.py +40 -10
- deepset_mcp/tools/pipeline_template.py +35 -18
- deepset_mcp/tools/secrets.py +29 -13
- deepset_mcp/tools/workspace.py +2 -2
- deepset_mcp-0.0.7.dist-info/METADATA +100 -0
- deepset_mcp-0.0.7.dist-info/RECORD +74 -0
- deepset_mcp/api/README.md +0 -536
- deepset_mcp/api/pipeline/log_level.py +0 -13
- deepset_mcp/tool_models.py +0 -42
- deepset_mcp-0.0.6.dist-info/METADATA +0 -807
- deepset_mcp-0.0.6.dist-info/RECORD +0 -75
- /deepset_mcp/{tools/tokonomics → tokonomics}/object_store.py +0 -0
- {deepset_mcp-0.0.6.dist-info → deepset_mcp-0.0.7.dist-info}/WHEEL +0 -0
- {deepset_mcp-0.0.6.dist-info → deepset_mcp-0.0.7.dist-info}/entry_points.txt +0 -0
- {deepset_mcp-0.0.6.dist-info → deepset_mcp-0.0.7.dist-info}/licenses/LICENSE +0 -0
|
@@ -6,20 +6,20 @@ import json
|
|
|
6
6
|
import logging
|
|
7
7
|
from collections.abc import AsyncIterator
|
|
8
8
|
from typing import TYPE_CHECKING, Any
|
|
9
|
+
from urllib.parse import quote
|
|
9
10
|
|
|
10
11
|
from deepset_mcp.api.exceptions import UnexpectedAPIError
|
|
11
|
-
from deepset_mcp.api.pipeline.log_level import LogLevel
|
|
12
12
|
from deepset_mcp.api.pipeline.models import (
|
|
13
13
|
DeepsetPipeline,
|
|
14
14
|
DeepsetSearchResponse,
|
|
15
15
|
DeepsetStreamEvent,
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
LogLevel,
|
|
17
|
+
PipelineLog,
|
|
18
18
|
PipelineValidationResult,
|
|
19
19
|
ValidationError,
|
|
20
20
|
)
|
|
21
21
|
from deepset_mcp.api.pipeline.protocols import PipelineResourceProtocol
|
|
22
|
-
from deepset_mcp.api.shared_models import NoContentResponse
|
|
22
|
+
from deepset_mcp.api.shared_models import NoContentResponse, PaginatedResponse
|
|
23
23
|
from deepset_mcp.api.transport import raise_for_status
|
|
24
24
|
|
|
25
25
|
logger = logging.getLogger(__name__)
|
|
@@ -29,7 +29,7 @@ if TYPE_CHECKING:
|
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
class PipelineResource(PipelineResourceProtocol):
|
|
32
|
-
"""
|
|
32
|
+
"""Interact with pipelines on the deepset AI platform."""
|
|
33
33
|
|
|
34
34
|
def __init__(
|
|
35
35
|
self,
|
|
@@ -54,7 +54,7 @@ class PipelineResource(PipelineResourceProtocol):
|
|
|
54
54
|
data = {"query_yaml": yaml_config}
|
|
55
55
|
|
|
56
56
|
resp = await self._client.request(
|
|
57
|
-
endpoint=f"v1/workspaces/{self._workspace}/pipeline_validations",
|
|
57
|
+
endpoint=f"v1/workspaces/{quote(self._workspace, safe='')}/pipeline_validations",
|
|
58
58
|
method="POST",
|
|
59
59
|
data=data,
|
|
60
60
|
)
|
|
@@ -75,41 +75,45 @@ class PipelineResource(PipelineResourceProtocol):
|
|
|
75
75
|
|
|
76
76
|
raise UnexpectedAPIError(status_code=resp.status_code, message=resp.text, detail=resp.json)
|
|
77
77
|
|
|
78
|
-
async def list(
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
:
|
|
86
|
-
:param limit: Max number of items to return.
|
|
87
|
-
:returns: PipelineList with pipelines and metadata.
|
|
78
|
+
async def list(self, limit: int = 10, after: str | None = None) -> PaginatedResponse[DeepsetPipeline]:
|
|
79
|
+
"""Lists pipelines and returns the first page of results.
|
|
80
|
+
|
|
81
|
+
The returned object can be iterated over to fetch subsequent pages.
|
|
82
|
+
|
|
83
|
+
:param limit: The maximum number of pipelines to return per page.
|
|
84
|
+
:param after: The cursor to fetch the next page of results.
|
|
85
|
+
:returns: A `PaginatedResponse` object containing the first page of pipelines.
|
|
88
86
|
"""
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
87
|
+
# 1. Prepare arguments for the initial API call
|
|
88
|
+
# TODO: Pagination in the deepset API is currently implemented in an unintuitive way.
|
|
89
|
+
# TODO: The cursor is always time based (created_at) and after signifies pipelines older than the current cursor
|
|
90
|
+
# TODO: while 'before' signals pipelines younger than the current cursor.
|
|
91
|
+
# TODO: This is applied irrespective of any sort (e.g. name) that would conflict with this approach.
|
|
92
|
+
# TODO: Change this to 'after' once the behaviour is fixed on the deepset API
|
|
93
|
+
request_params = {"limit": limit, "before": after}
|
|
94
|
+
request_params = {k: v for k, v in request_params.items() if v is not None}
|
|
95
|
+
|
|
96
|
+
# 2. Make the first API call using a private, stateless method
|
|
97
|
+
page = await self._list_api_call(**request_params)
|
|
98
|
+
|
|
99
|
+
# 3. Inject the logic needed for subsequent fetches into the response object
|
|
100
|
+
page._inject_paginator(
|
|
101
|
+
fetch_func=self._list_api_call,
|
|
102
|
+
# Base args for the *next* fetch don't include initial cursors
|
|
103
|
+
base_args={"limit": limit},
|
|
104
|
+
)
|
|
105
|
+
return page
|
|
93
106
|
|
|
107
|
+
async def _list_api_call(self, **kwargs: Any) -> PaginatedResponse[DeepsetPipeline]:
|
|
108
|
+
"""A private, stateless method that performs the raw API call."""
|
|
94
109
|
resp = await self._client.request(
|
|
95
|
-
endpoint=f"v1/workspaces/{self._workspace}/pipelines",
|
|
96
|
-
method="GET",
|
|
97
|
-
params=params,
|
|
110
|
+
endpoint=f"v1/workspaces/{quote(self._workspace, safe='')}/pipelines", method="GET", params=kwargs
|
|
98
111
|
)
|
|
99
|
-
|
|
100
112
|
raise_for_status(resp)
|
|
113
|
+
if resp.json is None:
|
|
114
|
+
raise UnexpectedAPIError(status_code=resp.status_code, message="Empty response", detail=None)
|
|
101
115
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
if response is not None:
|
|
105
|
-
pipelines = [DeepsetPipeline.model_validate(item) for item in response.get("data", [])]
|
|
106
|
-
return PipelineList(
|
|
107
|
-
data=pipelines,
|
|
108
|
-
has_more=response.get("has_more", False),
|
|
109
|
-
total=response.get("total", len(pipelines)),
|
|
110
|
-
)
|
|
111
|
-
else:
|
|
112
|
-
return PipelineList(data=[], has_more=False, total=0)
|
|
116
|
+
return PaginatedResponse[DeepsetPipeline].create_with_cursor_field(resp.json, "pipeline_id")
|
|
113
117
|
|
|
114
118
|
async def get(self, pipeline_name: str, include_yaml: bool = True) -> DeepsetPipeline:
|
|
115
119
|
"""Fetch a single pipeline by its name.
|
|
@@ -118,14 +122,18 @@ class PipelineResource(PipelineResourceProtocol):
|
|
|
118
122
|
:param include_yaml: Whether to include YAML configuration in the response.
|
|
119
123
|
:returns: DeepsetPipeline instance.
|
|
120
124
|
"""
|
|
121
|
-
resp = await self._client.request(
|
|
125
|
+
resp = await self._client.request(
|
|
126
|
+
endpoint=f"v1/workspaces/{quote(self._workspace, safe='')}/pipelines/{quote(pipeline_name, safe='')}"
|
|
127
|
+
)
|
|
122
128
|
raise_for_status(resp)
|
|
123
129
|
|
|
124
130
|
pipeline = DeepsetPipeline.model_validate(resp.json)
|
|
125
131
|
|
|
126
132
|
if include_yaml:
|
|
127
133
|
yaml_response = await self._client.request(
|
|
128
|
-
endpoint=
|
|
134
|
+
endpoint=(
|
|
135
|
+
f"v1/workspaces/{quote(self._workspace, safe='')}/pipelines/{quote(pipeline_name, safe='')}/yaml"
|
|
136
|
+
)
|
|
129
137
|
)
|
|
130
138
|
|
|
131
139
|
raise_for_status(yaml_response)
|
|
@@ -135,16 +143,16 @@ class PipelineResource(PipelineResourceProtocol):
|
|
|
135
143
|
|
|
136
144
|
return pipeline
|
|
137
145
|
|
|
138
|
-
async def create(self,
|
|
146
|
+
async def create(self, pipeline_name: str, yaml_config: str) -> NoContentResponse:
|
|
139
147
|
"""Create a new pipeline with a name and YAML config.
|
|
140
148
|
|
|
141
|
-
:param
|
|
149
|
+
:param pipeline_name: Name of the new pipeline.
|
|
142
150
|
:param yaml_config: YAML configuration for the pipeline.
|
|
143
151
|
:returns: NoContentResponse indicating successful creation.
|
|
144
152
|
"""
|
|
145
|
-
data = {"name":
|
|
153
|
+
data = {"name": pipeline_name, "query_yaml": yaml_config}
|
|
146
154
|
resp = await self._client.request(
|
|
147
|
-
endpoint=f"v1/workspaces/{self._workspace}/pipelines",
|
|
155
|
+
endpoint=f"v1/workspaces/{quote(self._workspace, safe='')}/pipelines",
|
|
148
156
|
method="POST",
|
|
149
157
|
data=data,
|
|
150
158
|
)
|
|
@@ -170,7 +178,7 @@ class PipelineResource(PipelineResourceProtocol):
|
|
|
170
178
|
# Handle name update first if any
|
|
171
179
|
if updated_pipeline_name is not None:
|
|
172
180
|
name_resp = await self._client.request(
|
|
173
|
-
endpoint=f"v1/workspaces/{self._workspace}/pipelines/{pipeline_name}",
|
|
181
|
+
endpoint=f"v1/workspaces/{quote(self._workspace, safe='')}/pipelines/{quote(pipeline_name, safe='')}",
|
|
174
182
|
method="PATCH",
|
|
175
183
|
data={"name": updated_pipeline_name},
|
|
176
184
|
)
|
|
@@ -184,7 +192,9 @@ class PipelineResource(PipelineResourceProtocol):
|
|
|
184
192
|
|
|
185
193
|
if yaml_config is not None:
|
|
186
194
|
yaml_resp = await self._client.request(
|
|
187
|
-
endpoint=
|
|
195
|
+
endpoint=(
|
|
196
|
+
f"v1/workspaces/{quote(self._workspace, safe='')}/pipelines/{quote(pipeline_name, safe='')}/yaml"
|
|
197
|
+
),
|
|
188
198
|
method="PUT",
|
|
189
199
|
data={"query_yaml": yaml_config},
|
|
190
200
|
)
|
|
@@ -205,36 +215,62 @@ class PipelineResource(PipelineResourceProtocol):
|
|
|
205
215
|
pipeline_name: str,
|
|
206
216
|
limit: int = 30,
|
|
207
217
|
level: LogLevel | None = None,
|
|
208
|
-
|
|
209
|
-
|
|
218
|
+
after: str | None = None,
|
|
219
|
+
) -> PaginatedResponse[PipelineLog]:
|
|
220
|
+
"""Fetch logs for a specific pipeline and returns the first page of results.
|
|
221
|
+
|
|
222
|
+
The returned object can be iterated over to fetch subsequent pages.
|
|
210
223
|
|
|
211
224
|
:param pipeline_name: Name of the pipeline to fetch logs for.
|
|
212
|
-
:param limit: Maximum number of log entries to return.
|
|
225
|
+
:param limit: Maximum number of log entries to return per page.
|
|
213
226
|
:param level: Filter logs by level. If None, returns all levels.
|
|
214
|
-
:
|
|
227
|
+
:param after: The cursor to fetch the next page of results.
|
|
228
|
+
:returns: A `PaginatedResponse` object containing the first page of logs.
|
|
215
229
|
"""
|
|
216
|
-
|
|
230
|
+
# 1. Prepare arguments for the initial API call
|
|
231
|
+
request_params = {
|
|
217
232
|
"limit": limit,
|
|
218
233
|
"filter": "origin eq 'querypipeline'",
|
|
219
234
|
}
|
|
220
235
|
|
|
221
236
|
# Add level filter if specified
|
|
222
237
|
if level is not None:
|
|
223
|
-
|
|
238
|
+
request_params["filter"] = f"level eq '{level}' and origin eq 'querypipeline'"
|
|
239
|
+
|
|
240
|
+
# Add cursor if provided
|
|
241
|
+
if after is not None:
|
|
242
|
+
request_params["after"] = after
|
|
243
|
+
|
|
244
|
+
# Remove None values
|
|
245
|
+
request_params = {k: v for k, v in request_params.items() if v is not None}
|
|
246
|
+
|
|
247
|
+
# 2. Make the first API call using a private, stateless method
|
|
248
|
+
page = await self._get_logs_api_call(pipeline_name, **request_params)
|
|
249
|
+
|
|
250
|
+
# 3. Inject the logic needed for subsequent fetches into the response object
|
|
251
|
+
page._inject_paginator(
|
|
252
|
+
fetch_func=lambda **kwargs: self._get_logs_api_call(pipeline_name, **kwargs),
|
|
253
|
+
# Base args for the *next* fetch don't include initial cursors
|
|
254
|
+
base_args={"limit": limit, "filter": request_params["filter"]},
|
|
255
|
+
cursor_param="after", # Logs use 'after' cursor, not 'before' like pipelines
|
|
256
|
+
)
|
|
257
|
+
return page
|
|
224
258
|
|
|
259
|
+
async def _get_logs_api_call(self, pipeline_name: str, **kwargs: Any) -> PaginatedResponse[PipelineLog]:
|
|
260
|
+
"""A private, stateless method that performs the raw API call for logs."""
|
|
225
261
|
resp = await self._client.request(
|
|
226
|
-
endpoint=f"v1/workspaces/{self._workspace}/pipelines/{pipeline_name}/logs",
|
|
262
|
+
endpoint=f"v1/workspaces/{quote(self._workspace, safe='')}/pipelines/{quote(pipeline_name, safe='')}/logs",
|
|
227
263
|
method="GET",
|
|
228
|
-
params=
|
|
264
|
+
params=kwargs,
|
|
229
265
|
)
|
|
230
266
|
|
|
231
267
|
raise_for_status(resp)
|
|
232
268
|
|
|
233
269
|
if resp.json is not None:
|
|
234
|
-
return
|
|
270
|
+
return PaginatedResponse[PipelineLog].create_with_cursor_field(resp.json, "logged_at")
|
|
235
271
|
else:
|
|
236
|
-
# Return empty
|
|
237
|
-
return
|
|
272
|
+
# Return empty paginated response if no JSON data
|
|
273
|
+
return PaginatedResponse[PipelineLog](data=[], has_more=False, total=0)
|
|
238
274
|
|
|
239
275
|
async def deploy(self, pipeline_name: str) -> PipelineValidationResult:
|
|
240
276
|
"""Deploy a pipeline to production.
|
|
@@ -244,7 +280,9 @@ class PipelineResource(PipelineResourceProtocol):
|
|
|
244
280
|
:raises UnexpectedAPIError: If the API returns an unexpected status code.
|
|
245
281
|
"""
|
|
246
282
|
resp = await self._client.request(
|
|
247
|
-
endpoint=
|
|
283
|
+
endpoint=(
|
|
284
|
+
f"v1/workspaces/{quote(self._workspace, safe='')}/pipelines/{quote(pipeline_name, safe='')}/deploy"
|
|
285
|
+
),
|
|
248
286
|
method="POST",
|
|
249
287
|
)
|
|
250
288
|
|
|
@@ -274,7 +312,7 @@ class PipelineResource(PipelineResourceProtocol):
|
|
|
274
312
|
:raises UnexpectedAPIError: If the API returns an unexpected status code.
|
|
275
313
|
"""
|
|
276
314
|
resp = await self._client.request(
|
|
277
|
-
endpoint=f"v1/workspaces/{self._workspace}/pipelines/{pipeline_name}",
|
|
315
|
+
endpoint=f"v1/workspaces/{quote(self._workspace, safe='')}/pipelines/{quote(pipeline_name, safe='')}",
|
|
278
316
|
method="DELETE",
|
|
279
317
|
)
|
|
280
318
|
|
|
@@ -315,7 +353,9 @@ class PipelineResource(PipelineResourceProtocol):
|
|
|
315
353
|
data["filters"] = filters
|
|
316
354
|
|
|
317
355
|
resp = await self._client.request(
|
|
318
|
-
endpoint=
|
|
356
|
+
endpoint=(
|
|
357
|
+
f"v1/workspaces/{quote(self._workspace, safe='')}/pipelines/{quote(pipeline_name, safe='')}/search"
|
|
358
|
+
),
|
|
319
359
|
method="POST",
|
|
320
360
|
data=data,
|
|
321
361
|
response_type=dict[str, Any],
|
|
@@ -365,7 +405,10 @@ class PipelineResource(PipelineResourceProtocol):
|
|
|
365
405
|
data["filters"] = filters
|
|
366
406
|
|
|
367
407
|
async with self._client.stream_request(
|
|
368
|
-
endpoint=
|
|
408
|
+
endpoint=(
|
|
409
|
+
f"v1/workspaces/{quote(self._workspace, safe='')}/pipelines/"
|
|
410
|
+
f"{quote(pipeline_name, safe='')}/search-stream"
|
|
411
|
+
),
|
|
369
412
|
method="POST",
|
|
370
413
|
data=data,
|
|
371
414
|
) as resp:
|
|
@@ -20,22 +20,34 @@ class PipelineTemplateTag(BaseModel):
|
|
|
20
20
|
"""Model representing a tag on a pipeline template."""
|
|
21
21
|
|
|
22
22
|
name: str
|
|
23
|
+
"Human-readable name of the tag"
|
|
23
24
|
tag_id: UUID
|
|
25
|
+
"Unique identifier for the tag"
|
|
24
26
|
|
|
25
27
|
|
|
26
28
|
class PipelineTemplate(BaseModel):
|
|
27
29
|
"""Model representing a pipeline template."""
|
|
28
30
|
|
|
29
31
|
author: str
|
|
32
|
+
"Name of the template author or creator"
|
|
30
33
|
best_for: list[str]
|
|
34
|
+
"List of use cases this template is best suited for"
|
|
31
35
|
description: str
|
|
36
|
+
"Detailed description of the pipeline template"
|
|
32
37
|
template_name: str = Field(alias="pipeline_name")
|
|
38
|
+
"Internal name identifier for the template"
|
|
33
39
|
display_name: str = Field(alias="name")
|
|
40
|
+
"User-friendly display name for the template"
|
|
34
41
|
pipeline_template_id: UUID = Field(alias="pipeline_template_id")
|
|
42
|
+
"Unique identifier for the pipeline template"
|
|
35
43
|
potential_applications: list[str] = Field(alias="potential_applications")
|
|
44
|
+
"List of potential applications and scenarios for this template"
|
|
36
45
|
yaml_config: str | None = None
|
|
46
|
+
"YAML configuration defining the pipeline structure"
|
|
37
47
|
tags: list[PipelineTemplateTag]
|
|
48
|
+
"List of tags associated with the template for categorization"
|
|
38
49
|
pipeline_type: PipelineType
|
|
50
|
+
"Type of pipeline (query or indexing)"
|
|
39
51
|
|
|
40
52
|
@model_validator(mode="before")
|
|
41
53
|
@classmethod
|
|
@@ -62,26 +74,3 @@ class PipelineTemplate(BaseModel):
|
|
|
62
74
|
values["yaml_config"] = yaml_config
|
|
63
75
|
|
|
64
76
|
return values
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
class PipelineTemplateList(BaseModel):
|
|
68
|
-
"""Response model for listing pipeline templates."""
|
|
69
|
-
|
|
70
|
-
data: list[PipelineTemplate]
|
|
71
|
-
has_more: bool
|
|
72
|
-
total: int
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
class PipelineTemplateSearchResult(BaseModel):
|
|
76
|
-
"""Model representing a search result for pipeline templates."""
|
|
77
|
-
|
|
78
|
-
template: PipelineTemplate
|
|
79
|
-
similarity_score: float
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
class PipelineTemplateSearchResults(BaseModel):
|
|
83
|
-
"""Response model for pipeline template search results."""
|
|
84
|
-
|
|
85
|
-
results: list[PipelineTemplateSearchResult]
|
|
86
|
-
query: str
|
|
87
|
-
total_found: int
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
from typing import Protocol
|
|
6
6
|
|
|
7
|
-
from deepset_mcp.api.pipeline_template.models import PipelineTemplate
|
|
7
|
+
from deepset_mcp.api.pipeline_template.models import PipelineTemplate
|
|
8
|
+
from deepset_mcp.api.shared_models import PaginatedResponse
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
class PipelineTemplateResourceProtocol(Protocol):
|
|
@@ -14,8 +15,13 @@ class PipelineTemplateResourceProtocol(Protocol):
|
|
|
14
15
|
"""Fetch a single pipeline template by its name."""
|
|
15
16
|
...
|
|
16
17
|
|
|
17
|
-
async def
|
|
18
|
-
self,
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
async def list(
|
|
19
|
+
self,
|
|
20
|
+
limit: int = 10,
|
|
21
|
+
after: str | None = None,
|
|
22
|
+
field: str = "created_at",
|
|
23
|
+
order: str = "DESC",
|
|
24
|
+
filter: str | None = None,
|
|
25
|
+
) -> PaginatedResponse[PipelineTemplate]:
|
|
26
|
+
"""Lists pipeline templates and returns the first page of results with pagination support."""
|
|
21
27
|
...
|
|
@@ -5,9 +5,10 @@
|
|
|
5
5
|
from typing import Any
|
|
6
6
|
|
|
7
7
|
from deepset_mcp.api.exceptions import UnexpectedAPIError
|
|
8
|
-
from deepset_mcp.api.pipeline_template.models import PipelineTemplate
|
|
8
|
+
from deepset_mcp.api.pipeline_template.models import PipelineTemplate
|
|
9
9
|
from deepset_mcp.api.pipeline_template.protocols import PipelineTemplateResourceProtocol
|
|
10
10
|
from deepset_mcp.api.protocols import AsyncClientProtocol
|
|
11
|
+
from deepset_mcp.api.shared_models import PaginatedResponse
|
|
11
12
|
from deepset_mcp.api.transport import raise_for_status
|
|
12
13
|
|
|
13
14
|
|
|
@@ -46,47 +47,58 @@ class PipelineTemplateResource(PipelineTemplateResourceProtocol):
|
|
|
46
47
|
|
|
47
48
|
return PipelineTemplate.model_validate(data)
|
|
48
49
|
|
|
49
|
-
async def
|
|
50
|
-
self,
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
PipelineTemplateList
|
|
68
|
-
List of pipeline templates with metadata
|
|
50
|
+
async def list(
|
|
51
|
+
self,
|
|
52
|
+
limit: int = 10,
|
|
53
|
+
after: str | None = None,
|
|
54
|
+
field: str = "created_at",
|
|
55
|
+
order: str = "DESC",
|
|
56
|
+
filter: str | None = None,
|
|
57
|
+
) -> PaginatedResponse[PipelineTemplate]:
|
|
58
|
+
"""Lists pipeline templates and returns the first page of results.
|
|
59
|
+
|
|
60
|
+
The returned object can be iterated over to fetch subsequent pages.
|
|
61
|
+
|
|
62
|
+
:param limit: The maximum number of pipeline templates to return per page.
|
|
63
|
+
:param after: The cursor to fetch the next page of results.
|
|
64
|
+
:param field: Field to sort by (default: "created_at").
|
|
65
|
+
:param order: Sort order, either "ASC" or "DESC" (default: "DESC").
|
|
66
|
+
:param filter: OData filter expression for filtering templates.
|
|
67
|
+
:returns: A `PaginatedResponse` object containing the first page of pipeline templates.
|
|
69
68
|
"""
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
# TODO: Remove when fixed
|
|
70
|
+
if after is not None:
|
|
71
|
+
raise ValueError("Pagination using 'after' parameter is currently not supported by the deepset platform.")
|
|
72
|
+
|
|
73
|
+
# 1. Prepare arguments for the initial API call
|
|
74
|
+
# TODO: Pagination in the deepset API is currently implemented in an unintuitive way.
|
|
75
|
+
# TODO: The cursor is always time based (created_at) and after signifies templates older than the current cursor
|
|
76
|
+
# TODO: while 'before' signals templates younger than the current cursor.
|
|
77
|
+
# TODO: This is applied irrespective of any sort (e.g. name) that would conflict with this approach.
|
|
78
|
+
# TODO: Change this to 'after' once the behaviour is fixed on the deepset API
|
|
79
|
+
request_params = {"limit": limit, "before": after, "field": field, "order": order}
|
|
72
80
|
if filter is not None:
|
|
73
|
-
|
|
81
|
+
request_params["filter"] = filter
|
|
82
|
+
request_params = {k: v for k, v in request_params.items() if v is not None}
|
|
74
83
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
method="GET",
|
|
78
|
-
params=params,
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
raise_for_status(response)
|
|
84
|
+
# 2. Make the first API call using a private, stateless method
|
|
85
|
+
page = await self._list_api_call(**request_params)
|
|
82
86
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
+
# 3. Inject the logic needed for subsequent fetches into the response object
|
|
88
|
+
page._inject_paginator(
|
|
89
|
+
fetch_func=self._list_api_call,
|
|
90
|
+
# Base args for the *next* fetch don't include initial cursors
|
|
91
|
+
base_args={"limit": limit, "field": field, "order": order, "filter": filter},
|
|
92
|
+
)
|
|
93
|
+
return page
|
|
87
94
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
95
|
+
async def _list_api_call(self, **kwargs: Any) -> PaginatedResponse[PipelineTemplate]:
|
|
96
|
+
"""A private, stateless method that performs the raw API call."""
|
|
97
|
+
resp = await self._client.request(
|
|
98
|
+
endpoint=f"v1/workspaces/{self._workspace}/pipeline_templates", method="GET", params=kwargs
|
|
92
99
|
)
|
|
100
|
+
raise_for_status(resp)
|
|
101
|
+
if resp.json is None:
|
|
102
|
+
raise UnexpectedAPIError(status_code=resp.status_code, message="Empty response", detail=None)
|
|
103
|
+
|
|
104
|
+
return PaginatedResponse[PipelineTemplate].create_with_cursor_field(resp.json, "name")
|
deepset_mcp/api/protocols.py
CHANGED
|
@@ -4,18 +4,20 @@
|
|
|
4
4
|
|
|
5
5
|
from contextlib import AbstractAsyncContextManager
|
|
6
6
|
from types import TracebackType
|
|
7
|
-
from typing import Any, Literal, Protocol, Self, TypeVar, overload
|
|
8
|
-
|
|
9
|
-
from deepset_mcp.api.custom_components.protocols import CustomComponentsProtocol
|
|
10
|
-
from deepset_mcp.api.haystack_service.protocols import HaystackServiceProtocol
|
|
11
|
-
from deepset_mcp.api.indexes.protocols import IndexResourceProtocol
|
|
12
|
-
from deepset_mcp.api.integrations.protocols import IntegrationResourceProtocol
|
|
13
|
-
from deepset_mcp.api.pipeline.protocols import PipelineResourceProtocol
|
|
14
|
-
from deepset_mcp.api.pipeline_template.protocols import PipelineTemplateResourceProtocol
|
|
15
|
-
from deepset_mcp.api.secrets.protocols import SecretResourceProtocol
|
|
7
|
+
from typing import TYPE_CHECKING, Any, Literal, Protocol, Self, TypeVar, overload
|
|
8
|
+
|
|
16
9
|
from deepset_mcp.api.transport import StreamingResponse, TransportResponse
|
|
17
|
-
|
|
18
|
-
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from deepset_mcp.api.custom_components.protocols import CustomComponentsProtocol
|
|
13
|
+
from deepset_mcp.api.haystack_service.protocols import HaystackServiceProtocol
|
|
14
|
+
from deepset_mcp.api.indexes.protocols import IndexResourceProtocol
|
|
15
|
+
from deepset_mcp.api.integrations.protocols import IntegrationResourceProtocol
|
|
16
|
+
from deepset_mcp.api.pipeline.protocols import PipelineResourceProtocol
|
|
17
|
+
from deepset_mcp.api.pipeline_template.protocols import PipelineTemplateResourceProtocol
|
|
18
|
+
from deepset_mcp.api.secrets.protocols import SecretResourceProtocol
|
|
19
|
+
from deepset_mcp.api.user.protocols import UserResourceProtocol
|
|
20
|
+
from deepset_mcp.api.workspace.protocols import WorkspaceResourceProtocol
|
|
19
21
|
|
|
20
22
|
T = TypeVar("T")
|
|
21
23
|
|
|
@@ -9,12 +9,6 @@ class Secret(BaseModel):
|
|
|
9
9
|
"""Model representing a secret in deepset."""
|
|
10
10
|
|
|
11
11
|
name: str
|
|
12
|
+
"Human-readable name of the secret"
|
|
12
13
|
secret_id: str
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class SecretList(BaseModel):
|
|
16
|
-
"""Model representing a list of secrets with pagination."""
|
|
17
|
-
|
|
18
|
-
data: list[Secret]
|
|
19
|
-
has_more: bool
|
|
20
|
-
total: int
|
|
14
|
+
"Unique identifier for the secret"
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
from typing import Protocol
|
|
6
6
|
|
|
7
|
-
from deepset_mcp.api.secrets.models import Secret
|
|
8
|
-
from deepset_mcp.api.shared_models import NoContentResponse
|
|
7
|
+
from deepset_mcp.api.secrets.models import Secret
|
|
8
|
+
from deepset_mcp.api.shared_models import NoContentResponse, PaginatedResponse
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class SecretResourceProtocol(Protocol):
|
|
@@ -16,7 +16,8 @@ class SecretResourceProtocol(Protocol):
|
|
|
16
16
|
limit: int = 10,
|
|
17
17
|
field: str = "created_at",
|
|
18
18
|
order: str = "DESC",
|
|
19
|
-
|
|
19
|
+
after: str | None = None,
|
|
20
|
+
) -> PaginatedResponse[Secret]:
|
|
20
21
|
"""List secrets with pagination."""
|
|
21
22
|
...
|
|
22
23
|
|
|
@@ -6,9 +6,9 @@ from typing import Any
|
|
|
6
6
|
|
|
7
7
|
from deepset_mcp.api.exceptions import ResourceNotFoundError
|
|
8
8
|
from deepset_mcp.api.protocols import AsyncClientProtocol
|
|
9
|
-
from deepset_mcp.api.secrets.models import Secret
|
|
9
|
+
from deepset_mcp.api.secrets.models import Secret
|
|
10
10
|
from deepset_mcp.api.secrets.protocols import SecretResourceProtocol
|
|
11
|
-
from deepset_mcp.api.shared_models import NoContentResponse
|
|
11
|
+
from deepset_mcp.api.shared_models import NoContentResponse, PaginatedResponse
|
|
12
12
|
from deepset_mcp.api.transport import raise_for_status
|
|
13
13
|
|
|
14
14
|
|
|
@@ -27,26 +27,51 @@ class SecretResource(SecretResourceProtocol):
|
|
|
27
27
|
limit: int = 10,
|
|
28
28
|
field: str = "created_at",
|
|
29
29
|
order: str = "DESC",
|
|
30
|
-
|
|
30
|
+
after: str | None = None,
|
|
31
|
+
) -> PaginatedResponse[Secret]:
|
|
31
32
|
"""List secrets with pagination.
|
|
32
33
|
|
|
34
|
+
The returned object can be iterated over to fetch subsequent pages.
|
|
35
|
+
|
|
33
36
|
:param limit: Maximum number of secrets to return.
|
|
34
37
|
:param field: Field to sort by.
|
|
35
38
|
:param order: Sort order (ASC or DESC).
|
|
39
|
+
:param after: The cursor to fetch the next page of results.
|
|
36
40
|
|
|
37
|
-
:returns:
|
|
41
|
+
:returns: A `PaginatedResponse` object containing the first page of secrets.
|
|
38
42
|
"""
|
|
39
|
-
|
|
43
|
+
# 1. Prepare arguments for the initial API call
|
|
44
|
+
# TODO: Pagination in the deepset API is currently implemented in an unintuitive way.
|
|
45
|
+
# TODO: The cursor is always time based (created_at) and after signifies secrets older than the current cursor
|
|
46
|
+
# TODO: while 'before' signals secrets younger than the current cursor.
|
|
47
|
+
# TODO: This is applied irrespective of any sort (e.g. name) that would conflict with this approach.
|
|
48
|
+
# TODO: Change this to 'after' once the behaviour is fixed on the deepset API
|
|
49
|
+
request_params = {
|
|
40
50
|
"limit": str(limit),
|
|
41
51
|
"field": field,
|
|
42
52
|
"order": order,
|
|
53
|
+
"before": after,
|
|
43
54
|
}
|
|
55
|
+
request_params = {k: v for k, v in request_params.items() if v is not None}
|
|
56
|
+
|
|
57
|
+
# 2. Make the first API call using a private, stateless method
|
|
58
|
+
page = await self._list_api_call(**request_params)
|
|
59
|
+
|
|
60
|
+
# 3. Inject the logic needed for subsequent fetches into the response object
|
|
61
|
+
page._inject_paginator(
|
|
62
|
+
fetch_func=self._list_api_call,
|
|
63
|
+
# Base args for the *next* fetch don't include initial cursors
|
|
64
|
+
base_args={"limit": str(limit), "field": field, "order": order},
|
|
65
|
+
)
|
|
66
|
+
return page
|
|
44
67
|
|
|
68
|
+
async def _list_api_call(self, **kwargs: Any) -> PaginatedResponse[Secret]:
|
|
69
|
+
"""A private, stateless method that performs the raw API call."""
|
|
45
70
|
resp = await self._client.request(
|
|
46
71
|
endpoint="v2/secrets",
|
|
47
72
|
method="GET",
|
|
48
73
|
response_type=dict[str, Any],
|
|
49
|
-
params=
|
|
74
|
+
params=kwargs,
|
|
50
75
|
)
|
|
51
76
|
|
|
52
77
|
raise_for_status(resp)
|
|
@@ -54,7 +79,7 @@ class SecretResource(SecretResourceProtocol):
|
|
|
54
79
|
if resp.json is None:
|
|
55
80
|
raise ResourceNotFoundError("Failed to retrieve secrets.")
|
|
56
81
|
|
|
57
|
-
return
|
|
82
|
+
return PaginatedResponse[Secret].create_with_cursor_field(resp.json, "secret_id")
|
|
58
83
|
|
|
59
84
|
async def create(self, name: str, secret: str) -> NoContentResponse:
|
|
60
85
|
"""Create a new secret.
|