Skip to content

Commit 2d892f9

Browse files
authored
feat: add pipeline_type filter support to pipeline templates (#53)
* feat: add PipelineType enum and pipeline_type field to PipelineTemplate model * feat: add filter parameter to list_templates method * feat: update PipelineTemplateResourceProtocol to include filter parameter * feat: add filter support to list_pipeline_templates tool * test: add pipeline_type to sample template for testing * test: add unit tests for filter and sorting parameters * test: import PipelineType enum for updated tests * test: update FakePipelineTemplateResource to support new parameters * test: import Any for type hints * test: add pipeline_type to test template instances * test: add pipeline_type to second test template * test: add pipeline_type to remaining test template * test: add comprehensive tests for filter and sorting functionality * feat: update MCP tool to support filter and sorting parameters * test: add integration tests for filter and sorting functionality * fix: tests
1 parent 20d68eb commit 2d892f9

8 files changed

Lines changed: 265 additions & 11 deletions

File tree

src/deepset_mcp/api/pipeline_template/models.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
1+
from enum import StrEnum
12
from uuid import UUID
23

34
from pydantic import BaseModel, Field
45

56

7+
class PipelineType(StrEnum):
8+
"""Enum representing the type of a pipeline template."""
9+
10+
QUERY = "query"
11+
INDEXING = "indexing"
12+
13+
614
class PipelineTemplateTag(BaseModel):
715
"""Model representing a tag on a pipeline template."""
816

@@ -17,7 +25,9 @@ class PipelineTemplate(BaseModel):
1725
best_for: list[str]
1826
description: str
1927
template_name: str = Field(alias="pipeline_name")
28+
display_name: str = Field(alias="name")
2029
pipeline_template_id: UUID = Field(alias="pipeline_template_id")
2130
potential_applications: list[str] = Field(alias="potential_applications")
2231
yaml_config: str | None = Field(None, alias="query_yaml")
2332
tags: list[PipelineTemplateTag]
33+
pipeline_type: PipelineType

src/deepset_mcp/api/pipeline_template/resource.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,23 +41,36 @@ async def get_template(self, template_name: str) -> PipelineTemplate:
4141

4242
return PipelineTemplate.model_validate(data)
4343

44-
async def list_templates(self, limit: int = 100) -> list[PipelineTemplate]:
44+
async def list_templates(
45+
self, limit: int = 100, field: str = "created_at", order: str = "DESC", filter: str | None = None
46+
) -> list[PipelineTemplate]:
4547
"""List pipeline templates in the configured workspace.
4648
4749
Parameters
4850
----------
4951
limit : int, optional (default=100)
5052
Maximum number of templates to return
53+
field : str, optional (default="created_at")
54+
Field to sort by
55+
order : str, optional (default="DESC")
56+
Sort order (ASC or DESC)
57+
filter : str | None, optional (default=None)
58+
OData filter expression for filtering templates
5159
5260
Returns
5361
-------
5462
list[PipelineTemplate]
5563
List of pipeline templates
5664
"""
65+
params = {"limit": limit, "page_number": 1, "field": field, "order": order}
66+
67+
if filter is not None:
68+
params["filter"] = filter
69+
5770
response = await self._client.request(
5871
f"/v1/workspaces/{self._workspace}/pipeline_templates",
5972
method="GET",
60-
params={"limit": limit, "page_number": 1, "field": "created_at", "order": "DESC"},
73+
params=params,
6174
)
6275

6376
raise_for_status(response)

src/deepset_mcp/api/protocols.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,9 @@ async def get_template(self, template_name: str) -> PipelineTemplate:
134134
"""Fetch a single pipeline template by its name."""
135135
...
136136

137-
async def list_templates(self, limit: int = 100) -> list[PipelineTemplate]:
137+
async def list_templates(
138+
self, limit: int = 100, field: str = "created_at", order: str = "DESC", filter: str | None = None
139+
) -> list[PipelineTemplate]:
138140
"""List pipeline templates in the configured workspace."""
139141
...
140142

src/deepset_mcp/tools/pipeline_template.py

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,29 @@
33
from deepset_mcp.tools.formatting_utils import pipeline_template_to_llm_readable_string
44

55

6-
async def list_pipeline_templates(client: AsyncClientProtocol, workspace: str) -> str:
7-
"""Retrieves a list of all available pipeline templates."""
6+
async def list_pipeline_templates(
7+
client: AsyncClientProtocol,
8+
workspace: str,
9+
limit: int = 100,
10+
field: str = "created_at",
11+
order: str = "DESC",
12+
filter: str | None = None,
13+
) -> str:
14+
"""Retrieves a list of all available pipeline templates.
15+
16+
:param client: The async client for API requests.
17+
:param workspace: The workspace to list templates from.
18+
:param limit: Maximum number of templates to return (default: 100).
19+
:param field: Field to sort by (default: "created_at").
20+
:param order: Sort order, either "ASC" or "DESC" (default: "DESC").
21+
:param filter: OData filter expression to filter templates by criteria.
22+
23+
:returns: Formatted string with template information.
24+
"""
825
try:
9-
response = await client.pipeline_templates(workspace=workspace).list_templates()
26+
response = await client.pipeline_templates(workspace=workspace).list_templates(
27+
limit=limit, field=field, order=order, filter=filter
28+
)
1029
formatted_templates = [pipeline_template_to_llm_readable_string(t) for t in response]
1130
return "\n\n".join(formatted_templates)
1231
except ResourceNotFoundError:

test/integration/test_integration_pipeline_resource.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -222,9 +222,6 @@ async def test_validation_invalid_yaml(
222222
assert result.errors[0].code == "PIPELINE_SCHEMA_ERROR"
223223

224224

225-
@pytest.mark.skip(
226-
reason="API not working correctly."
227-
) # skip until deepset API correctly returns error for yaml syntax errors
228225
@pytest.mark.asyncio
229226
async def test_validation_syntax_error(
230227
pipeline_resource: PipelineResource,

test/integration/test_integration_pipeline_template_resource.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,48 @@ async def test_list_templates_with_limit(
9393

9494
# Verify that the number of templates is not more than the limit
9595
assert len(templates) <= limit
96+
97+
98+
@pytest.mark.asyncio
99+
async def test_list_templates_with_filter(
100+
template_resource: PipelineTemplateResource,
101+
) -> None:
102+
"""Test listing templates with a pipeline type filter."""
103+
# Test filtering by QUERY pipeline type
104+
query_templates = await template_resource.list_templates(filter="pipeline_type eq 'QUERY'")
105+
106+
# Verify that all returned templates are QUERY type
107+
assert isinstance(query_templates, list)
108+
109+
# If templates are available, verify they are all QUERY type
110+
for template in query_templates:
111+
assert isinstance(template, PipelineTemplate)
112+
assert template.pipeline_type == "query"
113+
114+
# Test filtering by INDEXING pipeline type
115+
indexing_templates = await template_resource.list_templates(filter="pipeline_type eq 'INDEXING'")
116+
117+
# Verify that all returned templates are INDEXING type
118+
assert isinstance(indexing_templates, list)
119+
120+
# If templates are available, verify they are all INDEXING type
121+
for template in indexing_templates:
122+
assert isinstance(template, PipelineTemplate)
123+
assert template.pipeline_type == "indexing"
124+
125+
126+
@pytest.mark.asyncio
127+
async def test_list_templates_with_custom_sorting(
128+
template_resource: PipelineTemplateResource,
129+
) -> None:
130+
"""Test listing templates with custom sorting."""
131+
# Test sorting by name in ascending order
132+
templates = await template_resource.list_templates(field="name", order="ASC", limit=5)
133+
134+
# Verify that the templates are returned as a list
135+
assert isinstance(templates, list)
136+
137+
# If we have multiple templates, verify they are sorted correctly
138+
if len(templates) > 1:
139+
for i in range(len(templates) - 1):
140+
assert templates[i].display_name <= templates[i + 1].display_name

test/unit/api/pipeline_template/test_pipeline_template_resource.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ def create_sample_template(
1414
author: str = "deepset-ai",
1515
description: str = "A test template",
1616
template_id: str = "3fa85f64-5717-4562-b3fc-2c963f66afa6",
17+
pipeline_type: str = "query",
1718
) -> dict[str, Any]:
1819
"""Create a sample pipeline template response dictionary for testing."""
1920
return {
@@ -29,6 +30,7 @@ def create_sample_template(
2930
"potential_applications": ["testing", "development"],
3031
"recommended_dataset": ["sample-data"],
3132
"tags": [{"name": "test", "tag_id": "d4a85f64-5717-4562-b3fc-2c963f66afa6"}],
33+
"pipeline_type": pipeline_type,
3234
}
3335

3436

@@ -179,3 +181,75 @@ async def test_list_templates_empty_result(self) -> None:
179181

180182
# Verify empty results
181183
assert len(result) == 0
184+
185+
@pytest.mark.asyncio
186+
async def test_list_templates_with_filter(self) -> None:
187+
"""Test listing templates with a filter."""
188+
# Create sample data
189+
sample_templates = [
190+
create_sample_template(
191+
name="Query Template", template_id="1fa85f64-5717-4562-b3fc-2c963f66afa6", pipeline_type="query"
192+
),
193+
]
194+
195+
# Create client with predefined response
196+
client = DummyClient(
197+
responses={
198+
"test-workspace/pipeline_templates": {
199+
"data": sample_templates,
200+
"has_more": False,
201+
"total": 1,
202+
}
203+
}
204+
)
205+
206+
# Create resource and call list method with filter
207+
resource = PipelineTemplateResource(client=client, workspace="test-workspace")
208+
result = await resource.list_templates(filter="pipeline_type eq 'QUERY'")
209+
210+
# Verify results
211+
assert len(result) == 1
212+
assert isinstance(result[0], PipelineTemplate)
213+
assert result[0].template_name == "Query Template"
214+
assert result[0].pipeline_type == "query"
215+
216+
# Verify request includes filter
217+
assert len(client.requests) == 1
218+
assert client.requests[0]["endpoint"] == "/v1/workspaces/test-workspace/pipeline_templates"
219+
assert "filter" in client.requests[0]["params"]
220+
assert client.requests[0]["params"]["filter"] == "pipeline_type eq 'QUERY'"
221+
222+
@pytest.mark.asyncio
223+
async def test_list_templates_with_custom_sorting(self) -> None:
224+
"""Test listing templates with custom field and order."""
225+
# Create sample data
226+
sample_templates = [
227+
create_sample_template(name="Template A", template_id="1fa85f64-5717-4562-b3fc-2c963f66afa6"),
228+
create_sample_template(name="Template B", template_id="2fa85f64-5717-4562-b3fc-2c963f66afa6"),
229+
]
230+
231+
# Create client with predefined response
232+
client = DummyClient(
233+
responses={
234+
"test-workspace/pipeline_templates": {
235+
"data": sample_templates,
236+
"has_more": False,
237+
"total": 2,
238+
}
239+
}
240+
)
241+
242+
# Create resource and call list method with custom sorting
243+
resource = PipelineTemplateResource(client=client, workspace="test-workspace")
244+
result = await resource.list_templates(field="name", order="ASC")
245+
246+
# Verify results
247+
assert len(result) == 2
248+
assert isinstance(result[0], PipelineTemplate)
249+
assert result[0].template_name == "Template A"
250+
assert result[1].template_name == "Template B"
251+
252+
# Verify request includes custom sorting
253+
assert len(client.requests) == 1
254+
assert client.requests[0]["params"]["field"] == "name"
255+
assert client.requests[0]["params"]["order"] == "ASC"

0 commit comments

Comments
 (0)