deepset-mcp 0.0.2rc1__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.2rc1.dist-info/METADATA +292 -0
- deepset_mcp-0.0.2rc1.dist-info/RECORD +114 -0
- deepset_mcp-0.0.2rc1.dist-info/WHEEL +4 -0
- deepset_mcp-0.0.2rc1.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
from deepset_mcp.api.exceptions import UnexpectedAPIError
|
|
7
|
+
from deepset_mcp.api.protocols import AsyncClientProtocol
|
|
8
|
+
from deepset_mcp.tools.haystack_service_models import (
|
|
9
|
+
ComponentDefinition,
|
|
10
|
+
ComponentDefinitionList,
|
|
11
|
+
ComponentFamily,
|
|
12
|
+
ComponentFamilyList,
|
|
13
|
+
ComponentInitParameter,
|
|
14
|
+
ComponentIODefinition,
|
|
15
|
+
ComponentIOProperty,
|
|
16
|
+
ComponentIOSchema,
|
|
17
|
+
ComponentSearchResult,
|
|
18
|
+
ComponentSearchResults,
|
|
19
|
+
)
|
|
20
|
+
from deepset_mcp.tools.model_protocol import ModelProtocol
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def extract_component_texts(*, component_def: dict[str, Any]) -> tuple[str, str]:
|
|
24
|
+
"""Extracts the component name and description for embedding.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
component_def: The component definition
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
A tuple containing the component name and description
|
|
31
|
+
"""
|
|
32
|
+
component_type = component_def["properties"]["type"]["const"]
|
|
33
|
+
name = component_def.get("title", "")
|
|
34
|
+
description = component_def.get("description", "")
|
|
35
|
+
return component_type, f"{name} {description}"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
async def _build_component_definition(
|
|
39
|
+
*, component_def: dict[str, Any], component_type: str, haystack_service: Any, schema: dict[str, Any] | None = None
|
|
40
|
+
) -> ComponentDefinition | str:
|
|
41
|
+
"""Build a ComponentDefinition from component schema data."""
|
|
42
|
+
try:
|
|
43
|
+
# Extract basic info
|
|
44
|
+
component_type_info = component_def["properties"]["type"]
|
|
45
|
+
init_params_schema = component_def["properties"].get("init_parameters", {}).get("properties", {})
|
|
46
|
+
required_params = component_def["properties"].get("init_parameters", {}).get("required", [])
|
|
47
|
+
|
|
48
|
+
# Build init parameters
|
|
49
|
+
init_params = [
|
|
50
|
+
ComponentInitParameter(
|
|
51
|
+
name=param_name,
|
|
52
|
+
annotation=param_info.get("_annotation", param_info.get("type", "Unknown")),
|
|
53
|
+
description=param_info.get("description", "No description available."),
|
|
54
|
+
default=param_info.get("default"),
|
|
55
|
+
required=param_name in required_params,
|
|
56
|
+
)
|
|
57
|
+
for param_name, param_info in init_params_schema.items()
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
# Try to get I/O schema
|
|
61
|
+
input_schema = None
|
|
62
|
+
output_schema = None
|
|
63
|
+
error_message = None
|
|
64
|
+
|
|
65
|
+
try:
|
|
66
|
+
component_name = component_type.split(".")[-1]
|
|
67
|
+
io_info = await haystack_service.get_component_input_output(component_name)
|
|
68
|
+
|
|
69
|
+
# Build input schema
|
|
70
|
+
if "input" in io_info:
|
|
71
|
+
input_props = io_info["input"].get("properties", {})
|
|
72
|
+
input_required = io_info["input"].get("required", [])
|
|
73
|
+
input_properties = {
|
|
74
|
+
prop_name: ComponentIOProperty(
|
|
75
|
+
name=prop_name,
|
|
76
|
+
annotation=prop_info.get("_annotation", prop_info.get("type", "Unknown")),
|
|
77
|
+
description=prop_info.get("description", "No description available."),
|
|
78
|
+
type=prop_info.get("type", "Unknown"),
|
|
79
|
+
required=prop_name in input_required,
|
|
80
|
+
)
|
|
81
|
+
for prop_name, prop_info in input_props.items()
|
|
82
|
+
}
|
|
83
|
+
input_schema = ComponentIOSchema(properties=input_properties, required=input_required)
|
|
84
|
+
|
|
85
|
+
# Build output schema
|
|
86
|
+
if "output" in io_info and isinstance(io_info["output"], dict):
|
|
87
|
+
output_info = io_info["output"]
|
|
88
|
+
if "properties" in output_info:
|
|
89
|
+
output_props = output_info.get("properties", {})
|
|
90
|
+
output_required = output_info.get("required", [])
|
|
91
|
+
output_properties = {
|
|
92
|
+
prop_name: ComponentIOProperty(
|
|
93
|
+
name=prop_name,
|
|
94
|
+
annotation=prop_info.get("_annotation", prop_info.get("type", "Unknown")),
|
|
95
|
+
description=prop_info.get("description", "No description available."),
|
|
96
|
+
type=prop_info.get("type", "Unknown"),
|
|
97
|
+
required=prop_name in output_required,
|
|
98
|
+
)
|
|
99
|
+
for prop_name, prop_info in output_props.items()
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
# Build definitions
|
|
103
|
+
definitions = {}
|
|
104
|
+
if "definitions" in output_info:
|
|
105
|
+
for def_name, def_info in output_info["definitions"].items():
|
|
106
|
+
if "properties" in def_info:
|
|
107
|
+
def_required = def_info.get("required", [])
|
|
108
|
+
def_properties = {
|
|
109
|
+
prop_name: ComponentIOProperty(
|
|
110
|
+
name=prop_name,
|
|
111
|
+
annotation=prop_info.get("_annotation", prop_info.get("type", "Unknown")),
|
|
112
|
+
description=prop_info.get("description", "No description available."),
|
|
113
|
+
type=prop_info.get("type", "Unknown"),
|
|
114
|
+
required=prop_name in def_required,
|
|
115
|
+
)
|
|
116
|
+
for prop_name, prop_info in def_info["properties"].items()
|
|
117
|
+
}
|
|
118
|
+
definitions[def_name] = ComponentIODefinition(
|
|
119
|
+
name=def_name,
|
|
120
|
+
type=def_info.get("type", "object"),
|
|
121
|
+
properties=def_properties,
|
|
122
|
+
required=def_required,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
output_schema = ComponentIOSchema(
|
|
126
|
+
properties=output_properties, required=output_required, definitions=definitions
|
|
127
|
+
)
|
|
128
|
+
except Exception as e:
|
|
129
|
+
error_message = f"Failed to fetch input/output schema: {str(e)}"
|
|
130
|
+
|
|
131
|
+
# Check if this is a custom component
|
|
132
|
+
is_custom = schema is not None and "package_version" in schema
|
|
133
|
+
package_version = schema.get("package_version") if schema else None
|
|
134
|
+
dynamic_params = schema.get("dynamic_params", False) if schema else False
|
|
135
|
+
|
|
136
|
+
return ComponentDefinition(
|
|
137
|
+
component_type=component_type,
|
|
138
|
+
title=component_def.get("title", "Unknown"),
|
|
139
|
+
description=component_def.get("description", "No description available."),
|
|
140
|
+
family=component_type_info.get("family", "Unknown"),
|
|
141
|
+
family_description=component_type_info.get("family_description", "No description available."),
|
|
142
|
+
init_parameters=init_params,
|
|
143
|
+
input_schema=input_schema,
|
|
144
|
+
output_schema=output_schema,
|
|
145
|
+
error_message=error_message,
|
|
146
|
+
is_custom=is_custom,
|
|
147
|
+
package_version=package_version,
|
|
148
|
+
dynamic_params=dynamic_params,
|
|
149
|
+
)
|
|
150
|
+
except Exception as e:
|
|
151
|
+
return f"Failed to build component definition: {str(e)}"
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
async def get_component_definition(*, client: AsyncClientProtocol, component_type: str) -> ComponentDefinition | str:
|
|
155
|
+
"""Returns the definition of a specific Haystack component.
|
|
156
|
+
|
|
157
|
+
Args:
|
|
158
|
+
client: The API client to use
|
|
159
|
+
component_type: Fully qualified component type
|
|
160
|
+
(e.g. haystack.components.routers.conditional_router.ConditionalRouter)
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
ComponentDefinition model or error message string
|
|
164
|
+
"""
|
|
165
|
+
haystack_service = client.haystack_service()
|
|
166
|
+
|
|
167
|
+
try:
|
|
168
|
+
response = await haystack_service.get_component_schemas()
|
|
169
|
+
except UnexpectedAPIError as e:
|
|
170
|
+
return f"Failed to retrieve component definition: {e}"
|
|
171
|
+
|
|
172
|
+
components = response["component_schema"]["definitions"]["Components"]
|
|
173
|
+
|
|
174
|
+
# Find the component by its type
|
|
175
|
+
component_def = None
|
|
176
|
+
for comp in components.values():
|
|
177
|
+
if comp["properties"]["type"].get("const") == component_type:
|
|
178
|
+
component_def = comp
|
|
179
|
+
break
|
|
180
|
+
|
|
181
|
+
if not component_def:
|
|
182
|
+
return f"Component not found: {component_type}"
|
|
183
|
+
|
|
184
|
+
return await _build_component_definition(
|
|
185
|
+
component_def=component_def, component_type=component_type, haystack_service=haystack_service
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
async def search_component_definition(
|
|
190
|
+
*, client: AsyncClientProtocol, query: str, model: ModelProtocol, top_k: int = 5
|
|
191
|
+
) -> ComponentSearchResults | str:
|
|
192
|
+
"""Searches for components based on name or description using semantic similarity.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
client: The API client to use
|
|
196
|
+
query: The search query
|
|
197
|
+
model: The model to use for computing embeddings
|
|
198
|
+
top_k: Maximum number of results to return (default: 5)
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
ComponentSearchResults model or error message string
|
|
202
|
+
"""
|
|
203
|
+
haystack_service = client.haystack_service()
|
|
204
|
+
|
|
205
|
+
try:
|
|
206
|
+
response = await haystack_service.get_component_schemas()
|
|
207
|
+
except UnexpectedAPIError as e:
|
|
208
|
+
return f"Failed to retrieve component schemas: {e}"
|
|
209
|
+
|
|
210
|
+
components = response["component_schema"]["definitions"]["Components"]
|
|
211
|
+
|
|
212
|
+
# Extract text for embedding from all components
|
|
213
|
+
component_texts: list[tuple[str, str]] = [
|
|
214
|
+
extract_component_texts(component_def=comp) for comp in components.values()
|
|
215
|
+
]
|
|
216
|
+
component_types: list[str] = [c[0] for c in component_texts]
|
|
217
|
+
|
|
218
|
+
if not component_texts:
|
|
219
|
+
return ComponentSearchResults(results=[], query=query, total_found=0)
|
|
220
|
+
|
|
221
|
+
# Compute embeddings
|
|
222
|
+
query_embedding = model.encode(query)
|
|
223
|
+
component_embeddings = model.encode([text for _, text in component_texts])
|
|
224
|
+
|
|
225
|
+
query_embedding_reshaped = query_embedding.reshape(1, -1)
|
|
226
|
+
|
|
227
|
+
# Calculate dot product between target and all paths
|
|
228
|
+
# This gives us a similarity score for each path
|
|
229
|
+
similarities = np.dot(component_embeddings, query_embedding_reshaped.T).flatten()
|
|
230
|
+
|
|
231
|
+
# Create (path, similarity) pairs
|
|
232
|
+
component_similarities = list(zip(component_types, similarities, strict=False))
|
|
233
|
+
|
|
234
|
+
# Sort by similarity score in descending order
|
|
235
|
+
component_similarities.sort(key=lambda x: x[1], reverse=True)
|
|
236
|
+
|
|
237
|
+
top_components = component_similarities[:top_k]
|
|
238
|
+
search_results = []
|
|
239
|
+
for component_type, sim in top_components:
|
|
240
|
+
# Find the component definition by type
|
|
241
|
+
component_def = None
|
|
242
|
+
for comp in components.values():
|
|
243
|
+
if comp["properties"]["type"].get("const") == component_type:
|
|
244
|
+
component_def = comp
|
|
245
|
+
break
|
|
246
|
+
|
|
247
|
+
if component_def:
|
|
248
|
+
definition = await _build_component_definition(
|
|
249
|
+
component_def=component_def, component_type=component_type, haystack_service=haystack_service
|
|
250
|
+
)
|
|
251
|
+
if isinstance(definition, ComponentDefinition):
|
|
252
|
+
search_results.append(ComponentSearchResult(component=definition, similarity_score=float(sim)))
|
|
253
|
+
|
|
254
|
+
return ComponentSearchResults(results=search_results, query=query, total_found=len(search_results))
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
async def list_component_families(*, client: AsyncClientProtocol) -> ComponentFamilyList | str:
|
|
258
|
+
"""Lists all Haystack component families that are available on deepset.
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
client: The API client to use
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
ComponentFamilyList model or error message string
|
|
265
|
+
"""
|
|
266
|
+
haystack_service = client.haystack_service()
|
|
267
|
+
|
|
268
|
+
try:
|
|
269
|
+
response = await haystack_service.get_component_schemas()
|
|
270
|
+
except UnexpectedAPIError as e:
|
|
271
|
+
return f"Failed to retrieve component families: {e}"
|
|
272
|
+
|
|
273
|
+
components = response["component_schema"]["definitions"]["Components"]
|
|
274
|
+
|
|
275
|
+
families = {}
|
|
276
|
+
for component_def in components.values():
|
|
277
|
+
component_type = component_def["properties"]["type"]
|
|
278
|
+
family = component_type["family"]
|
|
279
|
+
description = component_type.get("family_description", "No description available.")
|
|
280
|
+
families[family] = description
|
|
281
|
+
|
|
282
|
+
if not families:
|
|
283
|
+
return "No component families found in the response"
|
|
284
|
+
|
|
285
|
+
# Convert to ComponentFamily objects
|
|
286
|
+
family_objects = [
|
|
287
|
+
ComponentFamily(name=family, description=description) for family, description in sorted(families.items())
|
|
288
|
+
]
|
|
289
|
+
|
|
290
|
+
return ComponentFamilyList(families=family_objects, total_count=len(family_objects))
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
async def get_custom_components(*, client: AsyncClientProtocol) -> ComponentDefinitionList | str:
|
|
294
|
+
"""Get a list of all installed custom components.
|
|
295
|
+
|
|
296
|
+
:param client: The API client to use.
|
|
297
|
+
|
|
298
|
+
:returns: ComponentDefinitionList model or error message string.
|
|
299
|
+
"""
|
|
300
|
+
haystack_service = client.haystack_service()
|
|
301
|
+
|
|
302
|
+
try:
|
|
303
|
+
response = await haystack_service.get_component_schemas()
|
|
304
|
+
except UnexpectedAPIError as e:
|
|
305
|
+
return f"Error retrieving component schemas: {e}"
|
|
306
|
+
|
|
307
|
+
# Navigate to the components definition section
|
|
308
|
+
# Typically structured as {definitions: {Components: {<component_name>: <schema>}}}
|
|
309
|
+
schemas = response.get("component_schema", {})
|
|
310
|
+
all_schemas = schemas.get("definitions", {}).get("Components", {})
|
|
311
|
+
components = response["component_schema"]["definitions"]["Components"]
|
|
312
|
+
|
|
313
|
+
if not all_schemas:
|
|
314
|
+
return "No component schemas found or unexpected schema format."
|
|
315
|
+
|
|
316
|
+
# Filter for custom components (those with package_version key)
|
|
317
|
+
custom_component_schemas = {}
|
|
318
|
+
for component_name, schema in all_schemas.items():
|
|
319
|
+
if "package_version" in schema:
|
|
320
|
+
custom_component_schemas[component_name] = schema
|
|
321
|
+
|
|
322
|
+
if not custom_component_schemas:
|
|
323
|
+
return "No custom components found."
|
|
324
|
+
|
|
325
|
+
# Build ComponentDefinition objects for each custom component in parallel
|
|
326
|
+
async def build_single_component(schema: dict[str, Any]) -> ComponentDefinition | None:
|
|
327
|
+
"""Build a single component definition with concurrency control."""
|
|
328
|
+
async with semaphore: # Limit to 5 concurrent builds
|
|
329
|
+
# Find the component definition by its type
|
|
330
|
+
component_type = schema.get("properties", {}).get("type", {}).get("const", "Unknown")
|
|
331
|
+
component_def = None
|
|
332
|
+
for comp in components.values():
|
|
333
|
+
if comp["properties"]["type"].get("const") == component_type:
|
|
334
|
+
component_def = comp
|
|
335
|
+
break
|
|
336
|
+
|
|
337
|
+
if component_def:
|
|
338
|
+
definition = await _build_component_definition(
|
|
339
|
+
component_def=component_def,
|
|
340
|
+
component_type=component_type,
|
|
341
|
+
haystack_service=haystack_service,
|
|
342
|
+
schema=schema,
|
|
343
|
+
)
|
|
344
|
+
if isinstance(definition, ComponentDefinition):
|
|
345
|
+
return definition
|
|
346
|
+
return None
|
|
347
|
+
|
|
348
|
+
# Create semaphore to limit concurrent builds to 5
|
|
349
|
+
semaphore = asyncio.Semaphore(5)
|
|
350
|
+
|
|
351
|
+
# Build all components in parallel
|
|
352
|
+
tasks = [build_single_component(schema) for schema in custom_component_schemas.values()]
|
|
353
|
+
results = await asyncio.gather(*tasks)
|
|
354
|
+
|
|
355
|
+
# Filter out None results
|
|
356
|
+
component_definitions = [comp for comp in results if comp is not None]
|
|
357
|
+
|
|
358
|
+
return ComponentDefinitionList(components=component_definitions, total_count=len(component_definitions))
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""Data models for Haystack service tool outputs."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, Field
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ComponentInitParameter(BaseModel):
|
|
11
|
+
"""Represents an initialization parameter for a Haystack component."""
|
|
12
|
+
|
|
13
|
+
name: str
|
|
14
|
+
annotation: str
|
|
15
|
+
description: str
|
|
16
|
+
default: Any | None = None
|
|
17
|
+
required: bool = False
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ComponentIOProperty(BaseModel):
|
|
21
|
+
"""Represents an input/output property schema."""
|
|
22
|
+
|
|
23
|
+
name: str
|
|
24
|
+
annotation: str
|
|
25
|
+
description: str
|
|
26
|
+
type: str
|
|
27
|
+
required: bool = False
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class ComponentIODefinition(BaseModel):
|
|
31
|
+
"""Represents a definition referenced in I/O schema."""
|
|
32
|
+
|
|
33
|
+
name: str
|
|
34
|
+
type: str
|
|
35
|
+
properties: dict[str, ComponentIOProperty]
|
|
36
|
+
required: list[str]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class ComponentIOSchema(BaseModel):
|
|
40
|
+
"""Represents the input/output schema for a component."""
|
|
41
|
+
|
|
42
|
+
properties: dict[str, ComponentIOProperty]
|
|
43
|
+
required: list[str]
|
|
44
|
+
definitions: dict[str, ComponentIODefinition] = Field(default_factory=dict)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class ComponentDefinition(BaseModel):
|
|
48
|
+
"""Represents a complete Haystack component definition."""
|
|
49
|
+
|
|
50
|
+
component_type: str
|
|
51
|
+
title: str
|
|
52
|
+
description: str
|
|
53
|
+
family: str
|
|
54
|
+
family_description: str
|
|
55
|
+
init_parameters: list[ComponentInitParameter] = Field(default_factory=list)
|
|
56
|
+
input_schema: ComponentIOSchema | None = None
|
|
57
|
+
output_schema: ComponentIOSchema | None = None
|
|
58
|
+
error_message: str | None = None
|
|
59
|
+
is_custom: bool = False
|
|
60
|
+
package_version: str | None = None
|
|
61
|
+
dynamic_params: bool = False
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class ComponentSearchResult(BaseModel):
|
|
65
|
+
"""Represents a search result for a component."""
|
|
66
|
+
|
|
67
|
+
component: ComponentDefinition
|
|
68
|
+
similarity_score: float
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class ComponentSearchResults(BaseModel):
|
|
72
|
+
"""Response model for component search results."""
|
|
73
|
+
|
|
74
|
+
results: list[ComponentSearchResult]
|
|
75
|
+
query: str
|
|
76
|
+
total_found: int
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class ComponentFamily(BaseModel):
|
|
80
|
+
"""Represents a Haystack component family."""
|
|
81
|
+
|
|
82
|
+
name: str
|
|
83
|
+
description: str
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class ComponentFamilyList(BaseModel):
|
|
87
|
+
"""Response model for listing component families."""
|
|
88
|
+
|
|
89
|
+
families: list[ComponentFamily]
|
|
90
|
+
total_count: int
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class ComponentDefinitionList(BaseModel):
|
|
94
|
+
"""Response model for listing component definitions."""
|
|
95
|
+
|
|
96
|
+
components: list[ComponentDefinition]
|
|
97
|
+
total_count: int
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
from deepset_mcp.api.exceptions import BadRequestError, ResourceNotFoundError, UnexpectedAPIError
|
|
2
|
+
from deepset_mcp.api.indexes.models import Index, IndexList
|
|
3
|
+
from deepset_mcp.api.pipeline import PipelineValidationResult
|
|
4
|
+
from deepset_mcp.api.protocols import AsyncClientProtocol
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
async def list_indexes(*, client: AsyncClientProtocol, workspace: str) -> IndexList | str:
|
|
8
|
+
"""Use this to list available indexes on the deepset platform in your workspace.
|
|
9
|
+
|
|
10
|
+
:param client: Deepset API client to use for requesting indexes.
|
|
11
|
+
:param workspace: Workspace of which to list indexes.
|
|
12
|
+
"""
|
|
13
|
+
try:
|
|
14
|
+
result = await client.indexes(workspace=workspace).list()
|
|
15
|
+
except ResourceNotFoundError as e:
|
|
16
|
+
return f"Error listing indexes. Error: {e.message} ({e.status_code})"
|
|
17
|
+
|
|
18
|
+
return result
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
async def get_index(*, client: AsyncClientProtocol, workspace: str, index_name: str) -> Index | str:
|
|
22
|
+
"""Fetches detailed configuration information for a specific index, identified by its unique `index_name`.
|
|
23
|
+
|
|
24
|
+
:param client: Deepset API client to use for requesting the index.
|
|
25
|
+
:param workspace: Workspace of which to get the index from.
|
|
26
|
+
:param index_name: Unique name of the index to fetch.
|
|
27
|
+
"""
|
|
28
|
+
try:
|
|
29
|
+
response = await client.indexes(workspace=workspace).get(index_name)
|
|
30
|
+
except ResourceNotFoundError:
|
|
31
|
+
return f"There is no index named '{index_name}'. Did you mean to create it?"
|
|
32
|
+
|
|
33
|
+
return response
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
async def create_index(
|
|
37
|
+
*,
|
|
38
|
+
client: AsyncClientProtocol,
|
|
39
|
+
workspace: str,
|
|
40
|
+
index_name: str,
|
|
41
|
+
yaml_configuration: str,
|
|
42
|
+
description: str | None = None,
|
|
43
|
+
) -> dict[str, str | Index] | str:
|
|
44
|
+
"""Creates a new index within your deepset platform workspace.
|
|
45
|
+
|
|
46
|
+
:param client: Deepset API client to use.
|
|
47
|
+
:param workspace: Workspace in which to create the index.
|
|
48
|
+
:param index_name: Unique name of the index to create.
|
|
49
|
+
:param yaml_configuration: YAML configuration to use for the index.
|
|
50
|
+
:param description: Description of the index to create.
|
|
51
|
+
"""
|
|
52
|
+
try:
|
|
53
|
+
result = await client.indexes(workspace=workspace).create(
|
|
54
|
+
name=index_name, yaml_config=yaml_configuration, description=description
|
|
55
|
+
)
|
|
56
|
+
except ResourceNotFoundError:
|
|
57
|
+
return f"There is no workspace named '{workspace}'. Did you mean to configure it?"
|
|
58
|
+
except BadRequestError as e:
|
|
59
|
+
return f"Failed to create index '{index_name}': {e}"
|
|
60
|
+
except UnexpectedAPIError as e:
|
|
61
|
+
return f"Failed to create index '{index_name}': {e}"
|
|
62
|
+
|
|
63
|
+
return {"message": f"Index '{index_name}' created successfully.", "index": result}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
async def update_index(
|
|
67
|
+
*,
|
|
68
|
+
client: AsyncClientProtocol,
|
|
69
|
+
workspace: str,
|
|
70
|
+
index_name: str,
|
|
71
|
+
updated_index_name: str | None = None,
|
|
72
|
+
yaml_configuration: str | None = None,
|
|
73
|
+
) -> dict[str, str | Index] | str:
|
|
74
|
+
"""Updates an existing index in your deepset platform workspace.
|
|
75
|
+
|
|
76
|
+
This function can update either the name or the configuration of an existing index, or both.
|
|
77
|
+
At least one of updated_index_name or yaml_configuration must be provided.
|
|
78
|
+
|
|
79
|
+
:param client: Deepset API client to use.
|
|
80
|
+
:param workspace: Workspace in which to update the index.
|
|
81
|
+
:param index_name: Unique name of the index to update.
|
|
82
|
+
:param updated_index_name: Updated name of the index.
|
|
83
|
+
:param yaml_configuration: YAML configuration to update the index with.
|
|
84
|
+
"""
|
|
85
|
+
if not updated_index_name and not yaml_configuration:
|
|
86
|
+
return "You must provide either a new name or a new configuration to update the index."
|
|
87
|
+
|
|
88
|
+
try:
|
|
89
|
+
result = await client.indexes(workspace=workspace).update(
|
|
90
|
+
index_name=index_name, updated_index_name=updated_index_name, yaml_config=yaml_configuration
|
|
91
|
+
)
|
|
92
|
+
except ResourceNotFoundError:
|
|
93
|
+
return f"There is no index named '{index_name}'. Did you mean to create it?"
|
|
94
|
+
except BadRequestError as e:
|
|
95
|
+
return f"Failed to update index '{index_name}': {e}"
|
|
96
|
+
except UnexpectedAPIError as e:
|
|
97
|
+
return f"Failed to update index '{index_name}': {e}"
|
|
98
|
+
|
|
99
|
+
return {"message": f"Index '{index_name}' updated successfully.", "index": result}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
async def deploy_index(
|
|
103
|
+
*, client: AsyncClientProtocol, workspace: str, index_name: str
|
|
104
|
+
) -> str | PipelineValidationResult:
|
|
105
|
+
"""Deploys an index to production.
|
|
106
|
+
|
|
107
|
+
This function attempts to deploy the specified index in the given workspace.
|
|
108
|
+
If the deployment fails due to validation errors, it returns an object
|
|
109
|
+
describing the validation errors.
|
|
110
|
+
|
|
111
|
+
:param client: The async client for API communication.
|
|
112
|
+
:param workspace: The workspace name.
|
|
113
|
+
:param index_name: Name of the index to deploy.
|
|
114
|
+
|
|
115
|
+
:returns: A string indicating the deployment result or the validation results including errors.
|
|
116
|
+
"""
|
|
117
|
+
try:
|
|
118
|
+
deployment_result = await client.indexes(workspace=workspace).deploy(index_name=index_name)
|
|
119
|
+
except ResourceNotFoundError:
|
|
120
|
+
return f"There is no index named '{index_name}' in workspace '{workspace}'."
|
|
121
|
+
except BadRequestError as e:
|
|
122
|
+
return f"Failed to deploy index '{index_name}': {e}"
|
|
123
|
+
except UnexpectedAPIError as e:
|
|
124
|
+
return f"Failed to deploy index '{index_name}': {e}"
|
|
125
|
+
|
|
126
|
+
if not deployment_result.valid:
|
|
127
|
+
return deployment_result
|
|
128
|
+
|
|
129
|
+
return f"Index '{index_name}' deployed successfully."
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from typing import Any, Protocol
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ModelProtocol(Protocol):
|
|
7
|
+
"""Protocol for static embedding models."""
|
|
8
|
+
|
|
9
|
+
def encode(self, sentences: list[str] | str) -> np.ndarray[Any, Any]:
|
|
10
|
+
"""
|
|
11
|
+
Encodes a single or multiple sentences.
|
|
12
|
+
|
|
13
|
+
:param sentences: Single sentence or list of sentences to encode
|
|
14
|
+
:returns: Numpy array of encoded sentences
|
|
15
|
+
"""
|
|
16
|
+
...
|