Skip to content

Commit 3d72a4e

Browse files
authored
Merge pull request #35 from AET-DevOps25/feature/genai-llm-abstraction
feat(genai): Implement LLM abstraction layer
2 parents af5fed1 + 1b44eb3 commit 3d72a4e

File tree

5 files changed

+105
-1
lines changed

5 files changed

+105
-1
lines changed

genai/dummy_llm.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# genai/dummy_llm.py
2+
from fastapi import FastAPI
3+
import uvicorn
4+
5+
# This dummy server mimics the API of local LLM hosts like LM Studio or Ollama
6+
app = FastAPI()
7+
8+
@app.post("/v1/chat/completions")
9+
def dummy_completion():
10+
return {
11+
"choices": [{
12+
"message": {
13+
"role": "assistant",
14+
"content": "This is a dummy summary from the local model."
15+
}
16+
}]
17+
}
18+
19+
if __name__ == "__main__":
20+
print("Starting Dummy LLM Server on port 8001...")
21+
uvicorn.run(app, host="0.0.0.0", port=8001)

genai/src/main.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,11 @@
1313
from .services.embedding import embedder_service
1414
from .services.embedding.schemas import EmbedRequest, EmbedResponse, QueryRequest, QueryResponse, DocumentResult
1515
from .services.embedding.weaviate_service import get_weaviate_client, ensure_schema_exists, DOCUMENT_CLASS_NAME
16+
from .services.llm import llm_service
17+
from .services.llm.schemas import GenerateRequest, GenerateResponse
1618
from langchain_openai import OpenAIEmbeddings
1719

20+
1821
# --- Configuration ---
1922
logging.basicConfig(level=logging.INFO)
2023
load_dotenv()
@@ -101,4 +104,18 @@ def query_vector_db(request: QueryRequest):
101104

102105
docs_data = result["data"]["Get"][DOCUMENT_CLASS_NAME]
103106
docs = [DocumentResult(**doc) for doc in docs_data]
104-
return QueryResponse(query=request.query_text, results=docs)
107+
return QueryResponse(query=request.query_text, results=docs)
108+
109+
@app.post("/generate", response_model=GenerateResponse)
110+
def generate_completion(request: GenerateRequest):
111+
"""Generates a text completion using the configured LLM abstraction layer."""
112+
try:
113+
generated_text = llm_service.generate_text(request.prompt)
114+
return GenerateResponse(
115+
prompt=request.prompt,
116+
generated_text=generated_text,
117+
provider=os.getenv("LLM_PROVIDER", "dummy")
118+
)
119+
except Exception as e:
120+
logging.error(f"ERROR during text generation: {e}")
121+
raise HTTPException(status_code=500, detail=f"Failed to generate text: {str(e)}")

genai/src/services/llm/__init__.py

Whitespace-only changes.
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# genai/src/services/llm/llm_service.py
2+
3+
import os
4+
import logging
5+
from langchain_openai import ChatOpenAI
6+
from langchain_community.llms import FakeListLLM
7+
from langchain_core.language_models.base import BaseLanguageModel
8+
9+
def llm_factory() -> BaseLanguageModel:
10+
"""
11+
Factory function to create and return an LLM instance based on the provider
12+
specified in the environment variables.
13+
"""
14+
provider = os.getenv("LLM_PROVIDER", "dummy").lower()
15+
logging.info(f"--- Creating LLM for provider: {provider} ---")
16+
17+
if provider == "openai":
18+
if not os.getenv("OPENAI_API_KEY"):
19+
raise ValueError("OPENAI_API_KEY is not set for the 'openai' provider.")
20+
# Returns a high-quality chat model from OpenAI
21+
return ChatOpenAI(model="gpt-4o-mini", temperature=0.7)
22+
23+
elif provider == "dummy":
24+
# This is a fake LLM for testing. It will cycle through these responses.
25+
responses = [
26+
"The first summary from the dummy LLM is about procedural languages.",
27+
"The second summary is about object-oriented programming.",
28+
"This is a fallback response.",
29+
]
30+
return FakeListLLM(responses=responses)
31+
32+
# In the future, you could add other providers like 'ollama' here
33+
# elif provider == "ollama":
34+
# return ChatOllama(model="llama3")
35+
36+
else:
37+
raise ValueError(f"Unsupported LLM provider: {provider}")
38+
39+
def generate_text(prompt: str) -> str:
40+
"""
41+
Generates a text completion for a given prompt using the configured LLM.
42+
"""
43+
# 1. Get the correct LLM instance from our factory
44+
llm = llm_factory()
45+
46+
# 2. Invoke the LLM with the prompt
47+
# (The .invoke() method is standard across all LangChain models)
48+
response = llm.invoke(prompt)
49+
50+
# 3. The response object's structure can vary slightly by model.
51+
# For Chat models, the text is in the .content attribute.
52+
# For standard LLMs (like our FakeListLLM), it's the string itself.
53+
if hasattr(response, 'content'):
54+
return response.content
55+
else:
56+
return response

genai/src/services/llm/schemas.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# genai/src/services/llm/schemas.py
2+
from pydantic import BaseModel
3+
4+
class GenerateRequest(BaseModel):
5+
prompt: str
6+
7+
class GenerateResponse(BaseModel):
8+
prompt: str
9+
generated_text: str
10+
provider: str

0 commit comments

Comments
 (0)