Skip to content

Commit 7ea614e

Browse files
committed
base
1 parent 0f93902 commit 7ea614e

File tree

7 files changed

+209
-132
lines changed

7 files changed

+209
-132
lines changed

api/Assistant/assistant_model.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ class Context(Base):
2828

2929
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid4)
3030
content=Column(Text,nullable=True)
31-
file_url=Column(Text,nullable=True)
3231
pecha_title = Column(String(255), nullable=True)
3332
pecha_text_id = Column(String(255), nullable=True)
3433
assistant_id = Column(UUID(as_uuid=True),ForeignKey("assistant.id", ondelete="CASCADE"),nullable=False)

api/Assistant/assistant_response_model.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,12 @@
44

55
class ContextRequest(BaseModel):
66
content: Optional[str] = None
7-
file_url: Optional[str] = None
87
pecha_title: Optional[str] = None
98
pecha_text_id: Optional[str] = None
109

1110
class ContextResponse(BaseModel):
1211
id: UUID
1312
content: Optional[str] = None
14-
file_url: Optional[str] = None
1513
pecha_title: Optional[str] = None
1614
pecha_text_id: Optional[str] = None
1715

api/Assistant/assistant_service.py

Lines changed: 33 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import logging
21
from api.Users.user_service import validate_and_extract_user_email
32
from api.db.pg_database import SessionLocal
43
from api.Assistant.assistant_repository import get_all_assistants, get_assistant_by_id_repository, delete_assistant_repository, update_assistant_repository
@@ -8,29 +7,22 @@
87
from api.Assistant.assistant_model import Assistant, Context
98
from uuid import UUID
109
from datetime import datetime, timezone
11-
from fastapi import HTTPException, status
10+
from fastapi import HTTPException, status, UploadFile
1211
from api.error_constant import ErrorConstants
13-
from api.upload.S3_utils import generate_presigned_access_url, delete_file
14-
from api.config import get
1512
from api.cache.cache_enums import CacheType
1613
from api.Assistant.assistant_cache_service import (
1714
get_assistant_detail_cache,
1815
set_assistant_detail_cache,
1916
delete_assistant_detail_cache,
2017
)
18+
from api.langgraph.context_processor import validate_file, extract_content_from_file
2119

2220

2321
def _build_context_responses(contexts) -> List[ContextResponse]:
2422
return [
2523
ContextResponse(
2624
id=context.id,
2725
content=context.content,
28-
file_url=(
29-
generate_presigned_access_url(
30-
bucket_name=get("AWS_BUCKET_NAME"),
31-
s3_key=context.file_url
32-
) if context.file_url else None
33-
),
3426
pecha_title=context.pecha_title,
3527
pecha_text_id=context.pecha_text_id
3628
) for context in contexts
@@ -79,21 +71,37 @@ def get_assistants(skip: 0, limit: 20) -> AssistantResponse:
7971
return assistant_response
8072

8173

82-
def create_assistant_service(token: str, assistant_request: AssistantRequest):
83-
current_user_email=validate_and_extract_user_email(token=token)
74+
async def create_assistant_service(token: str, assistant_request: AssistantRequest, files: List[UploadFile] = None):
75+
current_user_email = validate_and_extract_user_email(token=token)
76+
77+
contexts_list = []
78+
79+
for ctx in assistant_request.contexts:
80+
contexts_list.append(
81+
Context(content=ctx.content, pecha_title=ctx.pecha_title, pecha_text_id=ctx.pecha_text_id)
82+
)
83+
84+
if files:
85+
for file in files:
86+
if file.filename:
87+
file_bytes = await file.read()
88+
try:
89+
validate_file(file.filename, len(file_bytes))
90+
extracted_content = extract_content_from_file(file_bytes, file.filename)
91+
contexts_list.append(Context(content=extracted_content))
92+
except ValueError as e:
93+
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
94+
8495
with SessionLocal() as db_session:
8596
assistant = Assistant(
86-
name=assistant_request.name,
87-
source_type=assistant_request.source_type,
88-
description=assistant_request.description,
89-
system_prompt=assistant_request.system_prompt,
90-
system_assistance=assistant_request.system_assistance,
91-
created_by=current_user_email,
92-
contexts=[
93-
Context(content=ctx.content, file_url=ctx.file_url, pecha_title=ctx.pecha_title, pecha_text_id=ctx.pecha_text_id)
94-
for ctx in assistant_request.contexts
95-
]
96-
)
97+
name=assistant_request.name,
98+
source_type=assistant_request.source_type,
99+
description=assistant_request.description,
100+
system_prompt=assistant_request.system_prompt,
101+
system_assistance=assistant_request.system_assistance,
102+
created_by=current_user_email,
103+
contexts=contexts_list
104+
)
97105
create_assistant_repository(db=db_session, assistant=assistant)
98106

99107
async def get_assistant_by_id_service(assistant_id: UUID) -> AssistantInfoResponse:
@@ -118,21 +126,14 @@ async def get_assistant_by_id_service(assistant_id: UUID) -> AssistantInfoRespon
118126
return assistant_info
119127

120128
async def delete_assistant_service(assistant_id: UUID, token: str):
121-
current_user_email=validate_and_extract_user_email(token=token)
129+
current_user_email = validate_and_extract_user_email(token=token)
122130
with SessionLocal() as db_session:
123131
assistant = get_assistant_by_id_repository(db=db_session, assistant_id=assistant_id)
124132
if current_user_email != assistant.created_by:
125133
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=ErrorConstants.UNAUTHORIZED_ERROR_MESSAGE)
126134
if assistant.system_assistance:
127135
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=ErrorConstants.FORBIDDEN_ERROR_MESSAGE)
128136

129-
for context in assistant.contexts:
130-
if context.file_url:
131-
try:
132-
delete_file(context.file_url)
133-
except Exception as e:
134-
logging.error(f"Failed to delete S3 file {context.file_url}: {str(e)}")
135-
136137
delete_assistant_repository(db=db_session, assistant_id=assistant_id)
137138

138139
await delete_assistant_detail_cache(
@@ -161,7 +162,7 @@ async def update_assistant_service(assistant_id: UUID, update_request: UpdateAss
161162
for context in assistant.contexts:
162163
db_session.delete(context)
163164
assistant.contexts = [
164-
Context(content=ctx.content, file_url=ctx.file_url, pecha_title=ctx.pecha_title, pecha_text_id=ctx.pecha_text_id)
165+
Context(content=ctx.content, pecha_title=ctx.pecha_title, pecha_text_id=ctx.pecha_text_id)
165166
for ctx in update_request.contexts
166167
]
167168

api/Assistant/assistant_view.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
from fastapi import APIRouter
1+
from fastapi import APIRouter, UploadFile, File, Form
22
from starlette import status
33
from api.Assistant.assistant_response_model import AssistantResponse, AssistantRequest, AssistantInfoResponse, UpdateAssistantRequest
44
from fastapi import Query, Depends
55
from api.Assistant.assistant_service import create_assistant_service, get_assistant_by_id_service, get_assistants, delete_assistant_service, update_assistant_service
66
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
7-
from typing import Annotated
7+
from typing import Annotated, Optional, List
88
from uuid import UUID
99
from api.constant import Constant
10+
import json
1011
oauth2_scheme = HTTPBearer()
1112

1213
assistant_router=APIRouter(
@@ -21,8 +22,30 @@ async def get_all_assistants(
2122
return get_assistants(skip=skip, limit=limit)
2223

2324
@assistant_router.post("", status_code=status.HTTP_201_CREATED)
24-
async def create_assistant(assistant_request: AssistantRequest, authentication_credential: Annotated[HTTPAuthorizationCredentials, Depends(oauth2_scheme)]):
25-
create_assistant_service(token=authentication_credential.credentials, assistant_request=assistant_request)
25+
async def create_assistant(
26+
authentication_credential: Annotated[HTTPAuthorizationCredentials, Depends(oauth2_scheme)],
27+
name: str = Form(...),
28+
system_prompt: str = Form(...),
29+
source_type: Optional[str] = Form(None),
30+
description: Optional[str] = Form(None),
31+
system_assistance: bool = Form(False),
32+
contexts: Optional[str] = Form(None),
33+
files: List[UploadFile] = File(default=[])
34+
):
35+
contexts_data = json.loads(contexts) if contexts else []
36+
assistant_request = AssistantRequest(
37+
name=name,
38+
source_type=source_type,
39+
description=description,
40+
system_prompt=system_prompt,
41+
contexts=contexts_data,
42+
system_assistance=system_assistance
43+
)
44+
await create_assistant_service(
45+
token=authentication_credential.credentials,
46+
assistant_request=assistant_request,
47+
files=files
48+
)
2649
return {"message": Constant.CREATED_ASSISTANT_MESSAGE}
2750

2851
@assistant_router.get("/{assistant_id}", status_code=status.HTTP_200_OK)

api/langgraph/context_processor.py

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
from pypdf import PdfReader
55
from docx import Document
66
from api.Assistant.assistant_response_model import ContextRequest
7-
from api.upload.S3_utils import download_file_from_s3
8-
from api.config import get
7+
8+
ALLOWED_FILE_EXTENSIONS = {'.pdf', '.txt', '.text', '.docx'}
9+
MAX_FILE_SIZE_MB = 10
10+
MAX_FILE_SIZE_BYTES = MAX_FILE_SIZE_MB * 1024 * 1024
911

1012

1113
def extract_text_from_pdf(pdf_bytes: BytesIO) -> str:
@@ -49,19 +51,28 @@ def extract_text_from_docx(file_bytes: BytesIO) -> str:
4951
raise
5052

5153

52-
def process_file_context(file_url: str) -> str:
53-
bucket_name = get("AWS_BUCKET_NAME")
54-
file_bytes = download_file_from_s3(bucket_name, file_url)
54+
def validate_file(filename: str, file_size: int) -> None:
55+
import os
56+
ext = os.path.splitext(filename.lower())[1]
57+
if ext not in ALLOWED_FILE_EXTENSIONS:
58+
raise ValueError(f"Unsupported file type: {ext}. Allowed types: {', '.join(ALLOWED_FILE_EXTENSIONS)}")
5559

56-
if file_url.lower().endswith('.pdf'):
57-
text = extract_text_from_pdf(file_bytes)
58-
elif file_url.lower().endswith(('.txt', '.text')):
59-
text = extract_text_from_txt(file_bytes)
60-
elif file_url.lower().endswith(('.docx')):
61-
text = extract_text_from_docx(file_bytes)
60+
if file_size > MAX_FILE_SIZE_BYTES:
61+
raise ValueError(f"File size exceeds {MAX_FILE_SIZE_MB}MB limit")
62+
63+
64+
def extract_content_from_file(file_bytes: bytes, filename: str) -> str:
65+
file_stream = BytesIO(file_bytes)
66+
filename_lower = filename.lower()
67+
68+
if filename_lower.endswith('.pdf'):
69+
return extract_text_from_pdf(file_stream)
70+
elif filename_lower.endswith(('.txt', '.text')):
71+
return extract_text_from_txt(file_stream)
72+
elif filename_lower.endswith('.docx'):
73+
return extract_text_from_docx(file_stream)
6274
else:
63-
raise ValueError(f"Unsupported file type: {file_url}")
64-
return text
75+
raise ValueError(f"Unsupported file type: {filename}")
6576

6677

6778
def process_contexts(contexts: List[ContextRequest]) -> Optional[List[str]]:
@@ -75,10 +86,6 @@ def process_contexts(contexts: List[ContextRequest]) -> Optional[List[str]]:
7586
if ctx.content:
7687
processed_contexts.append(ctx.content)
7788

78-
elif ctx.file_url:
79-
file_text = process_file_context(ctx.file_url)
80-
processed_contexts.append(file_text)
81-
8289
elif ctx.pecha_title and ctx.pecha_text_id:
8390
pecha_context = f"[Pecha: {ctx.pecha_title}, ID: {ctx.pecha_text_id}]"
8491
processed_contexts.append(pecha_context)

0 commit comments

Comments
 (0)