Skip to content

Commit 3d2078a

Browse files
committed
Unified vector_store api delete endpoints
1 parent 9b84aaf commit 3d2078a

3 files changed

Lines changed: 124 additions & 40 deletions

File tree

src/app/endpoints/vector_stores.py

Lines changed: 42 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
NotFoundResponse,
3838
ServiceUnavailableResponse,
3939
UnauthorizedResponse,
40+
VectorStoreDeleteResponse,
41+
VectorStoreFileDeleteResponse,
4042
VectorStoreFileResponse,
4143
VectorStoreFilesListResponse,
4244
VectorStoreResponse,
@@ -104,6 +106,26 @@
104106
),
105107
}
106108

109+
vector_store_delete_responses: dict[int | str, dict[str, Any]] = {
110+
200: VectorStoreDeleteResponse.openapi_response(),
111+
401: UnauthorizedResponse.openapi_response(examples=UNAUTHORIZED_OPENAPI_EXAMPLES),
112+
403: ForbiddenResponse.openapi_response(examples=["endpoint"]),
113+
500: InternalServerErrorResponse.openapi_response(examples=["configuration"]),
114+
503: ServiceUnavailableResponse.openapi_response(
115+
examples=["llama stack", "kubernetes api"]
116+
),
117+
}
118+
119+
vector_store_file_delete_responses: dict[int | str, dict[str, Any]] = {
120+
200: VectorStoreFileDeleteResponse.openapi_response(),
121+
401: UnauthorizedResponse.openapi_response(examples=UNAUTHORIZED_OPENAPI_EXAMPLES),
122+
403: ForbiddenResponse.openapi_response(examples=["endpoint"]),
123+
500: InternalServerErrorResponse.openapi_response(examples=["configuration"]),
124+
503: ServiceUnavailableResponse.openapi_response(
125+
examples=["llama stack", "kubernetes api"]
126+
),
127+
}
128+
107129

108130
@router.post("/vector-stores", responses=vector_store_responses)
109131
@authorize(Action.MANAGE_VECTOR_STORES)
@@ -285,7 +307,7 @@ async def get_vector_store(
285307
except BadRequestError as e:
286308
logger.error("Vector store not found: %s", e)
287309
response = NotFoundResponse(
288-
resource="vector_store", resource_id=vector_store_id
310+
resource="vector store", resource_id=vector_store_id
289311
)
290312
raise HTTPException(**response.model_dump()) from e
291313
except (LLSApiStatusError, OpenAIAPIStatusError) as e:
@@ -349,7 +371,7 @@ async def update_vector_store(
349371
except BadRequestError as e:
350372
logger.error("Vector store not found: %s", e)
351373
response = NotFoundResponse(
352-
resource="vector_store", resource_id=vector_store_id
374+
resource="vector store", resource_id=vector_store_id
353375
)
354376
raise HTTPException(**response.model_dump()) from e
355377
except (LLSApiStatusError, OpenAIAPIStatusError) as e:
@@ -360,20 +382,14 @@ async def update_vector_store(
360382

361383
@router.delete(
362384
"/vector-stores/{vector_store_id}",
363-
responses={
364-
"204": {"description": "Vector store deleted"},
365-
503: ServiceUnavailableResponse.openapi_response(
366-
examples=["llama stack", "kubernetes api"]
367-
),
368-
},
369-
status_code=status.HTTP_204_NO_CONTENT,
385+
responses=vector_store_delete_responses,
370386
)
371387
@authorize(Action.MANAGE_VECTOR_STORES)
372388
async def delete_vector_store(
373389
request: Request,
374390
vector_store_id: str,
375391
auth: Annotated[AuthTuple, Depends(get_auth_dependency())],
376-
) -> None:
392+
) -> VectorStoreDeleteResponse:
377393
"""Delete a vector store.
378394
379395
Parameters:
@@ -385,9 +401,11 @@ async def delete_vector_store(
385401
HTTPException:
386402
- 401: Authentication failed
387403
- 403: Authorization failed
388-
- 404: Vector store not found
389404
- 500: Lightspeed Stack configuration not loaded
390405
- 503: Unable to connect to Llama Stack
406+
407+
Returns:
408+
VectorStoreDeleteResponse: Delete outcome for the requested vector store.
391409
"""
392410
_ = auth
393411
_ = request
@@ -397,16 +415,14 @@ async def delete_vector_store(
397415
try:
398416
client = AsyncLlamaStackClientHolder().get_client()
399417
await client.vector_stores.delete(vector_store_id)
418+
return VectorStoreDeleteResponse(deleted=True, vector_store_id=vector_store_id)
400419
except APIConnectionError as e:
401420
logger.error("Unable to connect to Llama Stack: %s", e)
402421
response = ServiceUnavailableResponse(backend_name="Llama Stack", cause=str(e))
403422
raise HTTPException(**response.model_dump()) from e
404-
except BadRequestError as e:
405-
logger.error("Vector store not found: %s", e)
406-
response = NotFoundResponse(
407-
resource="vector_store", resource_id=vector_store_id
408-
)
409-
raise HTTPException(**response.model_dump()) from e
423+
except (BadRequestError, ValueError) as e:
424+
logger.error("Vector store delete failed: %s", e)
425+
return VectorStoreDeleteResponse(deleted=False, vector_store_id=vector_store_id)
410426
except (LLSApiStatusError, OpenAIAPIStatusError) as e:
411427
logger.error("API status error while deleting vector store: %s", e)
412428
error_response = handle_known_apistatus_errors(e, "llama-stack")
@@ -790,21 +806,15 @@ async def get_vector_store_file(
790806

791807
@router.delete(
792808
"/vector-stores/{vector_store_id}/files/{file_id}",
793-
responses={
794-
"204": {"description": "File deleted from vector store"},
795-
503: ServiceUnavailableResponse.openapi_response(
796-
examples=["llama stack", "kubernetes api"]
797-
),
798-
},
799-
status_code=status.HTTP_204_NO_CONTENT,
809+
responses=vector_store_file_delete_responses,
800810
)
801811
@authorize(Action.MANAGE_VECTOR_STORES)
802812
async def delete_vector_store_file(
803813
request: Request,
804814
vector_store_id: str,
805815
file_id: str,
806816
auth: Annotated[AuthTuple, Depends(get_auth_dependency())],
807-
) -> None:
817+
) -> VectorStoreFileDeleteResponse:
808818
"""Delete a file from a vector store.
809819
810820
Parameters:
@@ -817,9 +827,11 @@ async def delete_vector_store_file(
817827
HTTPException:
818828
- 401: Authentication failed
819829
- 403: Authorization failed
820-
- 404: File not found in vector store
821830
- 500: Lightspeed Stack configuration not loaded
822831
- 503: Unable to connect to Llama Stack
832+
833+
Returns:
834+
VectorStoreFileDeleteResponse: Delete outcome for the requested file.
823835
"""
824836
_ = auth
825837
_ = request
@@ -832,14 +844,14 @@ async def delete_vector_store_file(
832844
vector_store_id=vector_store_id,
833845
file_id=file_id,
834846
)
847+
return VectorStoreFileDeleteResponse(deleted=True, file_id=file_id)
835848
except APIConnectionError as e:
836849
logger.error("Unable to connect to Llama Stack: %s", e)
837850
response = ServiceUnavailableResponse(backend_name="Llama Stack", cause=str(e))
838851
raise HTTPException(**response.model_dump()) from e
839-
except BadRequestError as e:
840-
logger.error("Vector store file not found: %s", e)
841-
response = NotFoundResponse(resource="file", resource_id=file_id)
842-
raise HTTPException(**response.model_dump()) from e
852+
except (BadRequestError, ValueError) as e:
853+
logger.error("Vector store file delete failed: %s", e)
854+
return VectorStoreFileDeleteResponse(deleted=False, file_id=file_id)
843855
except (LLSApiStatusError, OpenAIAPIStatusError) as e:
844856
logger.error("API status error while deleting vector store file: %s", e)
845857
error_response = handle_known_apistatus_errors(e, "llama-stack")

src/models/responses.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2968,6 +2968,74 @@ class VectorStoresListResponse(AbstractSuccessfulResponse):
29682968
}
29692969

29702970

2971+
class VectorStoreDeleteResponse(AbstractDeleteResponse):
2972+
"""Result of deleting a vector store (always HTTP 200)."""
2973+
2974+
resource_name: ClassVar[str] = "Vector store"
2975+
vector_store_id: str = Field(
2976+
...,
2977+
description="Vector store identifier that was passed to delete.",
2978+
examples=["vs_abc123"],
2979+
)
2980+
2981+
model_config = {
2982+
"json_schema_extra": {
2983+
"examples": [
2984+
{
2985+
"label": "deleted",
2986+
"value": {
2987+
"vector_store_id": "vs_abc123",
2988+
"deleted": True,
2989+
"response": "Vector store deleted successfully",
2990+
},
2991+
},
2992+
{
2993+
"label": "not found",
2994+
"value": {
2995+
"vector_store_id": "vs_abc123",
2996+
"deleted": False,
2997+
"response": "Vector store not found",
2998+
},
2999+
},
3000+
]
3001+
}
3002+
}
3003+
3004+
3005+
class VectorStoreFileDeleteResponse(AbstractDeleteResponse):
3006+
"""Result of deleting a file from a vector store (always HTTP 200)."""
3007+
3008+
resource_name: ClassVar[str] = "Vector store file"
3009+
file_id: str = Field(
3010+
...,
3011+
description="File identifier that was passed to delete.",
3012+
examples=["file_abc123"],
3013+
)
3014+
3015+
model_config = {
3016+
"json_schema_extra": {
3017+
"examples": [
3018+
{
3019+
"label": "deleted",
3020+
"value": {
3021+
"file_id": "file_abc123",
3022+
"deleted": True,
3023+
"response": "Vector store file deleted successfully",
3024+
},
3025+
},
3026+
{
3027+
"label": "not found",
3028+
"value": {
3029+
"file_id": "file_abc123",
3030+
"deleted": False,
3031+
"response": "Vector store file not found",
3032+
},
3033+
},
3034+
]
3035+
}
3036+
}
3037+
3038+
29713039
class PromptResourceResponse(AbstractSuccessfulResponse):
29723040
"""A stored prompt template as returned by Llama Stack."""
29733041

tests/unit/app/endpoints/test_vector_stores.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,8 @@ async def test_delete_vector_store_success(mocker: MockerFixture) -> None:
376376
response = await delete_vector_store(
377377
request=request, vector_store_id="vs_123", auth=auth
378378
)
379-
assert response is None
379+
assert response.deleted is True
380+
assert response.vector_store_id == "vs_123"
380381

381382

382383
@pytest.mark.asyncio
@@ -658,7 +659,8 @@ async def test_delete_vector_store_file_success(mocker: MockerFixture) -> None:
658659
response = await delete_vector_store_file(
659660
request=request, vector_store_id="vs_123", file_id="file_123", auth=auth
660661
)
661-
assert response is None
662+
assert response.deleted is True
663+
assert response.file_id == "file_123"
662664

663665

664666
# Additional error path tests
@@ -798,9 +800,11 @@ async def test_delete_vector_store_not_found(mocker: MockerFixture) -> None:
798800
request = get_test_request()
799801
auth = get_test_auth()
800802

801-
with pytest.raises(HTTPException) as e:
802-
await delete_vector_store(request=request, vector_store_id="vs_999", auth=auth)
803-
assert e.value.status_code == status.HTTP_404_NOT_FOUND
803+
response = await delete_vector_store(
804+
request=request, vector_store_id="vs_999", auth=auth
805+
)
806+
assert response.deleted is False
807+
assert response.vector_store_id == "vs_999"
804808

805809

806810
@pytest.mark.asyncio
@@ -1172,8 +1176,8 @@ async def test_delete_vector_store_file_not_found(mocker: MockerFixture) -> None
11721176
request = get_test_request()
11731177
auth = get_test_auth()
11741178

1175-
with pytest.raises(HTTPException) as e:
1176-
await delete_vector_store_file(
1177-
request=request, vector_store_id="vs_123", file_id="file_999", auth=auth
1178-
)
1179-
assert e.value.status_code == status.HTTP_404_NOT_FOUND
1179+
response = await delete_vector_store_file(
1180+
request=request, vector_store_id="vs_123", file_id="file_999", auth=auth
1181+
)
1182+
assert response.deleted is False
1183+
assert response.file_id == "file_999"

0 commit comments

Comments
 (0)