Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions src/deepset_mcp/api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,13 +161,13 @@ from deepset_mcp.api.client import AsyncDeepsetClient

async with AsyncDeepsetClient() as client:
indexes = client.indexes(workspace="your-workspace")

# List all indexes
index_list = await indexes.list()

# Get a specific index
index = await indexes.get("my-index")

# Create a new index
index_yaml = """
document_store:
Expand All @@ -178,14 +178,14 @@ async with AsyncDeepsetClient() as client:
converter:
type: TextFileToDocument
"""

await indexes.create(
name="my-new-index",
index_name="my-new-index",
yaml_config=index_yaml,
description="My document index"
)
updated_yaml = """

updated_yaml = """
document_store:
type: OpenSearchDocumentStore
indexing_pipeline:
Expand All @@ -194,13 +194,13 @@ async with AsyncDeepsetClient() as client:
converter:
type: TextFileToDocument
"""
# Update an existing index
await indexes.update(
index_name="my-index",
updated_index_name="my-renamed-index",
yaml_config=updated_yaml
)

# Update an existing index
await indexes.update(
index_name="my-index",
updated_index_name="my-renamed-index",
yaml_config=updated_yaml
)
```

### Pipeline Templates
Expand Down
6 changes: 3 additions & 3 deletions src/deepset_mcp/api/indexes/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from datetime import datetime
from typing import Any

from pydantic import BaseModel
from pydantic import BaseModel, Field
from rich.repr import Result

from deepset_mcp.api.shared_models import DeepsetUser
Expand All @@ -27,7 +27,7 @@ class Index(BaseModel):
pipeline_index_id: str
name: str
description: str | None = None
config_yaml: str
yaml_config: str = Field(alias="config_yaml")
workspace_id: str
settings: dict[str, Any]
desired_status: str
Expand Down Expand Up @@ -56,7 +56,7 @@ def __rich_repr__(self) -> Result:
else None,
)
yield "last_edited_at", self.last_edited_at.strftime("%m/%d/%Y %I:%M:%S %p") if self.last_edited_at else None
yield "config_yaml", self.config_yaml if self.config_yaml is not None else "Get full index to see config."
yield "yaml_config", self.yaml_config if self.yaml_config is not None else "Get full index to see config."


class IndexList(BaseModel):
Expand Down
30 changes: 21 additions & 9 deletions src/deepset_mcp/api/indexes/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
#
# SPDX-License-Identifier: Apache-2.0

from urllib.parse import quote

from deepset_mcp.api.exceptions import UnexpectedAPIError
from deepset_mcp.api.indexes.models import Index, IndexList
from deepset_mcp.api.indexes.protocols import IndexResourceProtocol
Expand Down Expand Up @@ -35,7 +37,9 @@ async def list(self, limit: int = 10, page_number: int = 1) -> IndexList:
"page_number": page_number,
}

response = await self._client.request(f"/v1/workspaces/{self._workspace}/indexes", params=params)
response = await self._client.request(
f"/v1/workspaces/{quote(self._workspace, safe='')}/indexes", params=params
)

raise_for_status(response)

Expand All @@ -48,28 +52,32 @@ async def get(self, index_name: str) -> Index:

:returns: Index details.
"""
response = await self._client.request(f"/v1/workspaces/{self._workspace}/indexes/{index_name}")
response = await self._client.request(
f"/v1/workspaces/{quote(self._workspace, safe='')}/indexes/{quote(index_name, safe='')}"
)

raise_for_status(response)

return Index.model_validate(response.json)

async def create(self, name: str, yaml_config: str, description: str | None = None) -> Index:
async def create(self, index_name: str, yaml_config: str, description: str | None = None) -> Index:
"""Create a new index with the given name and configuration.

:param name: Name of the index
:param index_name: Name of the index
:param yaml_config: YAML configuration for the index
:param description: Optional description for the index
:returns: Created index details
"""
data = {
"name": name,
"name": index_name,
"config_yaml": yaml_config,
}
if description is not None:
data["description"] = description

response = await self._client.request(f"v1/workspaces/{self._workspace}/indexes", method="POST", data=data)
response = await self._client.request(
f"v1/workspaces/{quote(self._workspace, safe='')}/indexes", method="POST", data=data
)

raise_for_status(response)

Expand All @@ -95,7 +103,9 @@ async def update(
raise ValueError("At least one of updated_index_name or yaml_config must be provided")

response = await self._client.request(
f"/v1/workspaces/{self._workspace}/indexes/{index_name}", method="PATCH", data=data
f"/v1/workspaces/{quote(self._workspace, safe='')}/indexes/{quote(index_name, safe='')}",
method="PATCH",
data=data,
)

raise_for_status(response)
Expand All @@ -107,7 +117,9 @@ async def delete(self, index_name: str) -> None:

:param index_name: Name of the index to delete.
"""
response = await self._client.request(f"/v1/workspaces/{self._workspace}/indexes/{index_name}", method="DELETE")
response = await self._client.request(
f"/v1/workspaces/{quote(self._workspace, safe='')}/indexes/{quote(index_name, safe='')}", method="DELETE"
)

raise_for_status(response)

Expand All @@ -119,7 +131,7 @@ async def deploy(self, index_name: str) -> PipelineValidationResult:
:raises UnexpectedAPIError: If the API returns an unexpected status code.
"""
resp = await self._client.request(
endpoint=f"v1/workspaces/{self._workspace}/indexes/{index_name}/deploy",
endpoint=f"v1/workspaces/{quote(self._workspace, safe='')}/indexes/{quote(index_name, safe='')}/deploy",
method="POST",
)

Expand Down
22 changes: 11 additions & 11 deletions test/integration/test_integration_index_resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def valid_index_config() -> str:
"""Return a valid index YAML configuration for testing."""
return json.dumps(
{
"config_yaml": """
"yaml_config": """
components:
file_classifier:
type: haystack.components.routers.file_type_router.FileTypeRouter
Expand Down Expand Up @@ -212,14 +212,14 @@ async def test_create_index(
# Create a new index
config = json.loads(valid_index_config)
await index_resource.create(
name=default_index_name, yaml_config=config["config_yaml"], description="Test index description"
index_name=default_index_name, yaml_config=config["yaml_config"], description="Test index description"
)

# Verify the index was created by retrieving it
index: Index = await index_resource.get(index_name=default_index_name)

assert index.name == default_index_name
assert index.config_yaml == config["config_yaml"]
assert index.yaml_config == config["yaml_config"]


@pytest.mark.asyncio
Expand All @@ -234,7 +234,7 @@ async def test_list_indexes(
for i in range(3):
index_name = f"test-list-index-{i}"
index_names.append(index_name)
await index_resource.create(name=index_name, yaml_config=config["config_yaml"])
await index_resource.create(index_name=index_name, yaml_config=config["yaml_config"])

# Test listing without pagination
indexes = await index_resource.list(limit=10)
Expand Down Expand Up @@ -268,12 +268,12 @@ async def test_get_index(
"""Test getting a single index by name."""
# Create an index to retrieve
config = json.loads(valid_index_config)
await index_resource.create(name=default_index_name, yaml_config=config["config_yaml"])
await index_resource.create(index_name=default_index_name, yaml_config=config["yaml_config"])

# Test getting the index
index: Index = await index_resource.get(index_name=default_index_name)
assert index.name == default_index_name
assert index.config_yaml == config["config_yaml"]
assert index.yaml_config == config["yaml_config"]


@pytest.mark.asyncio
Expand All @@ -287,7 +287,7 @@ async def test_update_index(

# Create an index to update
config = json.loads(valid_index_config)
await index_resource.create(name=original_name, yaml_config=config["config_yaml"])
await index_resource.create(index_name=original_name, yaml_config=config["yaml_config"])

# Update the index name
await index_resource.update(
Expand All @@ -300,15 +300,15 @@ async def test_update_index(
assert updated_index.name == updated_name

# Update the index config
modified_yaml = config["config_yaml"].replace("split_length: 250", "split_length: 300")
modified_yaml = config["yaml_config"].replace("split_length: 250", "split_length: 300")
await index_resource.update(
index_name=updated_name,
yaml_config=modified_yaml,
)

# Verify the config was updated
updated_index = await index_resource.get(index_name=updated_name)
assert updated_index.config_yaml == modified_yaml
assert updated_index.yaml_config == modified_yaml


@pytest.mark.asyncio
Expand All @@ -333,7 +333,7 @@ async def test_delete_index(

# Create an index to delete
config = json.loads(valid_index_config)
await index_resource.create(name=index_name, yaml_config=config["config_yaml"])
await index_resource.create(index_name=index_name, yaml_config=config["yaml_config"])

# Verify the index exists
index: Index = await index_resource.get(index_name=index_name)
Expand All @@ -357,7 +357,7 @@ async def test_deploy_index_success(

# Create an index to deploy
config = json.loads(valid_index_config)
await index_resource.create(name=index_name, yaml_config=config["config_yaml"])
await index_resource.create(index_name=index_name, yaml_config=config["yaml_config"])

# Deploy the index
result = await index_resource.deploy(index_name=index_name)
Expand Down
6 changes: 4 additions & 2 deletions test/unit/api/indexes/test_index_resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,9 @@ async def test_create_index_successful(
)

resource = IndexResource(fake_client, workspace)
result = await resource.create(name="test-index", yaml_config="yaml: content", description="Test description")
result = await resource.create(
index_name="test-index", yaml_config="yaml: content", description="Test description"
)

assert isinstance(result, Index)
assert result.name == "test-index"
Expand Down Expand Up @@ -239,7 +241,7 @@ async def test_create_index_invalid_request(
"""Test that creating an index with invalid parameters raises an error."""
resource = IndexResource(fake_client, workspace)
with pytest.raises(BadRequestError):
await resource.create(name="invalid-index", yaml_config="invalid: yaml")
await resource.create(index_name="invalid-index", yaml_config="invalid: yaml")

async def test_update_nonexistent_index(
self, fake_client: BaseFakeClient, workspace: str, fake_update_404_response: None
Expand Down
54 changes: 26 additions & 28 deletions test/unit/tools/test_indexes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@
#
# SPDX-License-Identifier: Apache-2.0

from datetime import datetime

import pytest

from deepset_mcp.api.exceptions import BadRequestError, ResourceNotFoundError, UnexpectedAPIError
from deepset_mcp.api.indexes.models import Index, IndexList, IndexStatus
from deepset_mcp.api.indexes.models import Index, IndexList
from deepset_mcp.api.indexes.protocols import IndexResourceProtocol
from deepset_mcp.api.pipeline.models import PipelineValidationResult, ValidationError
from deepset_mcp.api.shared_models import DeepsetUser
from deepset_mcp.tools.indexes import create_index, deploy_index, get_index, list_indexes, update_index
from test.unit.conftest import BaseFakeClient

Expand Down Expand Up @@ -94,34 +92,34 @@ def indexes(self, workspace: str) -> FakeIndexResource:
def create_test_index(
name: str = "test_index",
description: str | None = "Test index description",
config_yaml: str = "config: value",
yaml_config: str = "config: value",
) -> Index:
"""Helper function to create a complete Index object for testing."""
user = DeepsetUser(user_id="u1", given_name="Test", family_name="User")
status = IndexStatus(
pending_file_count=0,
failed_file_count=0,
indexed_no_documents_file_count=0,
indexed_file_count=10,
total_file_count=10,
)

return Index(
pipeline_index_id="idx_123",
name=name,
description=description,
config_yaml=config_yaml,
workspace_id="ws_123",
settings={"key": "value"},
desired_status="DEPLOYED",
deployed_at=datetime(2023, 1, 1, 12, 0),
last_edited_at=datetime(2023, 1, 2, 14, 30),
max_index_replica_count=3,
created_at=datetime(2023, 1, 1, 10, 0),
updated_at=datetime(2023, 1, 2, 14, 30),
created_by=user,
last_edited_by=user,
status=status,
return Index.model_validate(
{
"pipeline_index_id": "idx_123",
"name": name,
"description": description,
"config_yaml": yaml_config,
"workspace_id": "ws_123",
"settings": {"key": "value"},
"desired_status": "DEPLOYED",
"deployed_at": "2023-01-01T12:00:00Z",
"last_edited_at": "2023-01-02T14:30:00Z",
"max_index_replica_count": 3,
"created_at": "2023-01-01T10:00:00Z",
"updated_at": "2023-01-02T14:30:00Z",
"created_by": {"user_id": "u1", "given_name": "Test", "family_name": "User"},
"last_edited_by": {"user_id": "u1", "given_name": "Test", "family_name": "User"},
"status": {
"pending_file_count": 0,
"failed_file_count": 0,
"indexed_no_documents_file_count": 0,
"indexed_file_count": 10,
"total_file_count": 10,
},
}
)


Expand Down