Skip to content

Commit a5ff895

Browse files
committed
Fix DLS ingestion mapping client
1 parent a24630b commit a5ff895

5 files changed

Lines changed: 127 additions & 1 deletion

File tree

src/models/processors.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,9 +312,13 @@ async def process_document_standard(
312312

313313
dimensions = len(embeddings[0])
314314

315+
# Mapping updates are index-admin operations and fail under document-level
316+
# security when attempted with a user-scoped OpenSearch client.
317+
mapping_client = clients.opensearch or opensearch_client
318+
315319
# Ensure the embedding field exists for this model
316320
embedding_field_name = await ensure_embedding_field_exists(
317-
opensearch_client, embedding_model, get_index_name(), dimensions
321+
mapping_client, embedding_model, get_index_name(), dimensions
318322
)
319323

320324
# Index each chunk as a separate document

tests/integration/core/test_api_endpoints.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,11 @@ async def test_upload_and_search_endpoint(tmp_path: Path, disable_langflow_inges
165165
"services.startup_orchestrator",
166166
"app.routes.internal",
167167
"app.routes",
168+
"app.container",
168169
"app.factory",
169170
"app.lifespan",
171+
"dependencies",
172+
"utils.opensearch_init",
170173
]:
171174
sys.modules.pop(mod, None)
172175
from config.settings import clients, get_index_name
@@ -396,8 +399,11 @@ async def test_langflow_chat_and_nudges_endpoints():
396399
"services.startup_orchestrator",
397400
"app.routes.internal",
398401
"app.routes",
402+
"app.container",
399403
"app.factory",
400404
"services.chat_service",
405+
"dependencies",
406+
"utils.opensearch_init",
401407
]:
402408
sys.modules.pop(mod, None)
403409

@@ -503,8 +509,11 @@ async def test_search_multi_embedding_models(tmp_path: Path):
503509
"services.startup_orchestrator",
504510
"app.routes.internal",
505511
"app.routes",
512+
"app.container",
506513
"app.factory",
507514
"app.lifespan",
515+
"dependencies",
516+
"utils.opensearch_init",
508517
]:
509518
sys.modules.pop(mod, None)
510519

@@ -647,8 +656,11 @@ async def test_router_upload_ingest_traditional(tmp_path: Path, disable_langflow
647656
"services.startup_orchestrator",
648657
"app.routes.internal",
649658
"app.routes",
659+
"app.container",
650660
"app.factory",
651661
"app.lifespan",
662+
"dependencies",
663+
"utils.opensearch_init",
652664
]:
653665
sys.modules.pop(mod, None)
654666
from config.settings import clients, get_index_name

tests/integration/core/test_onboarding_sample_docs.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
"services.default_docs_service",
3838
"services.search_service",
3939
"services.startup_orchestrator",
40+
"utils.opensearch_init",
4041
]
4142

4243

tests/integration/core/test_startup_ingest.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,21 @@ async def test_startup_ingest_creates_task(disable_langflow_ingest: bool):
5353
for mod in [
5454
"api.router",
5555
"api.connector_router",
56+
"api",
57+
"app.container",
58+
"app.factory",
59+
"app.lifespan",
60+
"app.routes",
61+
"app.routes.internal",
5662
"config.settings",
63+
"dependencies",
5764
"auth_middleware",
5865
"main",
66+
"services",
67+
"services.default_docs_service",
68+
"services.search_service",
69+
"services.startup_orchestrator",
70+
"utils.opensearch_init",
5971
]:
6072
sys.modules.pop(mod, None)
6173

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
from types import SimpleNamespace
2+
3+
import pytest
4+
5+
from models.processors import TaskProcessor
6+
7+
8+
@pytest.mark.asyncio
9+
async def test_standard_processor_uses_admin_client_for_embedding_mapping(
10+
tmp_path,
11+
monkeypatch,
12+
):
13+
user_client = SimpleNamespace(
14+
exists_calls=[],
15+
index_calls=[],
16+
)
17+
admin_client = object()
18+
mapping_clients = []
19+
20+
async def exists(*, index, id):
21+
user_client.exists_calls.append({"index": index, "id": id})
22+
return False
23+
24+
async def index(**kwargs):
25+
user_client.index_calls.append(kwargs)
26+
27+
user_client.exists = exists
28+
user_client.index = index
29+
30+
class SessionManager:
31+
def get_user_opensearch_client(self, user_id, jwt_token):
32+
assert user_id == "user-1"
33+
assert jwt_token == "Bearer user-token"
34+
return user_client
35+
36+
class ModelsService:
37+
async def get_litellm_model_name(self, embedding_model):
38+
return embedding_model
39+
40+
class EmbeddingClient:
41+
class Embeddings:
42+
async def create(self, model, input):
43+
return SimpleNamespace(
44+
data=[
45+
SimpleNamespace(embedding=[0.1, 0.2, 0.3])
46+
for _ in input
47+
]
48+
)
49+
50+
embeddings = Embeddings()
51+
52+
async def ensure_embedding_field_exists(client, model_name, index_name, dimensions):
53+
mapping_clients.append(client)
54+
assert model_name == "text-embedding-3-small"
55+
assert index_name == "documents"
56+
assert dimensions == 3
57+
return "chunk_embedding_text_embedding_3_small"
58+
59+
monkeypatch.setattr(
60+
"config.settings.clients",
61+
SimpleNamespace(
62+
opensearch=admin_client,
63+
patched_embedding_client=EmbeddingClient(),
64+
),
65+
)
66+
monkeypatch.setattr("config.settings.get_index_name", lambda: "documents")
67+
monkeypatch.setattr(
68+
"config.settings.get_openrag_config",
69+
lambda: SimpleNamespace(knowledge=SimpleNamespace(embedding_model="")),
70+
)
71+
monkeypatch.setattr(
72+
"utils.embedding_fields.ensure_embedding_field_exists",
73+
ensure_embedding_field_exists,
74+
)
75+
76+
file_path = tmp_path / "doc.md"
77+
file_path.write_text("# Test\n\nhello world", encoding="utf-8")
78+
document_service = SimpleNamespace(session_manager=SessionManager())
79+
processor = TaskProcessor(
80+
document_service=document_service,
81+
models_service=ModelsService(),
82+
docling_service=None,
83+
)
84+
85+
result = await processor.process_document_standard(
86+
file_path=str(file_path),
87+
file_hash="file-1",
88+
owner_user_id="user-1",
89+
original_filename="doc.md",
90+
jwt_token="Bearer user-token",
91+
embedding_model="text-embedding-3-small",
92+
)
93+
94+
assert result == {"status": "indexed", "id": "file-1"}
95+
assert mapping_clients == [admin_client]
96+
assert user_client.exists_calls == [{"index": "documents", "id": "file-1"}]
97+
assert user_client.index_calls

0 commit comments

Comments
 (0)