deepset-mcp 0.0.2__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 +0 -0
- deepset_mcp/agents/__init__.py +0 -0
- deepset_mcp/agents/debugging/__init__.py +0 -0
- deepset_mcp/agents/debugging/debugging_agent.py +37 -0
- deepset_mcp/agents/debugging/system_prompt.md +214 -0
- deepset_mcp/agents/generalist/__init__.py +0 -0
- deepset_mcp/agents/generalist/generalist_agent.py +38 -0
- deepset_mcp/agents/generalist/system_prompt.md +241 -0
- deepset_mcp/api/README.md +536 -0
- deepset_mcp/api/__init__.py +0 -0
- deepset_mcp/api/client.py +277 -0
- deepset_mcp/api/custom_components/__init__.py +0 -0
- deepset_mcp/api/custom_components/models.py +25 -0
- deepset_mcp/api/custom_components/protocols.py +17 -0
- deepset_mcp/api/custom_components/resource.py +56 -0
- deepset_mcp/api/exceptions.py +70 -0
- deepset_mcp/api/haystack_service/__init__.py +0 -0
- deepset_mcp/api/haystack_service/protocols.py +13 -0
- deepset_mcp/api/haystack_service/resource.py +55 -0
- deepset_mcp/api/indexes/__init__.py +0 -0
- deepset_mcp/api/indexes/models.py +63 -0
- deepset_mcp/api/indexes/protocols.py +53 -0
- deepset_mcp/api/indexes/resource.py +138 -0
- deepset_mcp/api/integrations/__init__.py +1 -0
- deepset_mcp/api/integrations/models.py +49 -0
- deepset_mcp/api/integrations/protocols.py +27 -0
- deepset_mcp/api/integrations/resource.py +57 -0
- deepset_mcp/api/pipeline/__init__.py +17 -0
- deepset_mcp/api/pipeline/log_level.py +9 -0
- deepset_mcp/api/pipeline/models.py +235 -0
- deepset_mcp/api/pipeline/protocols.py +83 -0
- deepset_mcp/api/pipeline/resource.py +378 -0
- deepset_mcp/api/pipeline_template/__init__.py +0 -0
- deepset_mcp/api/pipeline_template/models.py +56 -0
- deepset_mcp/api/pipeline_template/protocols.py +17 -0
- deepset_mcp/api/pipeline_template/resource.py +88 -0
- deepset_mcp/api/protocols.py +122 -0
- deepset_mcp/api/secrets/__init__.py +0 -0
- deepset_mcp/api/secrets/models.py +16 -0
- deepset_mcp/api/secrets/protocols.py +29 -0
- deepset_mcp/api/secrets/resource.py +112 -0
- deepset_mcp/api/shared_models.py +17 -0
- deepset_mcp/api/transport.py +336 -0
- deepset_mcp/api/user/__init__.py +0 -0
- deepset_mcp/api/user/protocols.py +11 -0
- deepset_mcp/api/user/resource.py +38 -0
- deepset_mcp/api/workspace/__init__.py +7 -0
- deepset_mcp/api/workspace/models.py +23 -0
- deepset_mcp/api/workspace/protocols.py +41 -0
- deepset_mcp/api/workspace/resource.py +94 -0
- deepset_mcp/benchmark/README.md +425 -0
- deepset_mcp/benchmark/__init__.py +1 -0
- deepset_mcp/benchmark/agent_configs/debugging_agent.yml +10 -0
- deepset_mcp/benchmark/agent_configs/generalist_agent.yml +6 -0
- deepset_mcp/benchmark/dp_validation_error_analysis/__init__.py +0 -0
- deepset_mcp/benchmark/dp_validation_error_analysis/eda.ipynb +757 -0
- deepset_mcp/benchmark/dp_validation_error_analysis/prepare_interaction_data.ipynb +167 -0
- deepset_mcp/benchmark/dp_validation_error_analysis/preprocessing_utils.py +213 -0
- deepset_mcp/benchmark/runner/__init__.py +0 -0
- deepset_mcp/benchmark/runner/agent_benchmark_runner.py +561 -0
- deepset_mcp/benchmark/runner/agent_loader.py +110 -0
- deepset_mcp/benchmark/runner/cli.py +39 -0
- deepset_mcp/benchmark/runner/cli_agent.py +373 -0
- deepset_mcp/benchmark/runner/cli_index.py +71 -0
- deepset_mcp/benchmark/runner/cli_pipeline.py +73 -0
- deepset_mcp/benchmark/runner/cli_tests.py +226 -0
- deepset_mcp/benchmark/runner/cli_utils.py +61 -0
- deepset_mcp/benchmark/runner/config.py +73 -0
- deepset_mcp/benchmark/runner/config_loader.py +64 -0
- deepset_mcp/benchmark/runner/interactive.py +140 -0
- deepset_mcp/benchmark/runner/models.py +203 -0
- deepset_mcp/benchmark/runner/repl.py +67 -0
- deepset_mcp/benchmark/runner/setup_actions.py +238 -0
- deepset_mcp/benchmark/runner/streaming.py +360 -0
- deepset_mcp/benchmark/runner/teardown_actions.py +196 -0
- deepset_mcp/benchmark/runner/tracing.py +21 -0
- deepset_mcp/benchmark/tasks/chat_rag_answers_wrong_format.yml +16 -0
- deepset_mcp/benchmark/tasks/documents_output_wrong.yml +13 -0
- deepset_mcp/benchmark/tasks/jinja_str_instead_of_complex_type.yml +11 -0
- deepset_mcp/benchmark/tasks/jinja_syntax_error.yml +11 -0
- deepset_mcp/benchmark/tasks/missing_output_mapping.yml +14 -0
- deepset_mcp/benchmark/tasks/no_query_input.yml +13 -0
- deepset_mcp/benchmark/tasks/pipelines/chat_agent_jinja_str.yml +141 -0
- deepset_mcp/benchmark/tasks/pipelines/chat_agent_jinja_syntax.yml +141 -0
- deepset_mcp/benchmark/tasks/pipelines/chat_rag_answers_wrong_format.yml +181 -0
- deepset_mcp/benchmark/tasks/pipelines/chat_rag_missing_output_mapping.yml +189 -0
- deepset_mcp/benchmark/tasks/pipelines/rag_documents_wrong_format.yml +193 -0
- deepset_mcp/benchmark/tasks/pipelines/rag_no_query_input.yml +191 -0
- deepset_mcp/benchmark/tasks/pipelines/standard_index.yml +167 -0
- deepset_mcp/initialize_embedding_model.py +12 -0
- deepset_mcp/main.py +133 -0
- deepset_mcp/prompts/deepset_copilot_prompt.md +271 -0
- deepset_mcp/prompts/deepset_debugging_agent.md +214 -0
- deepset_mcp/store.py +5 -0
- deepset_mcp/tool_factory.py +473 -0
- deepset_mcp/tools/__init__.py +0 -0
- deepset_mcp/tools/custom_components.py +52 -0
- deepset_mcp/tools/doc_search.py +83 -0
- deepset_mcp/tools/haystack_service.py +358 -0
- deepset_mcp/tools/haystack_service_models.py +97 -0
- deepset_mcp/tools/indexes.py +129 -0
- deepset_mcp/tools/model_protocol.py +16 -0
- deepset_mcp/tools/pipeline.py +335 -0
- deepset_mcp/tools/pipeline_template.py +116 -0
- deepset_mcp/tools/secrets.py +45 -0
- deepset_mcp/tools/tokonomics/__init__.py +73 -0
- deepset_mcp/tools/tokonomics/decorators.py +396 -0
- deepset_mcp/tools/tokonomics/explorer.py +347 -0
- deepset_mcp/tools/tokonomics/object_store.py +177 -0
- deepset_mcp/tools/workspace.py +61 -0
- deepset_mcp-0.0.2.dist-info/METADATA +288 -0
- deepset_mcp-0.0.2.dist-info/RECORD +114 -0
- deepset_mcp-0.0.2.dist-info/WHEEL +4 -0
- deepset_mcp-0.0.2.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from collections.abc import AsyncIterator
|
|
3
|
+
from contextlib import AbstractAsyncContextManager, asynccontextmanager
|
|
4
|
+
from types import TracebackType
|
|
5
|
+
from typing import Any, Literal, Self, TypeVar, overload
|
|
6
|
+
|
|
7
|
+
from deepset_mcp.api.custom_components.resource import CustomComponentsResource
|
|
8
|
+
from deepset_mcp.api.haystack_service.resource import HaystackServiceResource
|
|
9
|
+
from deepset_mcp.api.indexes.resource import IndexResource
|
|
10
|
+
from deepset_mcp.api.integrations.resource import IntegrationResource
|
|
11
|
+
from deepset_mcp.api.pipeline.resource import PipelineResource
|
|
12
|
+
from deepset_mcp.api.pipeline_template.resource import PipelineTemplateResource
|
|
13
|
+
from deepset_mcp.api.protocols import AsyncClientProtocol
|
|
14
|
+
from deepset_mcp.api.secrets.resource import SecretResource
|
|
15
|
+
from deepset_mcp.api.transport import (
|
|
16
|
+
AsyncTransport,
|
|
17
|
+
StreamingResponse,
|
|
18
|
+
TransportProtocol,
|
|
19
|
+
TransportResponse,
|
|
20
|
+
)
|
|
21
|
+
from deepset_mcp.api.user.resource import UserResource
|
|
22
|
+
from deepset_mcp.api.workspace.resource import WorkspaceResource
|
|
23
|
+
|
|
24
|
+
T = TypeVar("T")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class AsyncDeepsetClient(AsyncClientProtocol):
|
|
28
|
+
"""Async Client for interacting with the deepset API."""
|
|
29
|
+
|
|
30
|
+
def __init__(
|
|
31
|
+
self,
|
|
32
|
+
api_key: str | None = None,
|
|
33
|
+
base_url: str = "https://api.cloud.deepset.ai/api",
|
|
34
|
+
transport: TransportProtocol | None = None,
|
|
35
|
+
transport_config: dict[str, Any] | None = None,
|
|
36
|
+
) -> None:
|
|
37
|
+
"""
|
|
38
|
+
Initialize an instance of the AsyncDeepsetClient.
|
|
39
|
+
|
|
40
|
+
Parameters
|
|
41
|
+
----------
|
|
42
|
+
api_key : str, optional
|
|
43
|
+
API key or token. Falls back to DEEPSET_API_KEY env var.
|
|
44
|
+
base_url : str, optional
|
|
45
|
+
Base URL for the deepset API.
|
|
46
|
+
transport : TransportProtocol, optional
|
|
47
|
+
Custom transport implementation.
|
|
48
|
+
transport_config : dict, optional
|
|
49
|
+
Configuration for default transport (e.g. timeout).
|
|
50
|
+
"""
|
|
51
|
+
self.api_key = api_key or os.environ.get("DEEPSET_API_KEY")
|
|
52
|
+
if not self.api_key:
|
|
53
|
+
raise ValueError("API key not provided and DEEPSET_API_KEY environment variable not set")
|
|
54
|
+
self.base_url = base_url
|
|
55
|
+
if transport is not None:
|
|
56
|
+
self._transport = transport
|
|
57
|
+
else:
|
|
58
|
+
self._transport = AsyncTransport(
|
|
59
|
+
base_url=self.base_url,
|
|
60
|
+
api_key=self.api_key,
|
|
61
|
+
config=transport_config,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
@overload
|
|
65
|
+
async def request(
|
|
66
|
+
self,
|
|
67
|
+
endpoint: str,
|
|
68
|
+
*,
|
|
69
|
+
response_type: type[T],
|
|
70
|
+
method: str = "GET",
|
|
71
|
+
data: dict[str, Any] | None = None,
|
|
72
|
+
headers: dict[str, str] | None = None,
|
|
73
|
+
timeout: float | None | Literal["config"] = "config",
|
|
74
|
+
**kwargs: Any,
|
|
75
|
+
) -> TransportResponse[T]: ...
|
|
76
|
+
|
|
77
|
+
@overload
|
|
78
|
+
async def request(
|
|
79
|
+
self,
|
|
80
|
+
endpoint: str,
|
|
81
|
+
*,
|
|
82
|
+
response_type: None = None,
|
|
83
|
+
method: str = "GET",
|
|
84
|
+
data: dict[str, Any] | None = None,
|
|
85
|
+
headers: dict[str, str] | None = None,
|
|
86
|
+
timeout: float | None | Literal["config"] = "config",
|
|
87
|
+
**kwargs: Any,
|
|
88
|
+
) -> TransportResponse[Any]: ...
|
|
89
|
+
|
|
90
|
+
async def request(
|
|
91
|
+
self,
|
|
92
|
+
endpoint: str,
|
|
93
|
+
*,
|
|
94
|
+
method: str = "GET",
|
|
95
|
+
data: dict[str, Any] | None = None,
|
|
96
|
+
headers: dict[str, str] | None = None,
|
|
97
|
+
response_type: type[T] | None = None,
|
|
98
|
+
timeout: float | None | Literal["config"] = "config",
|
|
99
|
+
**kwargs: Any,
|
|
100
|
+
) -> TransportResponse[Any]:
|
|
101
|
+
"""
|
|
102
|
+
Make a regular (non-streaming) request to the deepset API.
|
|
103
|
+
|
|
104
|
+
Parameters
|
|
105
|
+
----------
|
|
106
|
+
endpoint : str
|
|
107
|
+
API endpoint path
|
|
108
|
+
method : str, default="GET"
|
|
109
|
+
HTTP method
|
|
110
|
+
data : dict, optional
|
|
111
|
+
JSON data to send in request body
|
|
112
|
+
headers : dict, optional
|
|
113
|
+
Additional headers to include
|
|
114
|
+
response_type : type[T], optional
|
|
115
|
+
Expected response type for type checking
|
|
116
|
+
timeout : float | None | Literal["config"], optional
|
|
117
|
+
Request timeout in seconds. If "config", uses transport config timeout.
|
|
118
|
+
If None, disables timeout. If float, uses specific timeout.
|
|
119
|
+
**kwargs : Any
|
|
120
|
+
Additional arguments to pass to transport
|
|
121
|
+
|
|
122
|
+
Returns
|
|
123
|
+
-------
|
|
124
|
+
TransportResponse[T]
|
|
125
|
+
Response with parsed JSON if available
|
|
126
|
+
"""
|
|
127
|
+
if not endpoint.startswith("/"):
|
|
128
|
+
endpoint = f"/{endpoint}"
|
|
129
|
+
url = self.base_url + endpoint
|
|
130
|
+
|
|
131
|
+
# Default headers
|
|
132
|
+
request_headers: dict[str, str] = {
|
|
133
|
+
"Authorization": f"Bearer {self.api_key}",
|
|
134
|
+
"Accept": "application/json,text/plain,*/*",
|
|
135
|
+
}
|
|
136
|
+
if data is not None:
|
|
137
|
+
request_headers["Content-Type"] = "application/json"
|
|
138
|
+
# Merge custom headers
|
|
139
|
+
if headers:
|
|
140
|
+
headers.setdefault("Authorization", request_headers["Authorization"])
|
|
141
|
+
request_headers.update(headers)
|
|
142
|
+
|
|
143
|
+
return await self._transport.request(
|
|
144
|
+
method,
|
|
145
|
+
url,
|
|
146
|
+
json=data,
|
|
147
|
+
headers=request_headers,
|
|
148
|
+
response_type=response_type,
|
|
149
|
+
timeout=timeout,
|
|
150
|
+
**kwargs,
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
def stream_request(
|
|
154
|
+
self,
|
|
155
|
+
endpoint: str,
|
|
156
|
+
*,
|
|
157
|
+
method: str = "POST",
|
|
158
|
+
data: dict[str, Any] | None = None,
|
|
159
|
+
headers: dict[str, str] | None = None,
|
|
160
|
+
**kwargs: Any,
|
|
161
|
+
) -> AbstractAsyncContextManager[StreamingResponse]:
|
|
162
|
+
"""
|
|
163
|
+
Make a streaming request to the deepset API.
|
|
164
|
+
|
|
165
|
+
Must be used as an async context manager to ensure proper cleanup.
|
|
166
|
+
|
|
167
|
+
Parameters
|
|
168
|
+
----------
|
|
169
|
+
endpoint : str
|
|
170
|
+
API endpoint path
|
|
171
|
+
method : str, default="POST"
|
|
172
|
+
HTTP method (usually POST for streaming)
|
|
173
|
+
data : dict, optional
|
|
174
|
+
JSON data to send in request body
|
|
175
|
+
headers : dict, optional
|
|
176
|
+
Additional headers to include
|
|
177
|
+
**kwargs : Any
|
|
178
|
+
Additional arguments to pass to transport
|
|
179
|
+
|
|
180
|
+
Yields
|
|
181
|
+
------
|
|
182
|
+
StreamingResponse
|
|
183
|
+
Response object with streaming capabilities
|
|
184
|
+
|
|
185
|
+
Examples
|
|
186
|
+
--------
|
|
187
|
+
async with client.stream_request("/pipelines/search-stream", data={"query": "AI"}) as response:
|
|
188
|
+
if response.success:
|
|
189
|
+
async for line in response.iter_lines():
|
|
190
|
+
# Process each line of the stream
|
|
191
|
+
data = json.loads(line)
|
|
192
|
+
print(data)
|
|
193
|
+
else:
|
|
194
|
+
# Handle error
|
|
195
|
+
error_body = await response.read_body()
|
|
196
|
+
print(f"Error {response.status_code}: {error_body}")
|
|
197
|
+
"""
|
|
198
|
+
|
|
199
|
+
@asynccontextmanager
|
|
200
|
+
async def _stream() -> AsyncIterator[StreamingResponse]:
|
|
201
|
+
if not endpoint.startswith("/"):
|
|
202
|
+
full_endpoint = f"/{endpoint}"
|
|
203
|
+
url = self.base_url + full_endpoint
|
|
204
|
+
|
|
205
|
+
# Default headers for streaming
|
|
206
|
+
request_headers: dict[str, str] = {
|
|
207
|
+
"Authorization": f"Bearer {self.api_key}",
|
|
208
|
+
"Accept": "text/event-stream,application/json,text/plain,*/*",
|
|
209
|
+
}
|
|
210
|
+
if data is not None:
|
|
211
|
+
request_headers["Content-Type"] = "application/json"
|
|
212
|
+
# Merge custom headers
|
|
213
|
+
if headers:
|
|
214
|
+
headers.setdefault("Authorization", request_headers["Authorization"])
|
|
215
|
+
request_headers.update(headers)
|
|
216
|
+
|
|
217
|
+
async with self._transport.stream(
|
|
218
|
+
method,
|
|
219
|
+
url,
|
|
220
|
+
json=data,
|
|
221
|
+
headers=request_headers,
|
|
222
|
+
**kwargs,
|
|
223
|
+
) as response:
|
|
224
|
+
yield response
|
|
225
|
+
|
|
226
|
+
return _stream()
|
|
227
|
+
|
|
228
|
+
async def close(self) -> None:
|
|
229
|
+
"""Close underlying transport resources."""
|
|
230
|
+
await self._transport.close()
|
|
231
|
+
|
|
232
|
+
async def __aenter__(self) -> Self:
|
|
233
|
+
"""Enter the AsyncContextManager."""
|
|
234
|
+
return self
|
|
235
|
+
|
|
236
|
+
async def __aexit__(
|
|
237
|
+
self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None
|
|
238
|
+
) -> bool:
|
|
239
|
+
"""Exit the AsyncContextmanager and clean up resources."""
|
|
240
|
+
await self.close()
|
|
241
|
+
return False
|
|
242
|
+
|
|
243
|
+
def pipelines(self, workspace: str) -> PipelineResource:
|
|
244
|
+
"""Resource to interact with pipelines in the specified workspace."""
|
|
245
|
+
return PipelineResource(client=self, workspace=workspace)
|
|
246
|
+
|
|
247
|
+
def haystack_service(self) -> HaystackServiceResource:
|
|
248
|
+
"""Resource to interact with the Haystack service API."""
|
|
249
|
+
return HaystackServiceResource(client=self)
|
|
250
|
+
|
|
251
|
+
def pipeline_templates(self, workspace: str) -> PipelineTemplateResource:
|
|
252
|
+
"""Resource to interact with pipeline templates in the specified workspace."""
|
|
253
|
+
return PipelineTemplateResource(client=self, workspace=workspace)
|
|
254
|
+
|
|
255
|
+
def indexes(self, workspace: str) -> IndexResource:
|
|
256
|
+
"""Resource to interact with indexes in the specified workspace."""
|
|
257
|
+
return IndexResource(client=self, workspace=workspace)
|
|
258
|
+
|
|
259
|
+
def custom_components(self, workspace: str) -> CustomComponentsResource:
|
|
260
|
+
"""Resource to interact with custom components in the specified workspace."""
|
|
261
|
+
return CustomComponentsResource(client=self)
|
|
262
|
+
|
|
263
|
+
def users(self) -> UserResource:
|
|
264
|
+
"""Resource to interact with users."""
|
|
265
|
+
return UserResource(client=self)
|
|
266
|
+
|
|
267
|
+
def secrets(self) -> SecretResource:
|
|
268
|
+
"""Resource to interact with secrets."""
|
|
269
|
+
return SecretResource(client=self)
|
|
270
|
+
|
|
271
|
+
def workspaces(self) -> WorkspaceResource:
|
|
272
|
+
"""Resource to interact with workspaces."""
|
|
273
|
+
return WorkspaceResource(client=self)
|
|
274
|
+
|
|
275
|
+
def integrations(self) -> IntegrationResource:
|
|
276
|
+
"""Resource to interact with integrations."""
|
|
277
|
+
return IntegrationResource(client=self)
|
|
File without changes
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
from deepset_mcp.api.shared_models import DeepsetUser
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CustomComponentInstallation(BaseModel):
|
|
9
|
+
"""Model representing a custom component installation."""
|
|
10
|
+
|
|
11
|
+
custom_component_id: str
|
|
12
|
+
status: str
|
|
13
|
+
version: str
|
|
14
|
+
created_by_user_id: str
|
|
15
|
+
logs: list[dict[str, Any]]
|
|
16
|
+
organization_id: str
|
|
17
|
+
user_info: DeepsetUser | None = None
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class CustomComponentInstallationList(BaseModel):
|
|
21
|
+
"""Model representing a list of custom component installations."""
|
|
22
|
+
|
|
23
|
+
data: list[CustomComponentInstallation]
|
|
24
|
+
total: int
|
|
25
|
+
has_more: bool
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from typing import Protocol
|
|
2
|
+
|
|
3
|
+
from deepset_mcp.api.custom_components.models import CustomComponentInstallationList
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CustomComponentsProtocol(Protocol):
|
|
7
|
+
"""Protocol defining the implementation for CustomComponentsResource."""
|
|
8
|
+
|
|
9
|
+
async def list_installations(
|
|
10
|
+
self, limit: int = 20, page_number: int = 1, field: str = "created_at", order: str = "DESC"
|
|
11
|
+
) -> CustomComponentInstallationList:
|
|
12
|
+
"""List custom component installations."""
|
|
13
|
+
...
|
|
14
|
+
|
|
15
|
+
async def get_latest_installation_logs(self) -> str | None:
|
|
16
|
+
"""Get the logs from the latest custom component installation."""
|
|
17
|
+
...
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from deepset_mcp.api.custom_components.models import CustomComponentInstallationList
|
|
4
|
+
from deepset_mcp.api.custom_components.protocols import CustomComponentsProtocol
|
|
5
|
+
from deepset_mcp.api.protocols import AsyncClientProtocol
|
|
6
|
+
from deepset_mcp.api.transport import raise_for_status
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class CustomComponentsResource(CustomComponentsProtocol):
|
|
10
|
+
"""Resource for managing custom components in deepset."""
|
|
11
|
+
|
|
12
|
+
def __init__(self, client: AsyncClientProtocol) -> None:
|
|
13
|
+
"""Initialize a CustomComponentsResource.
|
|
14
|
+
|
|
15
|
+
:param client: The API client to use for requests.
|
|
16
|
+
"""
|
|
17
|
+
self._client = client
|
|
18
|
+
|
|
19
|
+
async def list_installations(
|
|
20
|
+
self, limit: int = 20, page_number: int = 1, field: str = "created_at", order: str = "DESC"
|
|
21
|
+
) -> CustomComponentInstallationList:
|
|
22
|
+
"""List custom component installations.
|
|
23
|
+
|
|
24
|
+
:param limit: Maximum number of installations to return.
|
|
25
|
+
:param page_number: Page number for pagination.
|
|
26
|
+
:param field: Field to sort by.
|
|
27
|
+
:param order: Sort order (ASC or DESC).
|
|
28
|
+
|
|
29
|
+
:returns: List of custom component installations.
|
|
30
|
+
"""
|
|
31
|
+
resp = await self._client.request(
|
|
32
|
+
endpoint=f"v2/custom_components?limit={limit}&page_number={page_number}&field={field}&order={order}",
|
|
33
|
+
method="GET",
|
|
34
|
+
response_type=dict[str, Any],
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
raise_for_status(resp)
|
|
38
|
+
|
|
39
|
+
if resp.json is None:
|
|
40
|
+
return CustomComponentInstallationList(data=[], total=0, has_more=False)
|
|
41
|
+
|
|
42
|
+
return CustomComponentInstallationList(**resp.json)
|
|
43
|
+
|
|
44
|
+
async def get_latest_installation_logs(self) -> str | None:
|
|
45
|
+
"""Get the logs from the latest custom component installation.
|
|
46
|
+
|
|
47
|
+
:returns: Latest installation logs.
|
|
48
|
+
"""
|
|
49
|
+
resp = await self._client.request(
|
|
50
|
+
endpoint="v2/custom_components/logs",
|
|
51
|
+
method="GET",
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
raise_for_status(resp)
|
|
55
|
+
|
|
56
|
+
return resp.text
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class DeepsetAPIError(Exception):
|
|
5
|
+
"""Base exception for all deepset API errors."""
|
|
6
|
+
|
|
7
|
+
def __init__(self, status_code: int | None = None, message: Any | None = None, detail: Any | None = None) -> None:
|
|
8
|
+
"""Initialize the exception."""
|
|
9
|
+
self.status_code = status_code
|
|
10
|
+
self.message = message
|
|
11
|
+
self.detail = detail
|
|
12
|
+
super().__init__(self.message)
|
|
13
|
+
|
|
14
|
+
def __str__(self) -> str:
|
|
15
|
+
"""Return a string representation of the exception."""
|
|
16
|
+
return f"{self.message} (Status Code: {self.status_code})"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ResourceNotFoundError(DeepsetAPIError):
|
|
20
|
+
"""Exception raised when a resource is not found (HTTP 404)."""
|
|
21
|
+
|
|
22
|
+
def __init__(self, message: Any = "Resource not found", detail: Any | None = None) -> None:
|
|
23
|
+
"""Initialize the exception."""
|
|
24
|
+
super().__init__(status_code=404, message=message, detail=detail)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class BadRequestError(DeepsetAPIError):
|
|
28
|
+
"""Exception raised for invalid requests (HTTP 400)."""
|
|
29
|
+
|
|
30
|
+
def __init__(self, message: Any = "Bad request", detail: Any | None = None) -> None:
|
|
31
|
+
"""Initialize the exception."""
|
|
32
|
+
super().__init__(status_code=400, message=message, detail=detail)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class RequestTimeoutError(Exception):
|
|
36
|
+
"""Exception raised when a request times out."""
|
|
37
|
+
|
|
38
|
+
def __init__(
|
|
39
|
+
self,
|
|
40
|
+
method: str,
|
|
41
|
+
url: str,
|
|
42
|
+
timeout: float | None | str,
|
|
43
|
+
duration: float | None = None,
|
|
44
|
+
detail: str | None = None,
|
|
45
|
+
):
|
|
46
|
+
"""Initialize the timeout exception with request context."""
|
|
47
|
+
self.method = method
|
|
48
|
+
self.url = url
|
|
49
|
+
self.timeout = timeout
|
|
50
|
+
self.duration = duration
|
|
51
|
+
self.detail = detail
|
|
52
|
+
|
|
53
|
+
timeout_display = f"{timeout}s" if isinstance(timeout, int | float) else str(timeout)
|
|
54
|
+
|
|
55
|
+
if duration is not None:
|
|
56
|
+
message = f"Request timed out after {duration:.2f}s (limit: {timeout_display}): {method} {url}"
|
|
57
|
+
else:
|
|
58
|
+
message = f"Request timed out (limit: {timeout_display}): {method} {url}"
|
|
59
|
+
|
|
60
|
+
super().__init__(message)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class UnexpectedAPIError(DeepsetAPIError):
|
|
64
|
+
"""Catch-all exception for unexpected API errors."""
|
|
65
|
+
|
|
66
|
+
def __init__(
|
|
67
|
+
self, status_code: int | None = None, message: Any = "Unexpected API error", detail: Any | None = None
|
|
68
|
+
):
|
|
69
|
+
"""Initialize the exception."""
|
|
70
|
+
super().__init__(status_code=status_code, message=message, detail=detail)
|
|
File without changes
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from typing import Any, Protocol
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class HaystackServiceProtocol(Protocol):
|
|
5
|
+
"""Protocol defining the implementation for HaystackService."""
|
|
6
|
+
|
|
7
|
+
async def get_component_schemas(self) -> dict[str, Any]:
|
|
8
|
+
"""Fetch the component schema from the API."""
|
|
9
|
+
...
|
|
10
|
+
|
|
11
|
+
async def get_component_input_output(self, component_name: str) -> dict[str, Any]:
|
|
12
|
+
"""Fetch input and output schema for a component from the API."""
|
|
13
|
+
...
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from deepset_mcp.api.exceptions import ResourceNotFoundError
|
|
4
|
+
from deepset_mcp.api.haystack_service.protocols import HaystackServiceProtocol
|
|
5
|
+
from deepset_mcp.api.protocols import AsyncClientProtocol
|
|
6
|
+
from deepset_mcp.api.transport import raise_for_status
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class HaystackServiceResource(HaystackServiceProtocol):
|
|
10
|
+
"""Manages interactions with the deepset Haystack service API."""
|
|
11
|
+
|
|
12
|
+
def __init__(self, client: AsyncClientProtocol) -> None:
|
|
13
|
+
"""Initializes a HaystackServiceResource instance."""
|
|
14
|
+
self._client = client
|
|
15
|
+
|
|
16
|
+
async def get_component_schemas(self) -> dict[str, Any]:
|
|
17
|
+
"""Fetch the component schema from the API.
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
The component schema as a dictionary
|
|
21
|
+
"""
|
|
22
|
+
resp = await self._client.request(
|
|
23
|
+
endpoint="v1/haystack/components",
|
|
24
|
+
method="GET",
|
|
25
|
+
headers={"accept": "application/json"},
|
|
26
|
+
data={"domain": "deepset-cloud"},
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
raise_for_status(resp)
|
|
30
|
+
|
|
31
|
+
return resp.json if resp.json is not None else {}
|
|
32
|
+
|
|
33
|
+
async def get_component_input_output(self, component_name: str) -> dict[str, Any]:
|
|
34
|
+
"""Fetch the component input and output schema from the API.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
component_name: The name of the component to fetch the input/output schema for
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
The component input/output schema as a dictionary
|
|
41
|
+
"""
|
|
42
|
+
resp = await self._client.request(
|
|
43
|
+
endpoint="v1/haystack/components/input-output",
|
|
44
|
+
method="GET",
|
|
45
|
+
headers={"accept": "application/json"},
|
|
46
|
+
params={"domain": "deepset-cloud", "names": component_name},
|
|
47
|
+
response_type=list[dict[str, Any]],
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
raise_for_status(resp)
|
|
51
|
+
|
|
52
|
+
if resp.json is None or len(resp.json) == 0:
|
|
53
|
+
raise ResourceNotFoundError(f"Component '{component_name}' not found.")
|
|
54
|
+
|
|
55
|
+
return resp.json[0] if resp.json is not None else {}
|
|
File without changes
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel
|
|
5
|
+
from rich.repr import Result
|
|
6
|
+
|
|
7
|
+
from deepset_mcp.api.shared_models import DeepsetUser
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class IndexStatus(BaseModel):
|
|
11
|
+
"""Status information about documents in an index."""
|
|
12
|
+
|
|
13
|
+
pending_file_count: int
|
|
14
|
+
failed_file_count: int
|
|
15
|
+
indexed_no_documents_file_count: int
|
|
16
|
+
indexed_file_count: int
|
|
17
|
+
total_file_count: int
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Index(BaseModel):
|
|
21
|
+
"""A deepset index."""
|
|
22
|
+
|
|
23
|
+
pipeline_index_id: str
|
|
24
|
+
name: str
|
|
25
|
+
description: str | None = None
|
|
26
|
+
config_yaml: str
|
|
27
|
+
workspace_id: str
|
|
28
|
+
settings: dict[str, Any]
|
|
29
|
+
desired_status: str
|
|
30
|
+
deployed_at: datetime | None = None
|
|
31
|
+
last_edited_at: datetime | None = None
|
|
32
|
+
max_index_replica_count: int
|
|
33
|
+
created_at: datetime
|
|
34
|
+
updated_at: datetime | None = None
|
|
35
|
+
created_by: DeepsetUser
|
|
36
|
+
last_edited_by: DeepsetUser | None = None
|
|
37
|
+
status: IndexStatus
|
|
38
|
+
|
|
39
|
+
def __rich_repr__(self) -> Result:
|
|
40
|
+
"""Used to display the model in an LLM friendly way."""
|
|
41
|
+
yield "name", self.name
|
|
42
|
+
yield "description", self.description, None
|
|
43
|
+
yield "desired_status", self.desired_status
|
|
44
|
+
yield "status", self.status
|
|
45
|
+
yield "status", self.status
|
|
46
|
+
yield "created_by", f"{self.created_by.given_name} {self.created_by.family_name} ({self.created_by.id})"
|
|
47
|
+
yield "created_at", self.created_at.strftime("%m/%d/%Y %I:%M:%S %p")
|
|
48
|
+
yield (
|
|
49
|
+
"last_edited_by",
|
|
50
|
+
f"{self.last_edited_by.given_name} {self.last_edited_by.family_name} ({self.last_edited_by.id})"
|
|
51
|
+
if self.last_edited_by
|
|
52
|
+
else None,
|
|
53
|
+
)
|
|
54
|
+
yield "last_edited_at", self.last_edited_at.strftime("%m/%d/%Y %I:%M:%S %p") if self.last_edited_at else None
|
|
55
|
+
yield "config_yaml", self.config_yaml if self.config_yaml is not None else "Get full index to see config."
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class IndexList(BaseModel):
|
|
59
|
+
"""Response model for listing indexes."""
|
|
60
|
+
|
|
61
|
+
data: list[Index]
|
|
62
|
+
has_more: bool
|
|
63
|
+
total: int
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from typing import Protocol
|
|
2
|
+
|
|
3
|
+
from deepset_mcp.api.indexes.models import Index, IndexList
|
|
4
|
+
from deepset_mcp.api.pipeline.models import PipelineValidationResult
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class IndexResourceProtocol(Protocol):
|
|
8
|
+
"""Protocol defining the implementation for IndexResource."""
|
|
9
|
+
|
|
10
|
+
async def list(self, limit: int = 10, page_number: int = 1) -> IndexList:
|
|
11
|
+
"""List indexes in the configured workspace."""
|
|
12
|
+
...
|
|
13
|
+
|
|
14
|
+
async def get(self, index_name: str) -> Index:
|
|
15
|
+
"""Fetch a single index by its name."""
|
|
16
|
+
...
|
|
17
|
+
|
|
18
|
+
async def create(self, name: str, yaml_config: str, description: str | None = None) -> Index:
|
|
19
|
+
"""Create a new index with the given name and configuration.
|
|
20
|
+
|
|
21
|
+
:param name: Name of the index
|
|
22
|
+
:param yaml_config: YAML configuration for the index
|
|
23
|
+
:param description: Optional description for the index
|
|
24
|
+
:returns: Created index details
|
|
25
|
+
"""
|
|
26
|
+
...
|
|
27
|
+
|
|
28
|
+
async def update(
|
|
29
|
+
self, index_name: str, updated_index_name: str | None = None, yaml_config: str | None = None
|
|
30
|
+
) -> Index:
|
|
31
|
+
"""Update name and/or configuration of an existing index.
|
|
32
|
+
|
|
33
|
+
:param index_name: Name of the index to update
|
|
34
|
+
:param updated_index_name: Optional new name for the index
|
|
35
|
+
:param yaml_config: Optional new YAML configuration
|
|
36
|
+
:returns: Updated index details
|
|
37
|
+
"""
|
|
38
|
+
...
|
|
39
|
+
|
|
40
|
+
async def deploy(self, index_name: str) -> PipelineValidationResult:
|
|
41
|
+
"""Deploy an index to production.
|
|
42
|
+
|
|
43
|
+
:param index_name: Name of the index to deploy.
|
|
44
|
+
:returns: PipelineValidationResult containing deployment status and any errors.
|
|
45
|
+
"""
|
|
46
|
+
...
|
|
47
|
+
|
|
48
|
+
async def delete(self, index_name: str) -> None:
|
|
49
|
+
"""Delete an index.
|
|
50
|
+
|
|
51
|
+
:param index_name: Name of the index to delete.
|
|
52
|
+
"""
|
|
53
|
+
...
|