julee 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- julee/__init__.py +3 -0
- julee/api/__init__.py +20 -0
- julee/api/app.py +180 -0
- julee/api/dependencies.py +257 -0
- julee/api/requests.py +175 -0
- julee/api/responses.py +43 -0
- julee/api/routers/__init__.py +43 -0
- julee/api/routers/assembly_specifications.py +212 -0
- julee/api/routers/documents.py +182 -0
- julee/api/routers/knowledge_service_configs.py +79 -0
- julee/api/routers/knowledge_service_queries.py +293 -0
- julee/api/routers/system.py +137 -0
- julee/api/routers/workflows.py +234 -0
- julee/api/services/__init__.py +20 -0
- julee/api/services/system_initialization.py +214 -0
- julee/api/tests/__init__.py +14 -0
- julee/api/tests/routers/__init__.py +17 -0
- julee/api/tests/routers/test_assembly_specifications.py +749 -0
- julee/api/tests/routers/test_documents.py +301 -0
- julee/api/tests/routers/test_knowledge_service_configs.py +234 -0
- julee/api/tests/routers/test_knowledge_service_queries.py +738 -0
- julee/api/tests/routers/test_system.py +179 -0
- julee/api/tests/routers/test_workflows.py +393 -0
- julee/api/tests/test_app.py +285 -0
- julee/api/tests/test_dependencies.py +245 -0
- julee/api/tests/test_requests.py +250 -0
- julee/domain/__init__.py +22 -0
- julee/domain/models/__init__.py +49 -0
- julee/domain/models/assembly/__init__.py +17 -0
- julee/domain/models/assembly/assembly.py +103 -0
- julee/domain/models/assembly/tests/__init__.py +0 -0
- julee/domain/models/assembly/tests/factories.py +37 -0
- julee/domain/models/assembly/tests/test_assembly.py +430 -0
- julee/domain/models/assembly_specification/__init__.py +24 -0
- julee/domain/models/assembly_specification/assembly_specification.py +172 -0
- julee/domain/models/assembly_specification/knowledge_service_query.py +123 -0
- julee/domain/models/assembly_specification/tests/__init__.py +0 -0
- julee/domain/models/assembly_specification/tests/factories.py +78 -0
- julee/domain/models/assembly_specification/tests/test_assembly_specification.py +490 -0
- julee/domain/models/assembly_specification/tests/test_knowledge_service_query.py +310 -0
- julee/domain/models/custom_fields/__init__.py +0 -0
- julee/domain/models/custom_fields/content_stream.py +68 -0
- julee/domain/models/custom_fields/tests/__init__.py +0 -0
- julee/domain/models/custom_fields/tests/test_custom_fields.py +53 -0
- julee/domain/models/document/__init__.py +17 -0
- julee/domain/models/document/document.py +150 -0
- julee/domain/models/document/tests/__init__.py +0 -0
- julee/domain/models/document/tests/factories.py +76 -0
- julee/domain/models/document/tests/test_document.py +297 -0
- julee/domain/models/knowledge_service_config/__init__.py +17 -0
- julee/domain/models/knowledge_service_config/knowledge_service_config.py +86 -0
- julee/domain/models/policy/__init__.py +15 -0
- julee/domain/models/policy/document_policy_validation.py +220 -0
- julee/domain/models/policy/policy.py +203 -0
- julee/domain/models/policy/tests/__init__.py +0 -0
- julee/domain/models/policy/tests/factories.py +47 -0
- julee/domain/models/policy/tests/test_document_policy_validation.py +420 -0
- julee/domain/models/policy/tests/test_policy.py +546 -0
- julee/domain/repositories/__init__.py +27 -0
- julee/domain/repositories/assembly.py +45 -0
- julee/domain/repositories/assembly_specification.py +52 -0
- julee/domain/repositories/base.py +146 -0
- julee/domain/repositories/document.py +49 -0
- julee/domain/repositories/document_policy_validation.py +52 -0
- julee/domain/repositories/knowledge_service_config.py +54 -0
- julee/domain/repositories/knowledge_service_query.py +44 -0
- julee/domain/repositories/policy.py +49 -0
- julee/domain/use_cases/__init__.py +17 -0
- julee/domain/use_cases/decorators.py +107 -0
- julee/domain/use_cases/extract_assemble_data.py +649 -0
- julee/domain/use_cases/initialize_system_data.py +842 -0
- julee/domain/use_cases/tests/__init__.py +7 -0
- julee/domain/use_cases/tests/test_extract_assemble_data.py +548 -0
- julee/domain/use_cases/tests/test_initialize_system_data.py +455 -0
- julee/domain/use_cases/tests/test_validate_document.py +1228 -0
- julee/domain/use_cases/validate_document.py +736 -0
- julee/fixtures/assembly_specifications.yaml +70 -0
- julee/fixtures/documents.yaml +178 -0
- julee/fixtures/knowledge_service_configs.yaml +37 -0
- julee/fixtures/knowledge_service_queries.yaml +27 -0
- julee/repositories/__init__.py +17 -0
- julee/repositories/memory/__init__.py +31 -0
- julee/repositories/memory/assembly.py +84 -0
- julee/repositories/memory/assembly_specification.py +125 -0
- julee/repositories/memory/base.py +227 -0
- julee/repositories/memory/document.py +149 -0
- julee/repositories/memory/document_policy_validation.py +104 -0
- julee/repositories/memory/knowledge_service_config.py +123 -0
- julee/repositories/memory/knowledge_service_query.py +120 -0
- julee/repositories/memory/policy.py +87 -0
- julee/repositories/memory/tests/__init__.py +0 -0
- julee/repositories/memory/tests/test_document.py +212 -0
- julee/repositories/memory/tests/test_document_policy_validation.py +161 -0
- julee/repositories/memory/tests/test_policy.py +443 -0
- julee/repositories/minio/__init__.py +31 -0
- julee/repositories/minio/assembly.py +103 -0
- julee/repositories/minio/assembly_specification.py +170 -0
- julee/repositories/minio/client.py +570 -0
- julee/repositories/minio/document.py +530 -0
- julee/repositories/minio/document_policy_validation.py +120 -0
- julee/repositories/minio/knowledge_service_config.py +187 -0
- julee/repositories/minio/knowledge_service_query.py +211 -0
- julee/repositories/minio/policy.py +106 -0
- julee/repositories/minio/tests/__init__.py +0 -0
- julee/repositories/minio/tests/fake_client.py +213 -0
- julee/repositories/minio/tests/test_assembly.py +374 -0
- julee/repositories/minio/tests/test_assembly_specification.py +391 -0
- julee/repositories/minio/tests/test_client_protocol.py +57 -0
- julee/repositories/minio/tests/test_document.py +591 -0
- julee/repositories/minio/tests/test_document_policy_validation.py +192 -0
- julee/repositories/minio/tests/test_knowledge_service_config.py +374 -0
- julee/repositories/minio/tests/test_knowledge_service_query.py +438 -0
- julee/repositories/minio/tests/test_policy.py +559 -0
- julee/repositories/temporal/__init__.py +38 -0
- julee/repositories/temporal/activities.py +114 -0
- julee/repositories/temporal/activity_names.py +34 -0
- julee/repositories/temporal/proxies.py +159 -0
- julee/services/__init__.py +18 -0
- julee/services/knowledge_service/__init__.py +48 -0
- julee/services/knowledge_service/anthropic/__init__.py +12 -0
- julee/services/knowledge_service/anthropic/knowledge_service.py +331 -0
- julee/services/knowledge_service/anthropic/tests/test_knowledge_service.py +318 -0
- julee/services/knowledge_service/factory.py +138 -0
- julee/services/knowledge_service/knowledge_service.py +160 -0
- julee/services/knowledge_service/memory/__init__.py +13 -0
- julee/services/knowledge_service/memory/knowledge_service.py +278 -0
- julee/services/knowledge_service/memory/test_knowledge_service.py +345 -0
- julee/services/knowledge_service/test_factory.py +112 -0
- julee/services/temporal/__init__.py +38 -0
- julee/services/temporal/activities.py +86 -0
- julee/services/temporal/activity_names.py +22 -0
- julee/services/temporal/proxies.py +41 -0
- julee/util/__init__.py +0 -0
- julee/util/domain.py +119 -0
- julee/util/repos/__init__.py +0 -0
- julee/util/repos/minio/__init__.py +0 -0
- julee/util/repos/minio/file_storage.py +213 -0
- julee/util/repos/temporal/__init__.py +11 -0
- julee/util/repos/temporal/client_proxies/file_storage.py +68 -0
- julee/util/repos/temporal/data_converter.py +123 -0
- julee/util/repos/temporal/minio_file_storage.py +12 -0
- julee/util/repos/temporal/proxies/__init__.py +0 -0
- julee/util/repos/temporal/proxies/file_storage.py +58 -0
- julee/util/repositories.py +55 -0
- julee/util/temporal/__init__.py +22 -0
- julee/util/temporal/activities.py +123 -0
- julee/util/temporal/decorators.py +473 -0
- julee/util/tests/__init__.py +1 -0
- julee/util/tests/test_decorators.py +770 -0
- julee/util/validation/__init__.py +29 -0
- julee/util/validation/repository.py +100 -0
- julee/util/validation/type_guards.py +369 -0
- julee/worker.py +211 -0
- julee/workflows/__init__.py +26 -0
- julee/workflows/extract_assemble.py +215 -0
- julee/workflows/validate_document.py +228 -0
- julee-0.1.0.dist-info/METADATA +195 -0
- julee-0.1.0.dist-info/RECORD +161 -0
- julee-0.1.0.dist-info/WHEEL +5 -0
- julee-0.1.0.dist-info/licenses/LICENSE +674 -0
- julee-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,738 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for the knowledge service queries API router.
|
|
3
|
+
|
|
4
|
+
This module provides comprehensive tests for the knowledge service queries
|
|
5
|
+
endpoints, focusing on testing the router behavior with proper dependency
|
|
6
|
+
injection and mocking patterns.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import pytest
|
|
10
|
+
from typing import Generator
|
|
11
|
+
from fastapi.testclient import TestClient
|
|
12
|
+
from fastapi import FastAPI
|
|
13
|
+
from fastapi_pagination import add_pagination
|
|
14
|
+
|
|
15
|
+
from julee.api.routers.knowledge_service_queries import router
|
|
16
|
+
from julee.api.dependencies import (
|
|
17
|
+
get_knowledge_service_query_repository,
|
|
18
|
+
)
|
|
19
|
+
from julee.domain.models import KnowledgeServiceQuery
|
|
20
|
+
from julee.repositories.memory import (
|
|
21
|
+
MemoryKnowledgeServiceQueryRepository,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@pytest.fixture
|
|
26
|
+
def memory_repo() -> MemoryKnowledgeServiceQueryRepository:
|
|
27
|
+
"""Create a memory knowledge service query repository for testing."""
|
|
28
|
+
return MemoryKnowledgeServiceQueryRepository()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@pytest.fixture
|
|
32
|
+
def app_with_router(
|
|
33
|
+
memory_repo: MemoryKnowledgeServiceQueryRepository,
|
|
34
|
+
) -> FastAPI:
|
|
35
|
+
"""Create a FastAPI app with just the knowledge service queries router."""
|
|
36
|
+
app = FastAPI()
|
|
37
|
+
|
|
38
|
+
# Override the dependency with our memory repository
|
|
39
|
+
app.dependency_overrides[get_knowledge_service_query_repository] = (
|
|
40
|
+
lambda: memory_repo
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# Add pagination support (required for the paginate function)
|
|
44
|
+
add_pagination(app)
|
|
45
|
+
|
|
46
|
+
# Include the router with the prefix
|
|
47
|
+
app.include_router(
|
|
48
|
+
router,
|
|
49
|
+
prefix="/knowledge_service_queries",
|
|
50
|
+
tags=["Knowledge Service Queries"],
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
return app
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@pytest.fixture
|
|
57
|
+
def client(
|
|
58
|
+
app_with_router: FastAPI,
|
|
59
|
+
) -> Generator[TestClient, None, None]:
|
|
60
|
+
"""Create a test client with the router app."""
|
|
61
|
+
with TestClient(app_with_router) as test_client:
|
|
62
|
+
yield test_client
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@pytest.fixture
|
|
66
|
+
def sample_knowledge_service_query() -> KnowledgeServiceQuery:
|
|
67
|
+
"""Create a sample knowledge service query for testing."""
|
|
68
|
+
return KnowledgeServiceQuery(
|
|
69
|
+
query_id="test-query-123",
|
|
70
|
+
name="Extract Meeting Summary",
|
|
71
|
+
knowledge_service_id="anthropic-claude",
|
|
72
|
+
prompt="Extract the main summary from this meeting transcript",
|
|
73
|
+
query_metadata={"model": "claude-3", "temperature": 0.2},
|
|
74
|
+
assistant_prompt="Please format as JSON",
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class TestGetKnowledgeServiceQueries:
|
|
79
|
+
"""Test the GET / endpoint for knowledge service queries."""
|
|
80
|
+
|
|
81
|
+
def test_get_knowledge_service_queries_empty_list(
|
|
82
|
+
self,
|
|
83
|
+
client: TestClient,
|
|
84
|
+
memory_repo: MemoryKnowledgeServiceQueryRepository,
|
|
85
|
+
) -> None:
|
|
86
|
+
"""Test getting queries when repository is empty."""
|
|
87
|
+
response = client.get("/knowledge_service_queries/")
|
|
88
|
+
|
|
89
|
+
assert response.status_code == 200
|
|
90
|
+
data = response.json()
|
|
91
|
+
|
|
92
|
+
# Verify pagination structure
|
|
93
|
+
assert "items" in data
|
|
94
|
+
assert "total" in data
|
|
95
|
+
assert "page" in data
|
|
96
|
+
assert "size" in data
|
|
97
|
+
assert "pages" in data
|
|
98
|
+
|
|
99
|
+
# Should return empty list when repository is empty
|
|
100
|
+
assert data["items"] == []
|
|
101
|
+
assert data["total"] == 0
|
|
102
|
+
|
|
103
|
+
def test_get_knowledge_service_queries_with_pagination_params(
|
|
104
|
+
self,
|
|
105
|
+
client: TestClient,
|
|
106
|
+
memory_repo: MemoryKnowledgeServiceQueryRepository,
|
|
107
|
+
) -> None:
|
|
108
|
+
"""Test getting queries with pagination parameters."""
|
|
109
|
+
response = client.get("/knowledge_service_queries/?page=2&size=10")
|
|
110
|
+
|
|
111
|
+
assert response.status_code == 200
|
|
112
|
+
data = response.json()
|
|
113
|
+
|
|
114
|
+
# Verify pagination parameters are handled
|
|
115
|
+
assert "items" in data
|
|
116
|
+
assert "page" in data
|
|
117
|
+
assert "size" in data
|
|
118
|
+
|
|
119
|
+
# Even with pagination params, should work with empty repository
|
|
120
|
+
assert data["items"] == []
|
|
121
|
+
|
|
122
|
+
async def test_get_knowledge_service_queries_with_data(
|
|
123
|
+
self,
|
|
124
|
+
client: TestClient,
|
|
125
|
+
memory_repo: MemoryKnowledgeServiceQueryRepository,
|
|
126
|
+
sample_knowledge_service_query: KnowledgeServiceQuery,
|
|
127
|
+
) -> None:
|
|
128
|
+
"""Test getting queries when repository contains data."""
|
|
129
|
+
# Create a second query for testing
|
|
130
|
+
query2 = KnowledgeServiceQuery(
|
|
131
|
+
query_id="test-query-456",
|
|
132
|
+
name="Extract Attendees",
|
|
133
|
+
knowledge_service_id="openai-service",
|
|
134
|
+
prompt="Extract all attendees from this meeting",
|
|
135
|
+
query_metadata={"model": "gpt-4", "temperature": 0.1},
|
|
136
|
+
assistant_prompt="Format as JSON array",
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
# Save queries to the repository
|
|
140
|
+
await memory_repo.save(sample_knowledge_service_query)
|
|
141
|
+
await memory_repo.save(query2)
|
|
142
|
+
|
|
143
|
+
response = client.get("/knowledge_service_queries/")
|
|
144
|
+
|
|
145
|
+
assert response.status_code == 200
|
|
146
|
+
data = response.json()
|
|
147
|
+
|
|
148
|
+
# Verify pagination structure
|
|
149
|
+
assert "items" in data
|
|
150
|
+
assert "total" in data
|
|
151
|
+
assert "page" in data
|
|
152
|
+
assert "size" in data
|
|
153
|
+
|
|
154
|
+
# Should return both queries
|
|
155
|
+
assert data["total"] == 2
|
|
156
|
+
assert len(data["items"]) == 2
|
|
157
|
+
|
|
158
|
+
# Verify the queries are returned (order may vary)
|
|
159
|
+
returned_ids = {item["query_id"] for item in data["items"]}
|
|
160
|
+
expected_ids = {
|
|
161
|
+
sample_knowledge_service_query.query_id,
|
|
162
|
+
query2.query_id,
|
|
163
|
+
}
|
|
164
|
+
assert returned_ids == expected_ids
|
|
165
|
+
|
|
166
|
+
# Verify query data structure
|
|
167
|
+
for item in data["items"]:
|
|
168
|
+
assert "query_id" in item
|
|
169
|
+
assert "name" in item
|
|
170
|
+
assert "knowledge_service_id" in item
|
|
171
|
+
assert "prompt" in item
|
|
172
|
+
|
|
173
|
+
async def test_get_knowledge_service_queries_pagination(
|
|
174
|
+
self,
|
|
175
|
+
client: TestClient,
|
|
176
|
+
memory_repo: MemoryKnowledgeServiceQueryRepository,
|
|
177
|
+
) -> None:
|
|
178
|
+
"""Test pagination with multiple queries."""
|
|
179
|
+
# Create several queries
|
|
180
|
+
queries = []
|
|
181
|
+
for i in range(5):
|
|
182
|
+
query = KnowledgeServiceQuery(
|
|
183
|
+
query_id=f"query-{i:03d}",
|
|
184
|
+
name=f"Query {i}",
|
|
185
|
+
knowledge_service_id="test-service",
|
|
186
|
+
prompt=f"Test prompt {i}",
|
|
187
|
+
)
|
|
188
|
+
queries.append(query)
|
|
189
|
+
await memory_repo.save(query)
|
|
190
|
+
|
|
191
|
+
# Test first page with size 2
|
|
192
|
+
response = client.get("/knowledge_service_queries/?page=1&size=2")
|
|
193
|
+
assert response.status_code == 200
|
|
194
|
+
data = response.json()
|
|
195
|
+
|
|
196
|
+
assert data["total"] == 5
|
|
197
|
+
assert data["page"] == 1
|
|
198
|
+
assert data["size"] == 2
|
|
199
|
+
assert len(data["items"]) == 2
|
|
200
|
+
|
|
201
|
+
# Test second page
|
|
202
|
+
response = client.get("/knowledge_service_queries/?page=2&size=2")
|
|
203
|
+
assert response.status_code == 200
|
|
204
|
+
data = response.json()
|
|
205
|
+
|
|
206
|
+
assert data["total"] == 5
|
|
207
|
+
assert data["page"] == 2
|
|
208
|
+
assert data["size"] == 2
|
|
209
|
+
assert len(data["items"]) == 2
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
class TestCreateKnowledgeServiceQuery:
|
|
213
|
+
"""Test the POST / endpoint for creating knowledge service queries."""
|
|
214
|
+
|
|
215
|
+
def test_create_knowledge_service_query_success(
|
|
216
|
+
self,
|
|
217
|
+
client: TestClient,
|
|
218
|
+
memory_repo: MemoryKnowledgeServiceQueryRepository,
|
|
219
|
+
) -> None:
|
|
220
|
+
"""Test successful creation of a knowledge service query."""
|
|
221
|
+
request_data = {
|
|
222
|
+
"name": "Extract Meeting Summary",
|
|
223
|
+
"knowledge_service_id": "anthropic-claude",
|
|
224
|
+
"prompt": "Extract the main summary from this meeting transcript",
|
|
225
|
+
"query_metadata": {"model": "claude-3", "temperature": 0.2},
|
|
226
|
+
"assistant_prompt": "Please format as JSON",
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
response = client.post("/knowledge_service_queries/", json=request_data)
|
|
230
|
+
|
|
231
|
+
assert response.status_code == 200
|
|
232
|
+
data = response.json()
|
|
233
|
+
|
|
234
|
+
# Verify response structure
|
|
235
|
+
assert "query_id" in data
|
|
236
|
+
assert data["name"] == request_data["name"]
|
|
237
|
+
assert data["knowledge_service_id"] == request_data["knowledge_service_id"]
|
|
238
|
+
assert data["prompt"] == request_data["prompt"]
|
|
239
|
+
assert data["query_metadata"] == request_data["query_metadata"]
|
|
240
|
+
assert data["assistant_prompt"] == request_data["assistant_prompt"]
|
|
241
|
+
assert "created_at" in data
|
|
242
|
+
assert "updated_at" in data
|
|
243
|
+
|
|
244
|
+
# Verify the query was saved to repository
|
|
245
|
+
query_id = data["query_id"]
|
|
246
|
+
assert query_id is not None
|
|
247
|
+
assert query_id != ""
|
|
248
|
+
|
|
249
|
+
async def test_create_knowledge_service_query_persisted(
|
|
250
|
+
self,
|
|
251
|
+
client: TestClient,
|
|
252
|
+
memory_repo: MemoryKnowledgeServiceQueryRepository,
|
|
253
|
+
) -> None:
|
|
254
|
+
"""Test that created query is persisted in repository."""
|
|
255
|
+
request_data = {
|
|
256
|
+
"name": "Extract Action Items",
|
|
257
|
+
"knowledge_service_id": "openai-gpt4",
|
|
258
|
+
"prompt": "List all action items from this meeting",
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
response = client.post("/knowledge_service_queries/", json=request_data)
|
|
262
|
+
assert response.status_code == 200
|
|
263
|
+
|
|
264
|
+
query_id = response.json()["query_id"]
|
|
265
|
+
|
|
266
|
+
# Verify query was saved by retrieving it
|
|
267
|
+
saved_query = await memory_repo.get(query_id)
|
|
268
|
+
assert saved_query is not None
|
|
269
|
+
assert saved_query.name == request_data["name"]
|
|
270
|
+
assert saved_query.knowledge_service_id == request_data["knowledge_service_id"]
|
|
271
|
+
assert saved_query.prompt == request_data["prompt"]
|
|
272
|
+
|
|
273
|
+
def test_create_knowledge_service_query_minimal_fields(
|
|
274
|
+
self,
|
|
275
|
+
client: TestClient,
|
|
276
|
+
memory_repo: MemoryKnowledgeServiceQueryRepository,
|
|
277
|
+
) -> None:
|
|
278
|
+
"""Test creation with only required fields."""
|
|
279
|
+
request_data = {
|
|
280
|
+
"name": "Minimal Query",
|
|
281
|
+
"knowledge_service_id": "test-service",
|
|
282
|
+
"prompt": "Test prompt",
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
response = client.post("/knowledge_service_queries/", json=request_data)
|
|
286
|
+
|
|
287
|
+
assert response.status_code == 200
|
|
288
|
+
data = response.json()
|
|
289
|
+
|
|
290
|
+
assert data["name"] == request_data["name"]
|
|
291
|
+
assert data["knowledge_service_id"] == request_data["knowledge_service_id"]
|
|
292
|
+
assert data["prompt"] == request_data["prompt"]
|
|
293
|
+
assert data["query_metadata"] == {}
|
|
294
|
+
assert data["assistant_prompt"] is None
|
|
295
|
+
|
|
296
|
+
def test_create_knowledge_service_query_validation_errors(
|
|
297
|
+
self,
|
|
298
|
+
client: TestClient,
|
|
299
|
+
memory_repo: MemoryKnowledgeServiceQueryRepository,
|
|
300
|
+
) -> None:
|
|
301
|
+
"""Test validation error handling."""
|
|
302
|
+
# Test empty name
|
|
303
|
+
request_data = {
|
|
304
|
+
"name": "",
|
|
305
|
+
"knowledge_service_id": "test-service",
|
|
306
|
+
"prompt": "Test prompt",
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
response = client.post("/knowledge_service_queries/", json=request_data)
|
|
310
|
+
assert response.status_code == 422
|
|
311
|
+
|
|
312
|
+
# Test empty knowledge_service_id
|
|
313
|
+
request_data = {
|
|
314
|
+
"name": "Test Query",
|
|
315
|
+
"knowledge_service_id": "",
|
|
316
|
+
"prompt": "Test prompt",
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
response = client.post("/knowledge_service_queries/", json=request_data)
|
|
320
|
+
assert response.status_code == 422
|
|
321
|
+
|
|
322
|
+
# Test empty prompt
|
|
323
|
+
request_data = {
|
|
324
|
+
"name": "Test Query",
|
|
325
|
+
"knowledge_service_id": "test-service",
|
|
326
|
+
"prompt": "",
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
response = client.post("/knowledge_service_queries/", json=request_data)
|
|
330
|
+
assert response.status_code == 422
|
|
331
|
+
|
|
332
|
+
def test_create_knowledge_service_query_missing_required_fields(
|
|
333
|
+
self,
|
|
334
|
+
client: TestClient,
|
|
335
|
+
memory_repo: MemoryKnowledgeServiceQueryRepository,
|
|
336
|
+
) -> None:
|
|
337
|
+
"""Test handling of missing required fields."""
|
|
338
|
+
# Missing name
|
|
339
|
+
request_data = {
|
|
340
|
+
"knowledge_service_id": "test-service",
|
|
341
|
+
"prompt": "Test prompt",
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
response = client.post("/knowledge_service_queries/", json=request_data)
|
|
345
|
+
assert response.status_code == 422
|
|
346
|
+
|
|
347
|
+
# Missing knowledge_service_id
|
|
348
|
+
request_data = {
|
|
349
|
+
"name": "Test Query",
|
|
350
|
+
"prompt": "Test prompt",
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
response = client.post("/knowledge_service_queries/", json=request_data)
|
|
354
|
+
assert response.status_code == 422
|
|
355
|
+
|
|
356
|
+
# Missing prompt
|
|
357
|
+
request_data = {
|
|
358
|
+
"name": "Test Query",
|
|
359
|
+
"knowledge_service_id": "test-service",
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
response = client.post("/knowledge_service_queries/", json=request_data)
|
|
363
|
+
assert response.status_code == 422
|
|
364
|
+
|
|
365
|
+
def test_post_and_get_integration(
|
|
366
|
+
self,
|
|
367
|
+
client: TestClient,
|
|
368
|
+
memory_repo: MemoryKnowledgeServiceQueryRepository,
|
|
369
|
+
) -> None:
|
|
370
|
+
"""Test that POST and GET endpoints work together."""
|
|
371
|
+
# Create a query via POST
|
|
372
|
+
request_data = {
|
|
373
|
+
"name": "Integration Test Query",
|
|
374
|
+
"knowledge_service_id": "test-integration-service",
|
|
375
|
+
"prompt": "This is an integration test prompt",
|
|
376
|
+
"query_metadata": {"test": True, "integration": "yes"},
|
|
377
|
+
"assistant_prompt": "Integration test response format",
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
post_response = client.post("/knowledge_service_queries/", json=request_data)
|
|
381
|
+
assert post_response.status_code == 200
|
|
382
|
+
created_query = post_response.json()
|
|
383
|
+
|
|
384
|
+
# Verify the query appears in GET response
|
|
385
|
+
get_response = client.get("/knowledge_service_queries/")
|
|
386
|
+
assert get_response.status_code == 200
|
|
387
|
+
get_data = get_response.json()
|
|
388
|
+
|
|
389
|
+
# Should find our created query in the list
|
|
390
|
+
assert get_data["total"] == 1
|
|
391
|
+
assert len(get_data["items"]) == 1
|
|
392
|
+
|
|
393
|
+
returned_query = get_data["items"][0]
|
|
394
|
+
assert returned_query["query_id"] == created_query["query_id"]
|
|
395
|
+
assert returned_query["name"] == request_data["name"]
|
|
396
|
+
assert (
|
|
397
|
+
returned_query["knowledge_service_id"]
|
|
398
|
+
== request_data["knowledge_service_id"]
|
|
399
|
+
)
|
|
400
|
+
assert returned_query["prompt"] == request_data["prompt"]
|
|
401
|
+
assert returned_query["query_metadata"] == request_data["query_metadata"]
|
|
402
|
+
assert returned_query["assistant_prompt"] == request_data["assistant_prompt"]
|
|
403
|
+
|
|
404
|
+
# Create another query to test multiple items
|
|
405
|
+
request_data2 = {
|
|
406
|
+
"name": "Second Integration Query",
|
|
407
|
+
"knowledge_service_id": "another-service",
|
|
408
|
+
"prompt": "Another test prompt",
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
post_response2 = client.post("/knowledge_service_queries/", json=request_data2)
|
|
412
|
+
assert post_response2.status_code == 200
|
|
413
|
+
|
|
414
|
+
# Verify both queries appear in GET response
|
|
415
|
+
get_response2 = client.get("/knowledge_service_queries/")
|
|
416
|
+
assert get_response2.status_code == 200
|
|
417
|
+
get_data2 = get_response2.json()
|
|
418
|
+
|
|
419
|
+
assert get_data2["total"] == 2
|
|
420
|
+
assert len(get_data2["items"]) == 2
|
|
421
|
+
|
|
422
|
+
# Verify both query IDs are present
|
|
423
|
+
returned_ids = {item["query_id"] for item in get_data2["items"]}
|
|
424
|
+
expected_ids = {
|
|
425
|
+
created_query["query_id"],
|
|
426
|
+
post_response2.json()["query_id"],
|
|
427
|
+
}
|
|
428
|
+
assert returned_ids == expected_ids
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
class TestBulkGetKnowledgeServiceQueries:
|
|
432
|
+
"""Test the bulk GET functionality with IDs parameter."""
|
|
433
|
+
|
|
434
|
+
async def test_bulk_get_queries_success(
|
|
435
|
+
self,
|
|
436
|
+
client: TestClient,
|
|
437
|
+
memory_repo: MemoryKnowledgeServiceQueryRepository,
|
|
438
|
+
) -> None:
|
|
439
|
+
"""Test successful bulk retrieval of queries by IDs."""
|
|
440
|
+
# Create test queries
|
|
441
|
+
queries = []
|
|
442
|
+
for i in range(3):
|
|
443
|
+
query = KnowledgeServiceQuery(
|
|
444
|
+
query_id=f"bulk-query-{i}",
|
|
445
|
+
name=f"Bulk Query {i}",
|
|
446
|
+
knowledge_service_id="test-service",
|
|
447
|
+
prompt=f"Test prompt {i}",
|
|
448
|
+
)
|
|
449
|
+
queries.append(query)
|
|
450
|
+
await memory_repo.save(query)
|
|
451
|
+
|
|
452
|
+
# Test bulk get with all IDs
|
|
453
|
+
ids_param = ",".join([q.query_id for q in queries])
|
|
454
|
+
response = client.get(f"/knowledge_service_queries/?ids={ids_param}")
|
|
455
|
+
|
|
456
|
+
assert response.status_code == 200
|
|
457
|
+
data = response.json()
|
|
458
|
+
|
|
459
|
+
# Verify pagination structure
|
|
460
|
+
assert "items" in data
|
|
461
|
+
assert "total" in data
|
|
462
|
+
assert data["total"] == 3
|
|
463
|
+
assert len(data["items"]) == 3
|
|
464
|
+
|
|
465
|
+
# Verify all queries are returned
|
|
466
|
+
returned_ids = {item["query_id"] for item in data["items"]}
|
|
467
|
+
expected_ids = {query.query_id for query in queries}
|
|
468
|
+
assert returned_ids == expected_ids
|
|
469
|
+
|
|
470
|
+
async def test_bulk_get_queries_partial_found(
|
|
471
|
+
self,
|
|
472
|
+
client: TestClient,
|
|
473
|
+
memory_repo: MemoryKnowledgeServiceQueryRepository,
|
|
474
|
+
) -> None:
|
|
475
|
+
"""Test bulk retrieval when only some IDs are found."""
|
|
476
|
+
# Create one query
|
|
477
|
+
query = KnowledgeServiceQuery(
|
|
478
|
+
query_id="existing-query",
|
|
479
|
+
name="Existing Query",
|
|
480
|
+
knowledge_service_id="test-service",
|
|
481
|
+
prompt="Test prompt",
|
|
482
|
+
)
|
|
483
|
+
await memory_repo.save(query)
|
|
484
|
+
|
|
485
|
+
# Request both existing and non-existing IDs
|
|
486
|
+
ids_param = "existing-query,non-existing-1,non-existing-2"
|
|
487
|
+
response = client.get(f"/knowledge_service_queries/?ids={ids_param}")
|
|
488
|
+
|
|
489
|
+
assert response.status_code == 200
|
|
490
|
+
data = response.json()
|
|
491
|
+
|
|
492
|
+
# Should return only the found query
|
|
493
|
+
assert data["total"] == 1
|
|
494
|
+
assert len(data["items"]) == 1
|
|
495
|
+
assert data["items"][0]["query_id"] == "existing-query"
|
|
496
|
+
|
|
497
|
+
def test_bulk_get_queries_empty_ids(
|
|
498
|
+
self,
|
|
499
|
+
client: TestClient,
|
|
500
|
+
memory_repo: MemoryKnowledgeServiceQueryRepository,
|
|
501
|
+
) -> None:
|
|
502
|
+
"""Test bulk retrieval with empty IDs parameter."""
|
|
503
|
+
response = client.get("/knowledge_service_queries/?ids=")
|
|
504
|
+
|
|
505
|
+
assert response.status_code == 400
|
|
506
|
+
data = response.json()
|
|
507
|
+
assert "Invalid ids parameter" in data["detail"]
|
|
508
|
+
|
|
509
|
+
def test_bulk_get_queries_whitespace_only_ids(
|
|
510
|
+
self,
|
|
511
|
+
client: TestClient,
|
|
512
|
+
memory_repo: MemoryKnowledgeServiceQueryRepository,
|
|
513
|
+
) -> None:
|
|
514
|
+
"""Test bulk retrieval with whitespace-only IDs."""
|
|
515
|
+
response = client.get("/knowledge_service_queries/?ids= , , ")
|
|
516
|
+
|
|
517
|
+
assert response.status_code == 400
|
|
518
|
+
data = response.json()
|
|
519
|
+
assert "Invalid ids parameter" in data["detail"]
|
|
520
|
+
|
|
521
|
+
def test_bulk_get_queries_too_many_ids(
|
|
522
|
+
self,
|
|
523
|
+
client: TestClient,
|
|
524
|
+
memory_repo: MemoryKnowledgeServiceQueryRepository,
|
|
525
|
+
) -> None:
|
|
526
|
+
"""Test bulk retrieval with too many IDs."""
|
|
527
|
+
# Create 101 IDs (exceeds limit of 100)
|
|
528
|
+
ids = [f"query-{i}" for i in range(101)]
|
|
529
|
+
ids_param = ",".join(ids)
|
|
530
|
+
|
|
531
|
+
response = client.get(f"/knowledge_service_queries/?ids={ids_param}")
|
|
532
|
+
|
|
533
|
+
assert response.status_code == 400
|
|
534
|
+
data = response.json()
|
|
535
|
+
assert "Too many IDs requested" in data["detail"]
|
|
536
|
+
assert "maximum 100" in data["detail"]
|
|
537
|
+
|
|
538
|
+
async def test_bulk_get_queries_with_spaces_and_commas(
|
|
539
|
+
self,
|
|
540
|
+
client: TestClient,
|
|
541
|
+
memory_repo: MemoryKnowledgeServiceQueryRepository,
|
|
542
|
+
) -> None:
|
|
543
|
+
"""Test bulk retrieval with various comma and space combinations."""
|
|
544
|
+
# Create test queries
|
|
545
|
+
queries = []
|
|
546
|
+
for i in range(2):
|
|
547
|
+
query = KnowledgeServiceQuery(
|
|
548
|
+
query_id=f"space-query-{i}",
|
|
549
|
+
name=f"Space Query {i}",
|
|
550
|
+
knowledge_service_id="test-service",
|
|
551
|
+
prompt=f"Test prompt {i}",
|
|
552
|
+
)
|
|
553
|
+
queries.append(query)
|
|
554
|
+
await memory_repo.save(query)
|
|
555
|
+
|
|
556
|
+
# Test with various spacing and comma patterns
|
|
557
|
+
test_cases = [
|
|
558
|
+
"space-query-0,space-query-1",
|
|
559
|
+
"space-query-0, space-query-1",
|
|
560
|
+
" space-query-0 , space-query-1 ",
|
|
561
|
+
"space-query-0, space-query-1 ,",
|
|
562
|
+
]
|
|
563
|
+
|
|
564
|
+
for ids_param in test_cases:
|
|
565
|
+
response = client.get(f"/knowledge_service_queries/?ids={ids_param}")
|
|
566
|
+
assert response.status_code == 200
|
|
567
|
+
data = response.json()
|
|
568
|
+
assert data["total"] == 2
|
|
569
|
+
returned_ids = {item["query_id"] for item in data["items"]}
|
|
570
|
+
expected_ids = {q.query_id for q in queries}
|
|
571
|
+
assert returned_ids == expected_ids
|
|
572
|
+
|
|
573
|
+
async def test_bulk_get_queries_single_id(
|
|
574
|
+
self,
|
|
575
|
+
client: TestClient,
|
|
576
|
+
memory_repo: MemoryKnowledgeServiceQueryRepository,
|
|
577
|
+
) -> None:
|
|
578
|
+
"""Test bulk retrieval with a single ID."""
|
|
579
|
+
query = KnowledgeServiceQuery(
|
|
580
|
+
query_id="single-query",
|
|
581
|
+
name="Single Query",
|
|
582
|
+
knowledge_service_id="test-service",
|
|
583
|
+
prompt="Single test prompt",
|
|
584
|
+
)
|
|
585
|
+
await memory_repo.save(query)
|
|
586
|
+
|
|
587
|
+
response = client.get("/knowledge_service_queries/?ids=single-query")
|
|
588
|
+
|
|
589
|
+
assert response.status_code == 200
|
|
590
|
+
data = response.json()
|
|
591
|
+
assert data["total"] == 1
|
|
592
|
+
assert data["items"][0]["query_id"] == "single-query"
|
|
593
|
+
assert data["items"][0]["name"] == "Single Query"
|
|
594
|
+
|
|
595
|
+
def test_bulk_get_queries_no_ids_falls_back_to_list_all(
|
|
596
|
+
self,
|
|
597
|
+
client: TestClient,
|
|
598
|
+
memory_repo: MemoryKnowledgeServiceQueryRepository,
|
|
599
|
+
) -> None:
|
|
600
|
+
"""Test that without IDs parameter, it falls back to list all."""
|
|
601
|
+
response = client.get("/knowledge_service_queries/")
|
|
602
|
+
|
|
603
|
+
assert response.status_code == 200
|
|
604
|
+
data = response.json()
|
|
605
|
+
|
|
606
|
+
# Should have pagination structure from list all
|
|
607
|
+
assert "items" in data
|
|
608
|
+
assert "total" in data
|
|
609
|
+
assert "page" in data
|
|
610
|
+
assert "size" in data
|
|
611
|
+
assert "pages" in data
|
|
612
|
+
|
|
613
|
+
async def test_bulk_get_queries_integration_with_assembly_spec_use_case(
|
|
614
|
+
self,
|
|
615
|
+
client: TestClient,
|
|
616
|
+
memory_repo: MemoryKnowledgeServiceQueryRepository,
|
|
617
|
+
) -> None:
|
|
618
|
+
"""Test the typical use case: getting queries referenced by spec."""
|
|
619
|
+
# Create queries that would be referenced by an assembly spec
|
|
620
|
+
query_mappings = {
|
|
621
|
+
"/properties/attendees": "attendee-extractor",
|
|
622
|
+
"/properties/summary": "summary-extractor",
|
|
623
|
+
"/properties/action_items": "action-extractor",
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
queries = []
|
|
627
|
+
for json_pointer, query_id in query_mappings.items():
|
|
628
|
+
query = KnowledgeServiceQuery(
|
|
629
|
+
query_id=query_id,
|
|
630
|
+
name=f"Query for {json_pointer}",
|
|
631
|
+
knowledge_service_id="test-service",
|
|
632
|
+
prompt=f"Extract data for {json_pointer}",
|
|
633
|
+
)
|
|
634
|
+
queries.append(query)
|
|
635
|
+
await memory_repo.save(query)
|
|
636
|
+
|
|
637
|
+
# Simulate getting all queries referenced by an assembly spec
|
|
638
|
+
query_ids = list(query_mappings.values())
|
|
639
|
+
ids_param = ",".join(query_ids)
|
|
640
|
+
|
|
641
|
+
response = client.get(f"/knowledge_service_queries/?ids={ids_param}")
|
|
642
|
+
|
|
643
|
+
assert response.status_code == 200
|
|
644
|
+
data = response.json()
|
|
645
|
+
|
|
646
|
+
# Should get all referenced queries
|
|
647
|
+
assert data["total"] == 3
|
|
648
|
+
returned_ids = {item["query_id"] for item in data["items"]}
|
|
649
|
+
assert returned_ids == set(query_ids)
|
|
650
|
+
|
|
651
|
+
# Verify query details are complete
|
|
652
|
+
for item in data["items"]:
|
|
653
|
+
assert "query_id" in item
|
|
654
|
+
assert "name" in item
|
|
655
|
+
assert "knowledge_service_id" in item
|
|
656
|
+
assert "prompt" in item
|
|
657
|
+
assert "query_metadata" in item
|
|
658
|
+
|
|
659
|
+
|
|
660
|
+
class TestGetIndividualKnowledgeServiceQuery:
|
|
661
|
+
"""Tests for the GET /knowledge_service_queries/{query_id} endpoint."""
|
|
662
|
+
|
|
663
|
+
async def test_get_query_success(
|
|
664
|
+
self,
|
|
665
|
+
client: TestClient,
|
|
666
|
+
memory_repo: MemoryKnowledgeServiceQueryRepository,
|
|
667
|
+
) -> None:
|
|
668
|
+
"""Test successfully retrieving an individual query."""
|
|
669
|
+
# Create a test query
|
|
670
|
+
query = KnowledgeServiceQuery(
|
|
671
|
+
query_id="test-query-123",
|
|
672
|
+
name="Test Query",
|
|
673
|
+
knowledge_service_id="test-service",
|
|
674
|
+
prompt="Extract test data",
|
|
675
|
+
assistant_prompt="Assistant instructions",
|
|
676
|
+
query_metadata={"max_tokens": 100, "temperature": 0.7},
|
|
677
|
+
)
|
|
678
|
+
await memory_repo.save(query)
|
|
679
|
+
|
|
680
|
+
# Get the query
|
|
681
|
+
response = client.get("/knowledge_service_queries/test-query-123")
|
|
682
|
+
|
|
683
|
+
assert response.status_code == 200
|
|
684
|
+
data = response.json()
|
|
685
|
+
|
|
686
|
+
assert data["query_id"] == "test-query-123"
|
|
687
|
+
assert data["name"] == "Test Query"
|
|
688
|
+
assert data["knowledge_service_id"] == "test-service"
|
|
689
|
+
assert data["prompt"] == "Extract test data"
|
|
690
|
+
assert data["assistant_prompt"] == "Assistant instructions"
|
|
691
|
+
assert data["query_metadata"] == {
|
|
692
|
+
"max_tokens": 100,
|
|
693
|
+
"temperature": 0.7,
|
|
694
|
+
}
|
|
695
|
+
assert "created_at" in data
|
|
696
|
+
assert "updated_at" in data
|
|
697
|
+
|
|
698
|
+
def test_get_query_not_found(self, client: TestClient) -> None:
|
|
699
|
+
"""Test retrieving a non-existent query returns 404."""
|
|
700
|
+
response = client.get("/knowledge_service_queries/nonexistent-query")
|
|
701
|
+
|
|
702
|
+
assert response.status_code == 404
|
|
703
|
+
data = response.json()
|
|
704
|
+
assert "not found" in data["detail"].lower()
|
|
705
|
+
assert "nonexistent-query" in data["detail"]
|
|
706
|
+
|
|
707
|
+
def test_get_query_empty_id(self, client: TestClient) -> None:
|
|
708
|
+
"""Test that empty query ID in URL is handled properly."""
|
|
709
|
+
# FastAPI will treat this as a different route, test edge case
|
|
710
|
+
response = client.get("/knowledge_service_queries/")
|
|
711
|
+
# This should hit the list endpoint instead
|
|
712
|
+
assert response.status_code == 200
|
|
713
|
+
|
|
714
|
+
async def test_get_query_without_optional_fields(
|
|
715
|
+
self,
|
|
716
|
+
client: TestClient,
|
|
717
|
+
memory_repo: MemoryKnowledgeServiceQueryRepository,
|
|
718
|
+
) -> None:
|
|
719
|
+
"""Test retrieving a query that doesn't have optional fields."""
|
|
720
|
+
# Create a minimal query without assistant_prompt
|
|
721
|
+
query = KnowledgeServiceQuery(
|
|
722
|
+
query_id="minimal-query",
|
|
723
|
+
name="Minimal Query",
|
|
724
|
+
knowledge_service_id="test-service",
|
|
725
|
+
prompt="Basic prompt",
|
|
726
|
+
query_metadata={},
|
|
727
|
+
)
|
|
728
|
+
await memory_repo.save(query)
|
|
729
|
+
|
|
730
|
+
response = client.get("/knowledge_service_queries/minimal-query")
|
|
731
|
+
|
|
732
|
+
assert response.status_code == 200
|
|
733
|
+
data = response.json()
|
|
734
|
+
|
|
735
|
+
assert data["query_id"] == "minimal-query"
|
|
736
|
+
assert data["name"] == "Minimal Query"
|
|
737
|
+
assert data["assistant_prompt"] is None
|
|
738
|
+
assert data["query_metadata"] == {}
|