Skip to content

Commit e3e1bf3

Browse files
authored
Merge pull request #1505 from jrobertboos/release/0.5.1
LCORE-1780: Cherry Picked #1473 For 0.5.1
2 parents e24ff11 + 41143b3 commit e3e1bf3

10 files changed

Lines changed: 2717 additions & 6 deletions

File tree

src/app/endpoints/vector_stores.py

Lines changed: 840 additions & 0 deletions
Large diffs are not rendered by default.

src/app/routers.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
stream_interrupt,
2929
streaming_query,
3030
tools,
31+
vector_stores,
3132
)
3233

3334

@@ -53,6 +54,7 @@ def include_routers(app: FastAPI) -> None:
5354
app.include_router(shields.router, prefix="/v1")
5455
app.include_router(providers.router, prefix="/v1")
5556
app.include_router(rags.router, prefix="/v1")
57+
app.include_router(vector_stores.router, prefix="/v1")
5658
# Query endpoints
5759
app.include_router(query.router, prefix="/v1")
5860
app.include_router(streaming_query.router, prefix="/v1")

src/constants.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,10 @@
128128
DEFAULT_AUTHENTICATION_MODULE = AUTH_MOD_NOOP
129129
# Maximum allowed size for base64-encoded x-rh-identity header (bytes)
130130
DEFAULT_RH_IDENTITY_MAX_HEADER_SIZE = 8192
131+
132+
# Maximum allowed file upload size (bytes) - 100MB default
133+
# Protects against DoS attacks via large file uploads
134+
DEFAULT_MAX_FILE_UPLOAD_SIZE = 100 * 1024 * 1024 # 100 MB
131135
DEFAULT_JWT_UID_CLAIM = "user_id"
132136
DEFAULT_JWT_USER_NAME_CLAIM = "username"
133137

src/models/config.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1030,6 +1030,11 @@ class Action(str, Enum):
10301030
A2A_MESSAGE = "a2a_message"
10311031
A2A_JSONRPC = "a2a_jsonrpc"
10321032

1033+
# Vector store management
1034+
MANAGE_VECTOR_STORES = "manage_vector_stores"
1035+
READ_VECTOR_STORES = "read_vector_stores"
1036+
MANAGE_FILES = "manage_files"
1037+
10331038

10341039
class AccessRule(ConfigurationBase):
10351040
"""Rule defining what actions a role can perform."""

src/models/requests.py

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""Models for REST API requests."""
22

3+
# pylint: disable=too-many-lines
4+
35
from enum import Enum
46
from typing import Any, Optional, Self
57

@@ -935,3 +937,213 @@ def validate_authorization_header_values(
935937
"File-path based secrets are only supported in static YAML config."
936938
)
937939
return value
940+
941+
942+
class VectorStoreCreateRequest(BaseModel):
943+
"""Model representing a request to create a vector store.
944+
945+
Attributes:
946+
name: Name of the vector store.
947+
embedding_model: Optional embedding model to use.
948+
embedding_dimension: Optional embedding dimension.
949+
chunking_strategy: Optional chunking strategy configuration.
950+
provider_id: Optional vector store provider identifier.
951+
metadata: Optional metadata dictionary for storing session information.
952+
"""
953+
954+
name: str = Field(
955+
...,
956+
description="Name of the vector store",
957+
examples=["my_vector_store"],
958+
min_length=1,
959+
max_length=256,
960+
)
961+
962+
embedding_model: Optional[str] = Field(
963+
None,
964+
description="Embedding model to use for the vector store",
965+
examples=["text-embedding-ada-002"],
966+
)
967+
968+
embedding_dimension: Optional[int] = Field(
969+
None,
970+
description="Dimension of the embedding vectors",
971+
examples=[1536],
972+
gt=0,
973+
)
974+
975+
chunking_strategy: Optional[dict[str, Any]] = Field(
976+
None,
977+
description="Chunking strategy configuration",
978+
examples=[{"type": "fixed", "chunk_size": 512, "chunk_overlap": 50}],
979+
)
980+
981+
provider_id: Optional[str] = Field(
982+
None,
983+
description="Vector store provider identifier",
984+
examples=["rhdh-docs"],
985+
)
986+
987+
metadata: Optional[dict[str, Any]] = Field(
988+
None,
989+
description="Metadata dictionary for storing session information",
990+
examples=[{"user_id": "user123", "session_id": "sess456"}],
991+
)
992+
993+
model_config = {
994+
"extra": "forbid",
995+
"json_schema_extra": {
996+
"examples": [
997+
{
998+
"name": "my_vector_store",
999+
"embedding_model": "text-embedding-ada-002",
1000+
"embedding_dimension": 1536,
1001+
"provider_id": "rhdh-docs",
1002+
"metadata": {"user_id": "user123"},
1003+
},
1004+
]
1005+
},
1006+
}
1007+
1008+
1009+
class VectorStoreUpdateRequest(BaseModel):
1010+
"""Model representing a request to update a vector store.
1011+
1012+
Attributes:
1013+
name: New name for the vector store.
1014+
expires_at: Optional expiration timestamp.
1015+
metadata: Optional metadata dictionary for storing session information.
1016+
"""
1017+
1018+
name: Optional[str] = Field(
1019+
None,
1020+
description="New name for the vector store",
1021+
examples=["updated_vector_store"],
1022+
min_length=1,
1023+
max_length=256,
1024+
)
1025+
1026+
expires_at: Optional[int] = Field(
1027+
None,
1028+
description="Unix timestamp when the vector store should expire",
1029+
examples=[1735689600],
1030+
gt=0,
1031+
)
1032+
1033+
metadata: Optional[dict[str, Any]] = Field(
1034+
None,
1035+
description="Metadata dictionary for storing session information",
1036+
examples=[{"user_id": "user123", "session_id": "sess456"}],
1037+
)
1038+
1039+
model_config = {
1040+
"extra": "forbid",
1041+
"json_schema_extra": {
1042+
"examples": [
1043+
{
1044+
"name": "updated_vector_store",
1045+
"expires_at": 1735689600,
1046+
"metadata": {"user_id": "user123"},
1047+
},
1048+
]
1049+
},
1050+
}
1051+
1052+
@model_validator(mode="after")
1053+
def check_at_least_one_field(self) -> Self:
1054+
"""Ensure at least one field is provided for update.
1055+
1056+
Raises:
1057+
ValueError: If all fields are None (empty update).
1058+
1059+
Returns:
1060+
Self: The validated model instance.
1061+
"""
1062+
if self.name is None and self.expires_at is None and self.metadata is None:
1063+
raise ValueError(
1064+
"At least one field must be provided: name, expires_at, or metadata"
1065+
)
1066+
return self
1067+
1068+
1069+
class VectorStoreFileCreateRequest(BaseModel):
1070+
"""Model representing a request to add a file to a vector store.
1071+
1072+
Attributes:
1073+
file_id: ID of the file to add to the vector store.
1074+
attributes: Optional metadata key-value pairs (max 16 pairs).
1075+
chunking_strategy: Optional chunking strategy configuration.
1076+
"""
1077+
1078+
file_id: str = Field(
1079+
...,
1080+
description="ID of the file to add to the vector store",
1081+
examples=["file-abc123"],
1082+
min_length=1,
1083+
)
1084+
1085+
attributes: Optional[dict[str, str | float | bool]] = Field(
1086+
None,
1087+
description=(
1088+
"Set of up to 16 key-value pairs for storing additional information. "
1089+
"Keys: strings (max 64 chars). Values: strings (max 512 chars), booleans, or numbers."
1090+
),
1091+
examples=[
1092+
{"created_at": "2026-04-04T15:20:00Z", "updated_at": "2026-04-04T15:20:00Z"}
1093+
],
1094+
)
1095+
1096+
chunking_strategy: Optional[dict[str, Any]] = Field(
1097+
None,
1098+
description="Chunking strategy configuration for this file",
1099+
examples=[{"type": "fixed", "chunk_size": 512, "chunk_overlap": 50}],
1100+
)
1101+
1102+
model_config = {
1103+
"extra": "forbid",
1104+
"json_schema_extra": {
1105+
"examples": [
1106+
{
1107+
"file_id": "file-abc123",
1108+
"attributes": {"created_at": "2026-04-04T15:20:00Z"},
1109+
"chunking_strategy": {"type": "fixed", "chunk_size": 512},
1110+
},
1111+
]
1112+
},
1113+
}
1114+
1115+
@field_validator("attributes")
1116+
@classmethod
1117+
def validate_attributes(
1118+
cls, value: Optional[dict[str, str | float | bool]]
1119+
) -> Optional[dict[str, str | float | bool]]:
1120+
"""Validate attributes field constraints.
1121+
1122+
Ensures:
1123+
- Maximum 16 key-value pairs
1124+
- Keys are max 64 characters
1125+
- String values are max 512 characters
1126+
1127+
Parameters:
1128+
value: The attributes dictionary to validate.
1129+
1130+
Raises:
1131+
ValueError: If constraints are violated.
1132+
1133+
Returns:
1134+
The validated attributes dictionary.
1135+
"""
1136+
if value is None:
1137+
return value
1138+
1139+
if len(value) > 16:
1140+
raise ValueError(f"attributes can have at most 16 pairs, got {len(value)}")
1141+
1142+
for key, val in value.items():
1143+
if len(key) > 64:
1144+
raise ValueError(f"attribute key '{key}' exceeds 64 characters")
1145+
1146+
if isinstance(val, str) and len(val) > 512:
1147+
raise ValueError(f"attribute value for '{key}' exceeds 512 characters")
1148+
1149+
return value

0 commit comments

Comments
 (0)