Skip to content

Commit e2098da

Browse files
authored
Merge pull request #37 from AET-DevOps25/feature/genai-improvement
MErge walids pr Feature/genai improvement
2 parents 915ecc8 + 634bb4e commit e2098da

File tree

6 files changed

+257
-168
lines changed

6 files changed

+257
-168
lines changed

genAi/compose.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ services:
33
build:
44
context: .
55
dockerfile: Dockerfile
6-
container_name: studymate-genai-dev
6+
container_name: genai-genai-dev
77
restart: unless-stopped
88
ports:
99
- "8081:8081" # FastAPI GenAI service
@@ -20,7 +20,7 @@ services:
2020

2121
weaviate:
2222
image: cr.weaviate.io/semitechnologies/weaviate:1.30.3
23-
container_name: studymate-weaviate-dev
23+
container_name: genai-weaviate-dev
2424
restart: unless-stopped
2525
command:
2626
- --host

genAi/llm.py

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,10 @@ class StudyLLM:
2828
base_url="https://gpu.aet.cit.tum.de/api/"
2929
)
3030

31-
def __init__(self, doc_path: str):
31+
def __init__(self):
3232
base_system_template = ("You are an expert on the information in the context given below.\n"
33-
"Use the context as your primary knowledge source. If you can't fulfill your task given the context, just say that.\n"
33+
"Use the context as your primary knowledge source.\n"
34+
"Do not mention that you have a context, just use it to fulfill your task.\n"
3435
"context: {context}\n"
3536
"Your task is {task}"
3637
)
@@ -39,12 +40,27 @@ def __init__(self, doc_path: str):
3940
('human', '{input}')
4041
])
4142
try:
42-
self.rag_helper = RAGHelper(doc_path)
43+
self.rag_helper = RAGHelper()
4344
except Exception as e:
4445
raise ValueError(f"Error initializing RAGHelper: {e}")
4546

4647

47-
async def prompt(self, prompt: str) -> str:
48+
async def load_document(self, doc_name: str, path: str, user_id: str):
49+
"""
50+
Load a document into the RAG system.
51+
52+
Args:
53+
doc_name (str): The name of the document to load.
54+
user_id (str): The ID of the user loading the document.
55+
"""
56+
try:
57+
await self.rag_helper.load_document(doc_name, path, user_id)
58+
return f"Document {doc_name} loaded successfully for user {user_id}."
59+
except Exception as e:
60+
error_msg = f"Failed to load document {doc_name} for user {user_id}: {str(e)}"
61+
raise ValueError(error_msg)
62+
63+
async def prompt(self, prompt: str, user_id: str) -> str:
4864
"""
4965
Call the LLM with a given prompt.
5066
@@ -59,7 +75,7 @@ async def prompt(self, prompt: str) -> str:
5975
"If you're asked a question that does not relate to your context, do not answer it - instead, answer by saying you're only familiar with <the topic in your context>.\n"
6076
)
6177

62-
context = self.rag_helper.retrieve(prompt, top_k=5)
78+
context = self.rag_helper.retrieve(query=prompt, user_id=user_id, top_k=5)
6379
chain = self.base_prompt_template | self.chat_llm
6480
response = await chain.ainvoke({
6581
'context': context,
@@ -69,7 +85,7 @@ async def prompt(self, prompt: str) -> str:
6985

7086
return response.content
7187

72-
async def summarize(self):
88+
async def summarize(self, document_name: str, user_id: str) -> str:
7389
"""
7490
Summarize the given document using the LLM.
7591
@@ -102,30 +118,36 @@ async def summarize(self):
102118
combine_prompt=combine_prompt
103119
)
104120

105-
result = await chain.ainvoke({"input_documents": self.rag_helper.summary_chunks})
121+
doc_name = f"{user_id}_{document_name}"
122+
chunks = self.rag_helper.get_generation_chunks(user_id, doc_name)
123+
result = await chain.ainvoke({"input_documents": chunks})
106124

107125
return result["output_text"]
108126

109-
async def generate_flashcards(self):
127+
async def generate_flashcards(self, document_name: str, user_id: str):
110128
"""
111129
Generate flashcards from the document using the LLM.
112130
113131
Returns:
114132
list: A list of flashcard objects.
115133
"""
116134
flashcard_chain = FlashcardChain(self.generation_llm)
117-
cards = await flashcard_chain.invoke(self.rag_helper.summary_chunks)
135+
doc_name = f"{user_id}_{document_name}"
136+
chunks = self.rag_helper.get_generation_chunks(user_id, doc_name)
137+
cards = await flashcard_chain.invoke(chunks)
118138
return cards
119139

120-
async def generate_quiz(self):
140+
async def generate_quiz(self, document_name: str, user_id: str):
121141
"""
122142
Generate a quiz from the document using the LLM.
123143
124144
Returns:
125145
list: A quiz object.
126146
"""
127147
quiz_chain = QuizChain(self.generation_llm)
128-
quiz = await quiz_chain.invoke(self.rag_helper.summary_chunks)
148+
doc_name = f"{user_id}_{document_name}"
149+
chunks = self.rag_helper.get_generation_chunks(user_id, doc_name)
150+
quiz = await quiz_chain.invoke(chunks)
129151
return quiz
130152

131153
def cleanup(self):

genAi/main.py

Lines changed: 28 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from fastapi import FastAPI
44
from fastapi.responses import JSONResponse
55
from helpers import save_document
6-
from request_models import CreateSessionRequest, PromptRequest, SummaryRequest, QuizRequest, FlashcardRequest
6+
from request_models import LoadDocumentRequest, PromptRequest, SummaryRequest, QuizRequest, FlashcardRequest
77
from llm import StudyLLM
88
from prometheus_fastapi_instrumentator import Instrumentator
99

@@ -12,14 +12,13 @@
1212
logger = logging.getLogger(__name__)
1313

1414

15-
llm_instances: dict[str, StudyLLM] = {}
15+
llm_instance: StudyLLM = StudyLLM()
1616

1717
@asynccontextmanager
1818
async def lifespan(_):
1919
yield
2020
# Shutdown: cleanup
21-
for llm in llm_instances.values():
22-
llm.cleanup()
21+
llm_instance.cleanup()
2322

2423
app = FastAPI(
2524
title="tutor",
@@ -48,8 +47,8 @@ async def lifespan(_):
4847
).instrument(app).expose(app)
4948

5049

51-
# llm_instances["dummy"] = StudyLLM("./documents/example/W07_Microservices_and_Scalable_Architectures.pdf") # TODO: remove
52-
# llm_instances["dummy2"] = StudyLLM("./documents/example/dummy_knowledge.txt") # TODO: remove
50+
# StudyLLM(user_id='dummy', doc_path="./documents/example/dummy_knowledge.txt") # TODO: remove
51+
# llm_instances["dummy2"] = StudyLLM(user_id='dummy', doc_path="./documents/example/W07_Microservices_and_Scalable_Architectures.pdf") # TODO: remove
5352

5453
# Auxiliary Endpoints
5554
@app.get("/health")
@@ -62,44 +61,34 @@ async def health_check():
6261

6362

6463
# AI Tasks Endpoints
65-
@app.post("/session/load")
66-
async def load_session(data: CreateSessionRequest):
64+
@app.post("/document")
65+
async def load_document(data: LoadDocumentRequest):
6766
"""
68-
Create a new session with the LLM for a given document URL.
67+
Load a new document in the LLM instance.
6968
"""
7069
try:
71-
if data.session_id in llm_instances:
72-
logger.info(f"Session {data.session_id} already exists")
73-
return {"message": "Session already loaded."}
74-
75-
logger.info(f"Creating new session {data.session_id} for document {data.document_name}")
76-
doc_name = f"{data.session_id}_{data.document_name}"
70+
logger.info(f"Loading new document {data.document_name} for user {data.user_id}")
71+
doc_name = f"{data.user_id}_{data.document_name}"
7772
path = save_document(doc_name, data.document_base64)
78-
llm_instances[data.session_id] = StudyLLM(path)
79-
logger.info(f"Session {data.session_id} created successfully")
80-
return {"message": "Session created successfully."}
73+
await llm_instance.load_document(doc_name, path, data.user_id)
74+
logger.info(f"Document {doc_name} created successfully")
75+
return {"message": "Document loaded successfully."}
8176
except Exception as e:
82-
error_msg = f"Failed to create session {data.session_id}: {str(e)}"
77+
error_msg = f"Failed to load document {doc_name}: {str(e)}"
8378
logger.error(error_msg)
8479
return {"error": error_msg}
8580

86-
8781
@app.post("/chat")
8882
async def receive_prompt(data: PromptRequest):
8983
"""
9084
Receive a prompt and return a response from the LLM.
9185
"""
9286
try:
93-
if data.session_id not in llm_instances:
94-
error_msg = f"Session {data.session_id} not found. Please ensure the document was processed successfully."
95-
logger.error(error_msg)
96-
return JSONResponse(status_code=404, content={"response": f"ERROR: {error_msg}"})
97-
98-
logger.info(f"Processing chat request for session {data.session_id}")
99-
response = await llm_instances[data.session_id].prompt(data.message)
87+
logger.info(f"Processing chat request for user {data.user_id}")
88+
response = await llm_instance.prompt(data.message, data.user_id)
10089
return {"response": response}
10190
except Exception as e:
102-
error_msg = f"Chat error for session {data.session_id}: {str(e)}"
91+
error_msg = f"Chat error for user {data.user_id}: {str(e)}"
10392
logger.error(error_msg)
10493
return {"response": f"ERROR: {error_msg}"}
10594

@@ -109,16 +98,11 @@ async def generate_summary(data: SummaryRequest):
10998
Receive a summary request and return a summary from the LLM.
11099
"""
111100
try:
112-
if data.session_id not in llm_instances:
113-
error_msg = f"Session {data.session_id} not found. Please ensure the document was processed successfully."
114-
logger.error(error_msg)
115-
return {"response": f"ERROR: {error_msg}"}
116-
117-
logger.info(f"Generating summary for session {data.session_id}")
118-
response = await llm_instances[data.session_id].summarize()
101+
logger.info(f"Generating summary for user {data.user_id}, document {data.document_name}")
102+
response = await llm_instance.summarize(data.document_name, data.user_id)
119103
return {"response": response}
120104
except Exception as e:
121-
error_msg = f"Summary generation error for session {data.session_id}: {str(e)}"
105+
error_msg = f"Summary generation error for user {data.user_id}: {str(e)}"
122106
logger.error(error_msg)
123107
return {"response": f"ERROR: {error_msg}"}
124108

@@ -128,17 +112,12 @@ async def generate_flashcards(data: FlashcardRequest):
128112
Receive a flashcard request and return flashcard objects from the LLM.
129113
"""
130114
try:
131-
if data.session_id not in llm_instances:
132-
error_msg = f"Session {data.session_id} not found. Please ensure the document was processed successfully."
133-
logger.error(error_msg)
134-
return {"response": {"flashcards": [], "error": error_msg}}
135-
136-
logger.info(f"Generating flashcards for session {data.session_id}")
137-
response = await llm_instances[data.session_id].generate_flashcards()
138-
logger.info(f"Flashcards generated successfully for session {data.session_id}")
115+
logger.info(f"Generating flashcards for user {data.user_id}, document {data.document_name}")
116+
response = await llm_instance.generate_flashcards(data.document_name, data.user_id)
117+
logger.info(f"Flashcards generated successfully for user {data.user_id}")
139118
return {"response": response}
140119
except Exception as e:
141-
error_msg = f"Flashcard generation error for session {data.session_id}: {str(e)}"
120+
error_msg = f"Flashcard generation error for user {data.user_id}: {str(e)}"
142121
logger.error(error_msg)
143122
return {"response": {"flashcards": [], "error": error_msg}}
144123

@@ -148,44 +127,11 @@ async def generate_quiz(data: QuizRequest):
148127
Receive a quiz request and return a quiz object from the LLM.
149128
"""
150129
try:
151-
if data.session_id not in llm_instances:
152-
error_msg = f"Session {data.session_id} not found. Please ensure the document was processed successfully."
153-
logger.error(error_msg)
154-
return {"response": {"questions": [], "error": error_msg}}
155-
156-
logger.info(f"Generating quiz for session {data.session_id}")
157-
response = await llm_instances[data.session_id].generate_quiz()
158-
logger.info(f"Quiz generated successfully for session {data.session_id}")
130+
logger.info(f"Generating quiz for user {data.user_id}, document {data.document_name}")
131+
response = await llm_instance.generate_quiz(data.document_name, data.user_id)
132+
logger.info(f"Quiz generated successfully for user {data.user_id}")
159133
return {"response": response}
160134
except Exception as e:
161-
error_msg = f"Quiz generation error for session {data.session_id}: {str(e)}"
135+
error_msg = f"Quiz generation error for user {data.user_id}: {str(e)}"
162136
logger.error(error_msg)
163137
return {"response": {"questions": [], "error": error_msg}}
164-
165-
@app.post("/process")
166-
async def process_document(data: SummaryRequest):
167-
"""Compatibility endpoint for Kotlin genai-service (/process).
168-
It creates a session (if not present) and immediately returns QUEUED.
169-
(Actual processing e.g. summary generation can be triggered asynchronously.)"""
170-
try:
171-
session_id = data.session_id or data.document_id
172-
if session_id not in llm_instances:
173-
logger.info(f"/process received – creating session {session_id}")
174-
# save document and create LLM instance
175-
doc_name = f"{session_id}_{data.document_name or 'doc.pdf'}"
176-
path = save_document(doc_name, data.document_base64 or "")
177-
llm_instances[session_id] = StudyLLM(path)
178-
return {
179-
"requestId": session_id,
180-
"status": "QUEUED",
181-
"message": "Document queued for processing",
182-
"estimatedTime": None
183-
}
184-
except Exception as e:
185-
logger.error(f"/process error: {str(e)}")
186-
return {
187-
"requestId": None,
188-
"status": "FAILED",
189-
"message": f"Failed to process document: {str(e)}",
190-
"estimatedTime": None
191-
}

0 commit comments

Comments
 (0)