Commit
·
16f0db6
1
Parent(s):
60924bb
Refactor Dockerfile to copy all application files into the container; add .gitignore for IDE files; update main.py and image_service.py to use OUTPUT_DIR from config; streamline file handling in process_file_service.py; remove unused OpenAI client initialization; enhance vector_store_service.py with configuration constants for improved maintainability.
Browse files- .idea/.gitignore +3 -0
- Dockerfile +4 -2
- src/constants/config.py +15 -0
- src/main.py +3 -2
- src/services/chat_service.py +0 -2
- src/services/image_service.py +5 -5
- src/services/process_file_service.py +1 -3
- src/services/vector_store_service.py +4 -11
- src/utils/client.py +0 -4
- src/utils/image_pipeline.py +4 -10
.idea/.gitignore
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
# Default ignored files
|
2 |
+
/shelf/
|
3 |
+
/workspace.xml
|
Dockerfile
CHANGED
@@ -43,8 +43,8 @@ RUN pip install --no-cache-dir --upgrade -r requirements.txt
|
|
43 |
# Installs all Python dependencies listed in requirements.txt. --no-cache-dir reduces image size.
|
44 |
|
45 |
# 6. Copy the rest of the application code
|
46 |
-
COPY ./src ./src
|
47 |
-
COPY ./README.md .
|
48 |
|
49 |
# What is this?
|
50 |
# Copies your source code and readme into the container.
|
@@ -54,6 +54,8 @@ EXPOSE 7860
|
|
54 |
|
55 |
# What is this?
|
56 |
# Documents that the container will listen on port 8080 (matches your uvicorn command).
|
|
|
|
|
57 |
|
58 |
# 8. Set the default command to run the FastAPI app
|
59 |
CMD ["fastapi", "run", "src/main.py", "--port", "7860"]
|
|
|
43 |
# Installs all Python dependencies listed in requirements.txt. --no-cache-dir reduces image size.
|
44 |
|
45 |
# 6. Copy the rest of the application code
|
46 |
+
# COPY ./src ./src
|
47 |
+
# COPY ./README.md .
|
48 |
|
49 |
# What is this?
|
50 |
# Copies your source code and readme into the container.
|
|
|
54 |
|
55 |
# What is this?
|
56 |
# Documents that the container will listen on port 8080 (matches your uvicorn command).
|
57 |
+
# Copy toàn bộ mã nguồn
|
58 |
+
COPY . .
|
59 |
|
60 |
# 8. Set the default command to run the FastAPI app
|
61 |
CMD ["fastapi", "run", "src/main.py", "--port", "7860"]
|
src/constants/config.py
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Device setup
|
2 |
+
import torch
|
3 |
+
|
4 |
+
TORCH_DEVICE = (
|
5 |
+
"cuda" if torch.cuda.is_available()
|
6 |
+
else "mps" if torch.backends.mps.is_available()
|
7 |
+
else "cpu"
|
8 |
+
)
|
9 |
+
IMAGE_MODEL_ID_OR_LINK = "stable-diffusion-v1-5/stable-diffusion-v1-5"
|
10 |
+
CACHE_DIR = "/tmp/cache"
|
11 |
+
DATA_DIR = "/tmp/data"
|
12 |
+
EMBEDDING_MODEL = "intfloat/multilingual-e5-large-instruct"
|
13 |
+
UPLOAD_DIR = "/tmp/uploads"
|
14 |
+
OUTPUT_DIR = "/tmp/outputs"
|
15 |
+
# EMBEDDING_MODEL = "sentence-transformers/all-MiniLM-L6-v2"
|
src/main.py
CHANGED
@@ -4,6 +4,7 @@ from fastapi.exceptions import RequestValidationError
|
|
4 |
from fastapi.middleware.cors import CORSMiddleware
|
5 |
from fastapi.responses import JSONResponse
|
6 |
from fastapi.staticfiles import StaticFiles
|
|
|
7 |
from models.responses.base_response import BaseResponse
|
8 |
from routes import chat_routes, process_file_routes, vector_store_routes
|
9 |
from utils.exception import CustomException
|
@@ -48,5 +49,5 @@ app.include_router(vector_store_routes.router, prefix="/api/v1")
|
|
48 |
def read_root():
|
49 |
return {"message": "Welcome my API"}
|
50 |
|
51 |
-
os.makedirs(
|
52 |
-
app.mount(
|
|
|
4 |
from fastapi.middleware.cors import CORSMiddleware
|
5 |
from fastapi.responses import JSONResponse
|
6 |
from fastapi.staticfiles import StaticFiles
|
7 |
+
from constants.config import OUTPUT_DIR
|
8 |
from models.responses.base_response import BaseResponse
|
9 |
from routes import chat_routes, process_file_routes, vector_store_routes
|
10 |
from utils.exception import CustomException
|
|
|
49 |
def read_root():
|
50 |
return {"message": "Welcome my API"}
|
51 |
|
52 |
+
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
53 |
+
app.mount(OUTPUT_DIR, StaticFiles(directory=OUTPUT_DIR), name="outputs")
|
src/services/chat_service.py
CHANGED
@@ -3,8 +3,6 @@ from models.requests.chat_request import ChatRequest
|
|
3 |
from services import vector_store_service
|
4 |
from utils.timing import measure_time
|
5 |
from utils.tools import tools_helper, tools_define
|
6 |
-
from utils.client import openai_client
|
7 |
-
|
8 |
|
9 |
def build_context_prompt(request: ChatRequest) -> list:
|
10 |
"""Build system prompt with context if file is provided."""
|
|
|
3 |
from services import vector_store_service
|
4 |
from utils.timing import measure_time
|
5 |
from utils.tools import tools_helper, tools_define
|
|
|
|
|
6 |
|
7 |
def build_context_prompt(request: ChatRequest) -> list:
|
8 |
"""Build system prompt with context if file is provided."""
|
src/services/image_service.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1 |
import os
|
2 |
import time
|
|
|
3 |
from utils import image_pipeline
|
4 |
|
5 |
negative_promt = "blurry, distorted, pixelated, incomplete, poorly drawn, misaligned, weird proportions, bad perspective, unnatural colors, noisy, out of focus, glitchy, unsharp, overexposed, underexposed, poorly lit, bad composition, excessive noise, oversaturated, too dark, too bright, inconsistent lighting, discolored, overly stylized, unrealistic, awkward pose, unbalanced, mismatched, distorted features, flat, unnatural texture, chaotic, unreadable, incoherent, asymmetrical, low quality, lowres, wrong anatomy, bad anatomy, deformed, disfigured, ugly"
|
@@ -8,7 +9,7 @@ height = 512
|
|
8 |
guidance_scale = 7.5
|
9 |
num_inference_steps = 30
|
10 |
|
11 |
-
base_url = "http://
|
12 |
|
13 |
def generate_image_url(prompt: str) -> str:
|
14 |
"""
|
@@ -16,8 +17,7 @@ def generate_image_url(prompt: str) -> str:
|
|
16 |
:param prompt: The prompt used for generate the image (must be in English)
|
17 |
:output: URL of the new image
|
18 |
"""
|
19 |
-
|
20 |
-
os.makedirs(output_dir, exist_ok=True)
|
21 |
try:
|
22 |
image = image_pipeline.pipeline(
|
23 |
prompt=prompt,
|
@@ -29,9 +29,9 @@ def generate_image_url(prompt: str) -> str:
|
|
29 |
).images[0]
|
30 |
|
31 |
file_name = f"image_{int(time.time())}.png"
|
32 |
-
image_path = os.path.join(
|
33 |
image.save(image_path)
|
34 |
|
35 |
-
return f"{base_url}/
|
36 |
except Exception as e:
|
37 |
raise RuntimeError(f"Failed to generate image: {e}")
|
|
|
1 |
import os
|
2 |
import time
|
3 |
+
from constants.config import OUTPUT_DIR
|
4 |
from utils import image_pipeline
|
5 |
|
6 |
negative_promt = "blurry, distorted, pixelated, incomplete, poorly drawn, misaligned, weird proportions, bad perspective, unnatural colors, noisy, out of focus, glitchy, unsharp, overexposed, underexposed, poorly lit, bad composition, excessive noise, oversaturated, too dark, too bright, inconsistent lighting, discolored, overly stylized, unrealistic, awkward pose, unbalanced, mismatched, distorted features, flat, unnatural texture, chaotic, unreadable, incoherent, asymmetrical, low quality, lowres, wrong anatomy, bad anatomy, deformed, disfigured, ugly"
|
|
|
9 |
guidance_scale = 7.5
|
10 |
num_inference_steps = 30
|
11 |
|
12 |
+
base_url = "http://0.0.0.0:7860"
|
13 |
|
14 |
def generate_image_url(prompt: str) -> str:
|
15 |
"""
|
|
|
17 |
:param prompt: The prompt used for generate the image (must be in English)
|
18 |
:output: URL of the new image
|
19 |
"""
|
20 |
+
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
|
|
21 |
try:
|
22 |
image = image_pipeline.pipeline(
|
23 |
prompt=prompt,
|
|
|
29 |
).images[0]
|
30 |
|
31 |
file_name = f"image_{int(time.time())}.png"
|
32 |
+
image_path = os.path.join(OUTPUT_DIR, file_name)
|
33 |
image.save(image_path)
|
34 |
|
35 |
+
return f"{base_url}/{OUTPUT_DIR}/{file_name}"
|
36 |
except Exception as e:
|
37 |
raise RuntimeError(f"Failed to generate image: {e}")
|
src/services/process_file_service.py
CHANGED
@@ -6,12 +6,10 @@ from PIL import Image
|
|
6 |
import pytesseract
|
7 |
from langchain_community.document_loaders import PyMuPDFLoader
|
8 |
from langchain_text_splitters import RecursiveCharacterTextSplitter
|
|
|
9 |
from constants.file_type import FileType
|
10 |
from services import vector_store_service
|
11 |
|
12 |
-
UPLOAD_DIR = "uploads"
|
13 |
-
os.makedirs(UPLOAD_DIR, exist_ok=True)
|
14 |
-
|
15 |
def save_file(file):
|
16 |
ext = os.path.splitext(file.filename)[-1].lstrip(".")
|
17 |
file_id = str(uuid.uuid4())
|
|
|
6 |
import pytesseract
|
7 |
from langchain_community.document_loaders import PyMuPDFLoader
|
8 |
from langchain_text_splitters import RecursiveCharacterTextSplitter
|
9 |
+
from constants.config import UPLOAD_DIR
|
10 |
from constants.file_type import FileType
|
11 |
from services import vector_store_service
|
12 |
|
|
|
|
|
|
|
13 |
def save_file(file):
|
14 |
ext = os.path.splitext(file.filename)[-1].lstrip(".")
|
15 |
file_id = str(uuid.uuid4())
|
src/services/vector_store_service.py
CHANGED
@@ -1,17 +1,10 @@
|
|
1 |
from chromadb import PersistentClient
|
2 |
from langchain_chroma import Chroma
|
3 |
from langchain_huggingface import HuggingFaceEmbeddings
|
4 |
-
import
|
5 |
|
6 |
-
client = PersistentClient(path=
|
7 |
-
|
8 |
-
EMBEDDING_MODEL = "sentence-transformers/all-MiniLM-L6-v2"
|
9 |
-
_device = (
|
10 |
-
"cuda" if torch.cuda.is_available()
|
11 |
-
else "mps" if torch.backends.mps.is_available()
|
12 |
-
else "cpu"
|
13 |
-
)
|
14 |
-
embeddings_function = HuggingFaceEmbeddings(model_name=EMBEDDING_MODEL, model_kwargs={'device': _device})
|
15 |
vector_store = {}
|
16 |
|
17 |
def get_all_collections_id():
|
@@ -25,7 +18,7 @@ def inspect_collection(collection_id: str):
|
|
25 |
def get_vector_store(collection_name) -> Chroma:
|
26 |
if collection_name not in vector_store:
|
27 |
vector_store[collection_name] = Chroma(
|
28 |
-
persist_directory=
|
29 |
collection_name=collection_name,
|
30 |
embedding_function=embeddings_function,
|
31 |
)
|
|
|
1 |
from chromadb import PersistentClient
|
2 |
from langchain_chroma import Chroma
|
3 |
from langchain_huggingface import HuggingFaceEmbeddings
|
4 |
+
from constants.config import DATA_DIR, EMBEDDING_MODEL, TORCH_DEVICE
|
5 |
|
6 |
+
client = PersistentClient(path=DATA_DIR)
|
7 |
+
embeddings_function = HuggingFaceEmbeddings(model_name=EMBEDDING_MODEL, model_kwargs={'device': TORCH_DEVICE})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
vector_store = {}
|
9 |
|
10 |
def get_all_collections_id():
|
|
|
18 |
def get_vector_store(collection_name) -> Chroma:
|
19 |
if collection_name not in vector_store:
|
20 |
vector_store[collection_name] = Chroma(
|
21 |
+
persist_directory=DATA_DIR,
|
22 |
collection_name=collection_name,
|
23 |
embedding_function=embeddings_function,
|
24 |
)
|
src/utils/client.py
CHANGED
@@ -1,8 +1,4 @@
|
|
1 |
import openai
|
2 |
-
from dotenv import load_dotenv
|
3 |
-
|
4 |
-
# Load environment variables
|
5 |
-
load_dotenv()
|
6 |
|
7 |
# Initialize OpenAI API client
|
8 |
openai_client = openai.OpenAI(
|
|
|
1 |
import openai
|
|
|
|
|
|
|
|
|
2 |
|
3 |
# Initialize OpenAI API client
|
4 |
openai_client = openai.OpenAI(
|
src/utils/image_pipeline.py
CHANGED
@@ -1,14 +1,8 @@
|
|
1 |
import torch
|
2 |
from diffusers import StableDiffusionPipeline
|
|
|
3 |
|
4 |
-
# Device setup
|
5 |
-
_device = (
|
6 |
-
"cuda" if torch.cuda.is_available()
|
7 |
-
else "mps" if torch.backends.mps.is_available()
|
8 |
-
else "cpu"
|
9 |
-
)
|
10 |
torch.backends.cuda.matmul.allow_tf32 = True # Enable TF32 for performance on CUDA
|
11 |
-
_model_id_or_link = "stable-diffusion-v1-5/stable-diffusion-v1-5"
|
12 |
|
13 |
_pipeline = None
|
14 |
|
@@ -17,20 +11,20 @@ def get_pipeline() -> StableDiffusionPipeline:
|
|
17 |
if _pipeline is None:
|
18 |
try:
|
19 |
_pipeline = StableDiffusionPipeline.from_pretrained(
|
20 |
-
|
21 |
torch_dtype=torch.bfloat16,
|
22 |
variant="fp16",
|
23 |
# safety_checker=True,
|
24 |
use_safetensors=True,
|
25 |
)
|
26 |
# _pipeline = StableDiffusionPipeline.from_single_file(
|
27 |
-
#
|
28 |
# torch_dtype=torch.bfloat16,
|
29 |
# variant="fp16",
|
30 |
# # safety_checker=True,
|
31 |
# use_safetensors=True,
|
32 |
# )
|
33 |
-
_pipeline.to(
|
34 |
except Exception as e:
|
35 |
raise RuntimeError(f"Failed to load the model: {e}")
|
36 |
return _pipeline
|
|
|
1 |
import torch
|
2 |
from diffusers import StableDiffusionPipeline
|
3 |
+
from constants.config import IMAGE_MODEL_ID_OR_LINK, TORCH_DEVICE
|
4 |
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
torch.backends.cuda.matmul.allow_tf32 = True # Enable TF32 for performance on CUDA
|
|
|
6 |
|
7 |
_pipeline = None
|
8 |
|
|
|
11 |
if _pipeline is None:
|
12 |
try:
|
13 |
_pipeline = StableDiffusionPipeline.from_pretrained(
|
14 |
+
IMAGE_MODEL_ID_OR_LINK,
|
15 |
torch_dtype=torch.bfloat16,
|
16 |
variant="fp16",
|
17 |
# safety_checker=True,
|
18 |
use_safetensors=True,
|
19 |
)
|
20 |
# _pipeline = StableDiffusionPipeline.from_single_file(
|
21 |
+
# IMAGE_MODEL_ID_OR_LINK,
|
22 |
# torch_dtype=torch.bfloat16,
|
23 |
# variant="fp16",
|
24 |
# # safety_checker=True,
|
25 |
# use_safetensors=True,
|
26 |
# )
|
27 |
+
_pipeline.to(TORCH_DEVICE)
|
28 |
except Exception as e:
|
29 |
raise RuntimeError(f"Failed to load the model: {e}")
|
30 |
return _pipeline
|