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,43 @@
|
|
|
1
|
+
"""
|
|
2
|
+
API routers for the julee CEAP system.
|
|
3
|
+
|
|
4
|
+
This package contains APIRouter modules that organize endpoints by domain.
|
|
5
|
+
Each router module defines routes at the root level and is mounted with a
|
|
6
|
+
prefix in the main app.
|
|
7
|
+
|
|
8
|
+
Organization:
|
|
9
|
+
- knowledge_service_queries: CRUD operations for knowledge service queries
|
|
10
|
+
- assembly_specifications: CRUD operations for assembly specifications
|
|
11
|
+
- documents: CRUD operations for documents
|
|
12
|
+
- workflows: Workflow management and execution endpoints
|
|
13
|
+
- system: Health checks and system status endpoints
|
|
14
|
+
|
|
15
|
+
Router modules follow the pattern:
|
|
16
|
+
1. Define routes at root level ("/" and "/{id}")
|
|
17
|
+
2. Include proper dependency injection
|
|
18
|
+
3. Use domain models for request/response
|
|
19
|
+
4. Follow consistent error handling patterns
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
# Import routers for convenient access
|
|
23
|
+
from julee.api.routers.knowledge_service_queries import (
|
|
24
|
+
router as knowledge_service_queries_router,
|
|
25
|
+
)
|
|
26
|
+
from julee.api.routers.knowledge_service_configs import (
|
|
27
|
+
router as knowledge_service_configs_router,
|
|
28
|
+
)
|
|
29
|
+
from julee.api.routers.assembly_specifications import (
|
|
30
|
+
router as assembly_specifications_router,
|
|
31
|
+
)
|
|
32
|
+
from julee.api.routers.system import router as system_router
|
|
33
|
+
from julee.api.routers.documents import router as documents_router
|
|
34
|
+
from julee.api.routers.workflows import router as workflows_router
|
|
35
|
+
|
|
36
|
+
__all__ = [
|
|
37
|
+
"knowledge_service_queries_router",
|
|
38
|
+
"knowledge_service_configs_router",
|
|
39
|
+
"assembly_specifications_router",
|
|
40
|
+
"documents_router",
|
|
41
|
+
"workflows_router",
|
|
42
|
+
"system_router",
|
|
43
|
+
]
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Assembly Specifications API router for the julee CEAP system.
|
|
3
|
+
|
|
4
|
+
This module provides the API endpoints for assembly specifications,
|
|
5
|
+
which define how to assemble documents of specific types including
|
|
6
|
+
JSON schemas and knowledge service query configurations.
|
|
7
|
+
|
|
8
|
+
Routes defined at root level:
|
|
9
|
+
- GET / - List assembly specifications (paginated)
|
|
10
|
+
- GET /{id} - Get a specific assembly specification by ID
|
|
11
|
+
|
|
12
|
+
These routes are mounted at /assembly_specifications in the main app.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import logging
|
|
16
|
+
from typing import cast
|
|
17
|
+
from fastapi import APIRouter, Depends, HTTPException, Path
|
|
18
|
+
from fastapi_pagination import Page, paginate
|
|
19
|
+
|
|
20
|
+
from julee.domain.models import AssemblySpecification
|
|
21
|
+
from julee.domain.repositories.assembly_specification import (
|
|
22
|
+
AssemblySpecificationRepository,
|
|
23
|
+
)
|
|
24
|
+
from julee.api.dependencies import (
|
|
25
|
+
get_assembly_specification_repository,
|
|
26
|
+
)
|
|
27
|
+
from julee.api.requests import CreateAssemblySpecificationRequest
|
|
28
|
+
|
|
29
|
+
logger = logging.getLogger(__name__)
|
|
30
|
+
|
|
31
|
+
# Create the router for assembly specifications
|
|
32
|
+
router = APIRouter()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@router.get("/", response_model=Page[AssemblySpecification])
|
|
36
|
+
async def get_assembly_specifications(
|
|
37
|
+
repository: AssemblySpecificationRepository = Depends( # type: ignore[misc]
|
|
38
|
+
get_assembly_specification_repository
|
|
39
|
+
),
|
|
40
|
+
) -> Page[AssemblySpecification]:
|
|
41
|
+
"""
|
|
42
|
+
Get a paginated list of assembly specifications.
|
|
43
|
+
|
|
44
|
+
This endpoint returns all assembly specifications in the system
|
|
45
|
+
with pagination support. Each specification contains the configuration
|
|
46
|
+
needed to define how to assemble documents of specific types.
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
Page[AssemblySpecification]: Paginated list of specifications
|
|
50
|
+
"""
|
|
51
|
+
logger.info("Assembly specifications requested")
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
# Get all assembly specifications from the repository
|
|
55
|
+
specifications = await repository.list_all()
|
|
56
|
+
|
|
57
|
+
logger.info(
|
|
58
|
+
"Assembly specifications retrieved successfully",
|
|
59
|
+
extra={"count": len(specifications)},
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Use fastapi-pagination to paginate the results
|
|
63
|
+
return cast(Page[AssemblySpecification], paginate(specifications))
|
|
64
|
+
|
|
65
|
+
except Exception as e:
|
|
66
|
+
logger.error(
|
|
67
|
+
"Failed to retrieve assembly specifications",
|
|
68
|
+
exc_info=True,
|
|
69
|
+
extra={"error_type": type(e).__name__, "error_message": str(e)},
|
|
70
|
+
)
|
|
71
|
+
raise HTTPException(
|
|
72
|
+
status_code=500,
|
|
73
|
+
detail="Failed to retrieve specifications due to an internal " "error.",
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@router.get("/{assembly_specification_id}", response_model=AssemblySpecification)
|
|
78
|
+
async def get_assembly_specification(
|
|
79
|
+
assembly_specification_id: str = Path(
|
|
80
|
+
description="The ID of the assembly specification to retrieve"
|
|
81
|
+
),
|
|
82
|
+
repository: AssemblySpecificationRepository = Depends( # type: ignore[misc]
|
|
83
|
+
get_assembly_specification_repository
|
|
84
|
+
),
|
|
85
|
+
) -> AssemblySpecification:
|
|
86
|
+
"""
|
|
87
|
+
Get a specific assembly specification by ID.
|
|
88
|
+
|
|
89
|
+
This endpoint retrieves a single assembly specification by its unique
|
|
90
|
+
identifier. The specification contains the JSON schema and knowledge
|
|
91
|
+
service query configurations needed for document assembly.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
assembly_specification_id: The unique ID of the specification
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
AssemblySpecification: The requested specification
|
|
98
|
+
|
|
99
|
+
Raises:
|
|
100
|
+
HTTPException: 404 if specification not found, 500 for other errors
|
|
101
|
+
"""
|
|
102
|
+
logger.info(
|
|
103
|
+
"Assembly specification requested",
|
|
104
|
+
extra={"assembly_specification_id": assembly_specification_id},
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
try:
|
|
108
|
+
# Get the specific assembly specification from the repository
|
|
109
|
+
specification = await repository.get(assembly_specification_id)
|
|
110
|
+
|
|
111
|
+
if specification is None:
|
|
112
|
+
logger.warning(
|
|
113
|
+
"Assembly specification not found",
|
|
114
|
+
extra={"assembly_specification_id": assembly_specification_id},
|
|
115
|
+
)
|
|
116
|
+
raise HTTPException(
|
|
117
|
+
status_code=404,
|
|
118
|
+
detail=f"Assembly specification with ID "
|
|
119
|
+
f"'{assembly_specification_id}' not found.",
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
logger.info(
|
|
123
|
+
"Assembly specification retrieved successfully",
|
|
124
|
+
extra={
|
|
125
|
+
"assembly_specification_id": assembly_specification_id,
|
|
126
|
+
"specification_name": specification.name,
|
|
127
|
+
},
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
return specification
|
|
131
|
+
|
|
132
|
+
except HTTPException:
|
|
133
|
+
# Re-raise HTTP exceptions (like 404)
|
|
134
|
+
raise
|
|
135
|
+
except Exception as e:
|
|
136
|
+
logger.error(
|
|
137
|
+
"Failed to retrieve assembly specification",
|
|
138
|
+
exc_info=True,
|
|
139
|
+
extra={
|
|
140
|
+
"error_type": type(e).__name__,
|
|
141
|
+
"error_message": str(e),
|
|
142
|
+
"assembly_specification_id": assembly_specification_id,
|
|
143
|
+
},
|
|
144
|
+
)
|
|
145
|
+
raise HTTPException(
|
|
146
|
+
status_code=500,
|
|
147
|
+
detail="Failed to retrieve specification due to an internal " "error.",
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@router.post("/", response_model=AssemblySpecification)
|
|
152
|
+
async def create_assembly_specification(
|
|
153
|
+
request: CreateAssemblySpecificationRequest,
|
|
154
|
+
repository: AssemblySpecificationRepository = Depends( # type: ignore[misc]
|
|
155
|
+
get_assembly_specification_repository
|
|
156
|
+
),
|
|
157
|
+
) -> AssemblySpecification:
|
|
158
|
+
"""
|
|
159
|
+
Create a new assembly specification.
|
|
160
|
+
|
|
161
|
+
This endpoint creates a new assembly specification that defines how to
|
|
162
|
+
assemble documents of specific types, including JSON schemas and
|
|
163
|
+
knowledge service query configurations.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
request: The assembly specification creation request
|
|
167
|
+
repository: Injected repository for persistence
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
AssemblySpecification: The created specification with generated ID and
|
|
171
|
+
timestamps
|
|
172
|
+
"""
|
|
173
|
+
logger.info(
|
|
174
|
+
"Assembly specification creation requested",
|
|
175
|
+
extra={"specification_name": request.name},
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
try:
|
|
179
|
+
# Generate unique ID for the new specification
|
|
180
|
+
specification_id = await repository.generate_id()
|
|
181
|
+
|
|
182
|
+
# Convert request to domain model with generated ID
|
|
183
|
+
specification = request.to_domain_model(specification_id)
|
|
184
|
+
|
|
185
|
+
# Save the specification via repository
|
|
186
|
+
await repository.save(specification)
|
|
187
|
+
|
|
188
|
+
logger.info(
|
|
189
|
+
"Assembly specification created successfully",
|
|
190
|
+
extra={
|
|
191
|
+
"assembly_specification_id": (specification.assembly_specification_id),
|
|
192
|
+
"specification_name": specification.name,
|
|
193
|
+
"version": specification.version,
|
|
194
|
+
},
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
return specification
|
|
198
|
+
|
|
199
|
+
except Exception as e:
|
|
200
|
+
logger.error(
|
|
201
|
+
"Failed to create assembly specification",
|
|
202
|
+
exc_info=True,
|
|
203
|
+
extra={
|
|
204
|
+
"error_type": type(e).__name__,
|
|
205
|
+
"error_message": str(e),
|
|
206
|
+
"specification_name": request.name,
|
|
207
|
+
},
|
|
208
|
+
)
|
|
209
|
+
raise HTTPException(
|
|
210
|
+
status_code=500,
|
|
211
|
+
detail="Failed to create specification due to an internal error.",
|
|
212
|
+
)
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Documents API router for the julee CEAP system.
|
|
3
|
+
|
|
4
|
+
This module provides document management API endpoints for retrieving
|
|
5
|
+
and managing documents in the system.
|
|
6
|
+
|
|
7
|
+
Routes defined at root level:
|
|
8
|
+
- GET / - List all documents with pagination
|
|
9
|
+
- GET /{document_id} - Get document metadata by ID
|
|
10
|
+
- GET /{document_id}/content - Get document content by ID
|
|
11
|
+
|
|
12
|
+
These routes are mounted with '/documents' prefix in the main app.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import logging
|
|
16
|
+
from typing import cast
|
|
17
|
+
|
|
18
|
+
from fastapi import APIRouter, Depends, HTTPException, Path
|
|
19
|
+
from fastapi.responses import Response
|
|
20
|
+
from fastapi_pagination import Page, paginate
|
|
21
|
+
|
|
22
|
+
from julee.domain.models.document import Document
|
|
23
|
+
from julee.domain.repositories.document import DocumentRepository
|
|
24
|
+
from julee.api.dependencies import get_document_repository
|
|
25
|
+
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
router = APIRouter()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@router.get("/", response_model=Page[Document])
|
|
32
|
+
async def list_documents(
|
|
33
|
+
repository: DocumentRepository = Depends(get_document_repository),
|
|
34
|
+
) -> Page[Document]:
|
|
35
|
+
"""
|
|
36
|
+
List all documents with pagination.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
repository: Document repository dependency
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
Paginated list of documents
|
|
43
|
+
|
|
44
|
+
Raises:
|
|
45
|
+
HTTPException: If repository operation fails
|
|
46
|
+
"""
|
|
47
|
+
try:
|
|
48
|
+
logger.info("Listing documents")
|
|
49
|
+
|
|
50
|
+
# Get all documents from repository
|
|
51
|
+
documents = await repository.list_all()
|
|
52
|
+
|
|
53
|
+
logger.info("Retrieved %d documents", len(documents))
|
|
54
|
+
|
|
55
|
+
# Return paginated result using fastapi-pagination
|
|
56
|
+
return cast(Page[Document], paginate(documents))
|
|
57
|
+
|
|
58
|
+
except Exception as e:
|
|
59
|
+
logger.error("Failed to list documents: %s", e)
|
|
60
|
+
raise HTTPException(
|
|
61
|
+
status_code=500, detail="Failed to retrieve documents"
|
|
62
|
+
) from e
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@router.get("/{document_id}", response_model=Document)
|
|
66
|
+
async def get_document(
|
|
67
|
+
document_id: str = Path(..., description="Document ID"),
|
|
68
|
+
repository: DocumentRepository = Depends(get_document_repository),
|
|
69
|
+
) -> Document:
|
|
70
|
+
"""
|
|
71
|
+
Get a single document by ID with metadata only.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
document_id: Unique document identifier
|
|
75
|
+
repository: Document repository dependency
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
Document with metadata only (no content)
|
|
79
|
+
|
|
80
|
+
Raises:
|
|
81
|
+
HTTPException: If document not found or repository operation fails
|
|
82
|
+
"""
|
|
83
|
+
try:
|
|
84
|
+
logger.info("Retrieving document metadata: %s", document_id)
|
|
85
|
+
|
|
86
|
+
# Get document from repository
|
|
87
|
+
document = await repository.get(document_id)
|
|
88
|
+
|
|
89
|
+
if not document:
|
|
90
|
+
raise HTTPException(
|
|
91
|
+
status_code=404,
|
|
92
|
+
detail=f"Document with ID '{document_id}' not found",
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
logger.info("Retrieved document metadata: %s", document_id)
|
|
96
|
+
return document
|
|
97
|
+
|
|
98
|
+
except HTTPException:
|
|
99
|
+
# Re-raise HTTP exceptions (like 404) without wrapping
|
|
100
|
+
raise
|
|
101
|
+
except Exception as e:
|
|
102
|
+
logger.error("Failed to get document %s: %s", document_id, e)
|
|
103
|
+
raise HTTPException(
|
|
104
|
+
status_code=500, detail="Failed to retrieve document"
|
|
105
|
+
) from e
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@router.get("/{document_id}/content")
|
|
109
|
+
async def get_document_content(
|
|
110
|
+
document_id: str = Path(..., description="Document ID"),
|
|
111
|
+
repository: DocumentRepository = Depends(get_document_repository),
|
|
112
|
+
) -> Response:
|
|
113
|
+
"""
|
|
114
|
+
Get the content of a document by ID.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
document_id: Unique document identifier
|
|
118
|
+
repository: Document repository dependency
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
Raw document content with appropriate Content-Type header
|
|
122
|
+
|
|
123
|
+
Raises:
|
|
124
|
+
HTTPException: If document not found or has no content
|
|
125
|
+
"""
|
|
126
|
+
try:
|
|
127
|
+
logger.info("Retrieving document content: %s", document_id)
|
|
128
|
+
|
|
129
|
+
# Get document from repository
|
|
130
|
+
document = await repository.get(document_id)
|
|
131
|
+
|
|
132
|
+
if not document:
|
|
133
|
+
raise HTTPException(
|
|
134
|
+
status_code=404,
|
|
135
|
+
detail=f"Document with ID '{document_id}' not found",
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
if not document.content:
|
|
139
|
+
raise HTTPException(
|
|
140
|
+
status_code=422,
|
|
141
|
+
detail=f"Document '{document_id}' has no content",
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
try:
|
|
145
|
+
# Read content
|
|
146
|
+
content_bytes = document.content.read()
|
|
147
|
+
|
|
148
|
+
logger.info(
|
|
149
|
+
"Retrieved document content: %s (%d bytes)",
|
|
150
|
+
document_id,
|
|
151
|
+
len(content_bytes),
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
# Return content with appropriate Content-Type
|
|
155
|
+
return Response(
|
|
156
|
+
content=content_bytes,
|
|
157
|
+
media_type=document.content_type,
|
|
158
|
+
headers={
|
|
159
|
+
"Content-Disposition": (
|
|
160
|
+
f'inline; filename="{document.original_filename}"'
|
|
161
|
+
)
|
|
162
|
+
},
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
except Exception as content_error:
|
|
166
|
+
logger.error(
|
|
167
|
+
"Failed to read content for document %s: %s",
|
|
168
|
+
document_id,
|
|
169
|
+
content_error,
|
|
170
|
+
)
|
|
171
|
+
raise HTTPException(
|
|
172
|
+
status_code=500, detail="Failed to read document content"
|
|
173
|
+
) from content_error
|
|
174
|
+
|
|
175
|
+
except HTTPException:
|
|
176
|
+
# Re-raise HTTP exceptions (like 404) without wrapping
|
|
177
|
+
raise
|
|
178
|
+
except Exception as e:
|
|
179
|
+
logger.error("Failed to get document content %s: %s", document_id, e)
|
|
180
|
+
raise HTTPException(
|
|
181
|
+
status_code=500, detail="Failed to retrieve document content"
|
|
182
|
+
) from e
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Knowledge Service Configs API router for the julee CEAP system.
|
|
3
|
+
|
|
4
|
+
This module provides the API endpoints for knowledge service configurations,
|
|
5
|
+
which define the available knowledge services that can be used for extracting
|
|
6
|
+
data during the assembly process.
|
|
7
|
+
|
|
8
|
+
Routes defined at root level:
|
|
9
|
+
- GET / - List all knowledge service configurations (paginated)
|
|
10
|
+
|
|
11
|
+
These routes are mounted at /knowledge_service_configs in the main app.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import logging
|
|
15
|
+
from typing import cast
|
|
16
|
+
from fastapi import APIRouter, Depends, HTTPException
|
|
17
|
+
from fastapi_pagination import Page, paginate
|
|
18
|
+
|
|
19
|
+
from julee.domain.models.knowledge_service_config import (
|
|
20
|
+
KnowledgeServiceConfig,
|
|
21
|
+
)
|
|
22
|
+
from julee.domain.repositories.knowledge_service_config import (
|
|
23
|
+
KnowledgeServiceConfigRepository,
|
|
24
|
+
)
|
|
25
|
+
from julee.api.dependencies import (
|
|
26
|
+
get_knowledge_service_config_repository,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
logger = logging.getLogger(__name__)
|
|
30
|
+
|
|
31
|
+
# Create the router for knowledge service configurations
|
|
32
|
+
router = APIRouter()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@router.get("/", response_model=Page[KnowledgeServiceConfig])
|
|
36
|
+
async def get_knowledge_service_configs(
|
|
37
|
+
repository: KnowledgeServiceConfigRepository = Depends( # type: ignore[misc]
|
|
38
|
+
get_knowledge_service_config_repository
|
|
39
|
+
),
|
|
40
|
+
) -> Page[KnowledgeServiceConfig]:
|
|
41
|
+
"""
|
|
42
|
+
Get all knowledge service configurations with pagination.
|
|
43
|
+
|
|
44
|
+
This endpoint returns all available knowledge service configurations
|
|
45
|
+
that can be used when creating knowledge service queries. Each
|
|
46
|
+
configuration contains the metadata needed to interact with a specific
|
|
47
|
+
external knowledge service.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
Page[KnowledgeServiceConfig]: Paginated list of all knowledge
|
|
51
|
+
service configurations
|
|
52
|
+
"""
|
|
53
|
+
logger.info("All knowledge service configurations requested")
|
|
54
|
+
|
|
55
|
+
try:
|
|
56
|
+
# Get all knowledge service configurations from the repository
|
|
57
|
+
configs = await repository.list_all()
|
|
58
|
+
|
|
59
|
+
logger.info(
|
|
60
|
+
"Knowledge service configurations retrieved successfully",
|
|
61
|
+
extra={"count": len(configs)},
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# Use fastapi-pagination to paginate the results
|
|
65
|
+
return cast(Page[KnowledgeServiceConfig], paginate(configs))
|
|
66
|
+
|
|
67
|
+
except Exception as e:
|
|
68
|
+
logger.error(
|
|
69
|
+
"Failed to retrieve knowledge service configurations",
|
|
70
|
+
exc_info=True,
|
|
71
|
+
extra={
|
|
72
|
+
"error_type": type(e).__name__,
|
|
73
|
+
"error_message": str(e),
|
|
74
|
+
},
|
|
75
|
+
)
|
|
76
|
+
raise HTTPException(
|
|
77
|
+
status_code=500,
|
|
78
|
+
detail="Failed to retrieve configurations due to an " "internal error.",
|
|
79
|
+
)
|