tattrongvu's picture
fix model list
863166a
import httpx
import os
import time
import subprocess
import uuid
from loguru import logger
from typing import Any, List, Tuple, Type, Literal, Optional, Union, Dict
import httpx
import os
import time
import subprocess
import uuid
import streamlit as st
from openai import OpenAI
import base64
from tqdm import tqdm
from app.config import app_settings
from app.qdrant_db import MyQdrantClient
from app.vdr_utils import (
get_text_embedding,
get_image_embedding,
pdf_folder_to_images,
scale_image,
pil_image_to_base64,
load_images,
)
class VDRSession:
def __init__(self):
self.client = None
self.api_key = None
self.base_url = app_settings.GLOBAL_API_BASE
self.SAVE_DIR = None
self.db_collection = None
self.session_id = str(uuid.uuid4())[:5]
self.indexed_images = []
self.model_name_list = []
self.vector_db_client = None
def set_api_key(self, api_key: str):
if api_key is not None and len(api_key)>10:
try:
api_key = api_key.strip()
client = OpenAI(api_key=api_key,
base_url=self.base_url)
models = client.models.list()
if models:
self.api_key = api_key
self.client = client
return True
except Exception as e:
logger.debug(f'Incorrect API Key: {e}')
self.client = None
return False
def set_context(self, embed_model: str):
self.embed_model = embed_model
if not self.SAVE_DIR:
self.SAVE_DIR=os.path.join('./temp_data', self.session_id)
os.makedirs(self.SAVE_DIR, exist_ok=True)
self.SAVE_IMAGE_DIR=os.path.join(self.SAVE_DIR, 'images')
logger.debug(f'Created folder: {self.SAVE_DIR} and {self.SAVE_IMAGE_DIR}')
if not self.vector_db_client:
self.vector_db_client = MyQdrantClient(path=self.SAVE_DIR)
if not self.db_collection:
self.db_collection = f"qd-{embed_model}-{self.session_id}"
try:
if self.embed_model == "tsi-embedding-colqwen2-2b-v1":
self.vector_db_client.create_collection(self.db_collection, vector_dim=128, vector_type="colbert")
elif self.embed_model == "jina-embedding-clip-v1":
self.vector_db_client.create_collection(self.db_collection, vector_dim=768, vector_type="dense")
else:
raise ValueError(f"Embedding model {self.embed_model} not supported")
except Exception as e:
logger.error(f"Error while creating collection: {e}")
return True
def get_available_vlms(self) -> List[str]:
assert self.client != None
#model_name_list = []
if self.model_name_list:
return self.model_name_list
try:
models = self.client.models.list()
for model in models.data:
model_name = model.id
substrings = ['gemini-2.0','claude','Qwen2.5-VL-72B-Instruct']
if any(substring in model_name for substring in substrings):
self.model_name_list.append(model.id)
except Exception as e:
logger.error(f"Error while query all models: {e}")
raise e
# Prioritize name
# Remove the item if it exists in the list
# priority_item = "gemini-2.0-flash"
# if priority_item in model_name_list:
# model_name_list.remove(priority_item)
# # Insert the item at the beginning of the list
# model_name_list.insert(0, priority_item)
return self.model_name_list
def get_available_image_embeds(self) -> List[str]:
assert self.client != None
model_name_list = []
try:
models = self.client.models.list()
for model in models.data:
model_name = model.id
substrings = ['tsi-embedding','clip']
if any(substring in model_name for substring in substrings):
model_name_list.append(model.id)
except Exception as e:
logger.error(f"Error while query all models: {e}")
raise e
return model_name_list
def search_images(self, text: str, top_k: int = 5) -> list[str]:
assert self.client != None
assert self.vector_db_client != None
try:
if not self.indexed_images:
raise Exception("No indexed images found. You need to click on 'Add selected context' button to index images.")
text = text.strip()
if len(text) < 2:
return False
embeddings = get_text_embedding(
texts=text,
openai_client=self.client,
model=self.embed_model
)[0]
index_results = self.vector_db_client.query_multivector(
multivector_input=embeddings,
collection_name=self.db_collection,
top_k=top_k
)
image_list=[self.indexed_images[i] for i in index_results]
images = []
for img in image_list:
#with open(file, "rb") as image:
#encoded = base64.b64encode(image.read()).decode()
encoded = pil_image_to_base64(img)
images.append(f"data:image/png;base64,{encoded}")
return images
except Exception as e:
logger.error(f"Error while generating image: {e}")
raise e
def ask(self, query: str, model: str, prompt_template: str, retrieved_context: Any, modality: str = "image", stream: bool = False) -> str:
assert self.client != None
assert query != None
assert prompt_template != None
assert retrieved_context != None
try:
prompt = prompt_template.format(user_question=query)
if modality == "image":
context = [
{
"type": "image_url",
"image_url": {
"url": base64_image
}
} for base64_image in retrieved_context
]
content = [
{
"type": "text",
"text": prompt
}
]
content=content+context
messages=[
{
"role": "user",
"content": content,
}
]
chat_response = self.client.chat.completions.create(
model=model,
messages=messages,
temperature=0.1,
max_tokens=2048,
stream=stream,
)
if not stream:
return chat_response.choices[0].message.content
else:
for chunk in chat_response:
if chunk.choices:
if chunk.choices[0].delta.content is not None:
yield chunk.choices[0].delta.content
#print(chunk.choices[0].delta.content, end="", flush=True)
except Exception as e:
logger.error(f"Error while asking: {e}")
raise e
def indexing(self, uploaded_files: list[str], embed_model: str, indexing_bar: Optional[st.progress] = None) -> bool:
self.set_context(embed_model)
assert self.client != None
assert self.db_collection != None
assert self.SAVE_DIR != None
assert self.embed_model != None
assert len(uploaded_files) > 0
# Write files to disk
for file in uploaded_files :
path = os.path.join(self.SAVE_DIR, file.name)
if os.path.exists(path):
print("File existed, skip")
continue
with open(path, "wb") as f:
f.write(file.getvalue())
image_path_list = pdf_folder_to_images(pdf_folder=self.SAVE_DIR, output_folder=self.SAVE_IMAGE_DIR)
logger.debug(f"Extracted {len(image_path_list)} images from {len(uploaded_files)} files.")
indexed_images = self.index_from_images(image_path_list, indexing_bar=indexing_bar)
logger.debug(f"Indexed {len(indexed_images)} images.")
self.indexed_images.extend(indexed_images)
return True
def clear_context(self):
self.indexed_images = []
self.vector_db_client.delete_collection(self.db_collection)
self.db_collection = None
self.vector_db_client = None
if self.SAVE_DIR:
if os.path.exists(self.SAVE_DIR):
subprocess.run(['rm', '-rf', self.SAVE_DIR])
logger.debug(f'Removed folder: {self.SAVE_DIR}')
self.SAVE_DIR = None
return True
def __del__(self):
self.clear_context()
logger.debug('VDR session is cleaned up.')
def index_from_images(self,
images_path_list: list,
batch_size: int =5,
indexing_bar: Optional[st.progress] = None
):
try:
indexed_images = []
total_len = len(images_path_list)
with tqdm(total=total_len, desc="Indexing Progress") as pbar:
for i in range(0, total_len, batch_size):
try:
batch = images_path_list[i:min(i+batch_size,total_len)]
#batch = load_images(batch)
batch = [scale_image(x, 768) for x in batch]
embeddings = get_image_embedding(
image_list=batch,
openai_client=self.client,
model=self.embed_model
)
self.vector_db_client.upsert_multivector(
index=i,
multivector_input_list=embeddings,
collection_name=self.db_collection
)
indexed_images.extend(batch)
# Update the progress bar
pbar.update(batch_size)
indexing_bar.progress(i/total_len, text=f"Indexing {i}/{total_len}")
except Exception as e:
logger.exception(f"Error during indexing: {e}")
continue
return indexed_images
logger.debug("Indexing complete!")
except Exception as e:
raise Exception(f"Error during indexing: {e}")