Commit e18aa9a8 authored by server1's avatar server1
Browse files

KHANHNQ: init service openai

parents
Pipeline #452 failed with stages
in 0 seconds
class WarningResult:
WARNING="WARNING"
ACCEPTED="ACCEPTED"
\ No newline at end of file
from openai import OpenAI
from pydantic import BaseModel
from typing import Optional, List
import os
from dotenv import load_dotenv
import io
import requests
from docx import Document
import pandas as pd
import base64
import mimetypes
from pathlib import Path
from markitdown import MarkItDown
import logging
# Configure module-level logger
logger = logging.getLogger(__name__)
if not logger.handlers:
# Basic configuration - user can override by configuring root logger in their app
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(levelname)-8s [%(name)s] %(message)s",
)
load_dotenv()
client = OpenAI()
class FileProcessingRequest(BaseModel):
file_url: Optional[str] = None
mime_type: str
file_name: str
file_content: Optional[str] = None
query: str
class FileProcessingResponse(BaseModel):
output_text: Optional[str] = None
success: bool
error: Optional[str] = None
class FileProcessor:
@staticmethod
def extract_text_from_url(file_url: Optional[str]):
url_suffix = FileProcessor._get_url_suffix(file_url)
if url_suffix == ".pdf":
return FileProcessor._extract_pdf_url_with_openai(file_url)
elif url_suffix == ".jpg" or url_suffix == ".jpeg" or url_suffix == ".png" or url_suffix == ".gif" or url_suffix == ".bmp" or url_suffix == ".tiff" or url_suffix == ".ico" or url_suffix == ".webp":
return FileProcessor._extract_image_text_from_url(file_url)
elif url_suffix == ".docx":
return FileProcessor._extract_docx_text_from_url(file_url)
elif url_suffix == ".txt":
return FileProcessor._extract_txt_text_from_url(file_url)
elif url_suffix == ".json" or url_suffix == ".jsonl":
return FileProcessor._extract_json_text_from_url(file_url)
else:
raise ValueError(f"Unsupported file type: {url_suffix}")
@staticmethod
def extract_text_from_file(file_path: Optional[str]):
file_ext = os.path.splitext(file_path)[1].lower()
if file_ext == ".docx" or file_ext == ".pptx" or file_ext == ".doc":
return FileProcessor.convert_to_markdown_with_markitdown(file_path)
elif file_ext == ".txt" or file_ext == ".md" or file_ext == ".py" or file_ext == ".c" or file_ext == ".cpp":
with open(file_path, "r") as file:
return file.read()
elif file_ext == ".png" or file_ext == ".jpg" or file_ext == ".jpeg" or file_ext == ".gif" or file_ext == ".bmp" or file_ext == ".tiff" or file_ext == ".ico" or file_ext == ".webp":
return FileProcessor.decode_image(file_path)
else:
raise ValueError(f"Unsupported file type: {file_ext}")
@staticmethod
def decode_image(file_path: str):
try:
with open(file_path, "rb") as image_file:
return base64.b64encode(image_file.read()).decode("utf-8")
except Exception as e:
raise Exception(f"Error decoding image: {str(e)}")
@staticmethod
def convert_to_markdown_with_markitdown(file_path: str):
if file_path == None:
raise ValueError("File path is required")
markdown_converter = MarkItDown(enable_plugins = False)
result = markdown_converter.convert(file_path)
return result.text_content
@staticmethod
def _extract_pdf_url_with_openai(file_url: str):
"""Fallback: sử dụng OpenAI để xử lý PDF"""
try:
response = client.responses.create(
model="gpt-4o",
input=[
{
"role": "user",
"content": [
{
"type": "input_text",
"text": "Extract all text from this PDF document.",
},
{
"type": "input_file",
"file_url": file_url,
},
],
},
]
)
return response.output_text
except Exception as e:
raise Exception(f"Error processing PDF with OpenAI: {str(e)}")
@staticmethod
def _extract_image_text_from_url(file_url: str) -> str:
with open(file_url, "rb") as image_file:
base64_image = base64.b64encode(image_file.read()).decode("utf-8")
image_type = mimetypes.guess_type(file_url)[0]
response = client.responses.create(
model="gpt-4o",
input=[
{
"role": "user",
"content": [
{
"type": "input_text",
"text": "Extract all text from this image.",
},
{
"type": "input_image",
"image_url": f"data:image/{image_type};base64,{base64_image}",
},
],
},
]
)
return response.output_text
@staticmethod
def _extract_docx_text_from_url(file_url: str) -> str:
"""Trích xuất văn bản từ DOCX từ URL"""
if FileProcessor._is_http_url(file_url):
response = requests.get(file_url)
response.raise_for_status()
file_content = response.content
doc_file = io.BytesIO(file_content)
else:
with open(file_url, "rb") as f:
doc_file = io.BytesIO(f.read())
doc = Document(doc_file)
text = ""
for paragraph in doc.paragraphs:
text += paragraph.text + "\n"
return text.strip()
@staticmethod
def _extract_txt_text_from_url(file_url: str) -> str:
"""Trích xuất văn bản từ TXT từ URL"""
if FileProcessor._is_http_url(file_url):
response = requests.get(file_url)
response.raise_for_status()
file_content = response.content
else:
with open(file_url, "rb") as f:
file_content = f.read()
encodings = ['utf-8', 'utf-16', 'latin-1', 'cp1252']
for encoding in encodings:
try:
return file_content.decode(encoding)
except UnicodeDecodeError:
continue
return file_content.decode('utf-8', errors='replace')
@staticmethod
def _extract_json_text_from_url(file_url: str) -> str:
return pd.read_json(file_url).to_markdown()
@staticmethod
def _get_url_suffix(url: str) -> str:
"""Extract file extension from URL, handling query parameters and fragments"""
if not url:
return ""
# Remove query parameters and fragments
url_path = url.split('?')[0].split('#')[0]
# Extract the last part after the last slash
filename = url_path.split('/')[-1]
# Get the extension
if '.' in filename:
return '.' + filename.split('.')[-1].lower()
else:
return ""
@staticmethod
def is_url(value: Optional[str]) -> bool:
"""Return True if the input looks like a web URL (http/https/data/file)."""
if not value:
return False
lower = value.lower()
return lower.startswith("http://") or lower.startswith("https://") or lower.startswith("data:") or lower.startswith("file:")
@staticmethod
def is_local_file(value: Optional[str]) -> bool:
"""Return True if the input is an existing local file path."""
if not value:
return False
try:
return Path(value).exists() and Path(value).is_file()
except Exception:
return False
@staticmethod
def detect_source_type(value: Optional[str]) -> str:
"""Detect whether the input is 'url', 'file', or 'unknown'."""
if FileProcessor.is_url(value):
return "url"
if FileProcessor.is_local_file(value):
return "file"
return "unknown"
@staticmethod
def get_extension(value: Optional[str]) -> str:
"""Get lowercase extension including dot from a URL or file path; empty string if none."""
if not value:
return ""
if FileProcessor.is_url(value):
return FileProcessor._get_url_suffix(value)
return os.path.splitext(value)[1].lower()
@staticmethod
def _is_http_url(maybe_url: str) -> bool:
if not maybe_url:
return False
lower = maybe_url.lower()
return lower.startswith("http://") or lower.startswith("https://")
@staticmethod
def _ensure_url_or_data_url(file_url: str, fallback_mime: Optional[str] = None) -> str:
"""Return an https URL or a data URL for local file paths.
If input is already an https/data URL, return as-is.
"""
if not file_url:
return file_url
lower = file_url.lower()
if lower.startswith("http://") or lower.startswith("https://") or lower.startswith("data:"):
return file_url
path = Path(file_url)
if not path.exists() or not path.is_file():
return file_url
mime_type, _ = mimetypes.guess_type(str(path))
if not mime_type:
mime_type = fallback_mime or "application/octet-stream"
with open(path, "rb") as f:
b64 = base64.b64encode(f.read()).decode("ascii")
return f"data:{mime_type};base64,{b64}"
def _split_text(text: str, max_chars: int = 8000, overlap: int = 500) -> list:
"""Split long text into overlapping chunks by characters (approx token-safe)."""
if max_chars <= 0:
return [text]
chunks = []
start = 0
n = len(text)
while start < n:
end = min(start + max_chars, n)
chunks.append(text[start:end])
if end == n:
break
start = max(0, end - overlap)
return chunks
def analyze_text_with_openai(text: str, query: str, model: str = "gpt-4o-mini") -> FileProcessingResponse:
"""Analyze text with chunking to avoid token limits; map-reduce summarization."""
logger.debug("analyze_text_with_openai called with model=%s and text length=%d", model, len(text))
try:
if len(text) <= 8000:
response = client.responses.create(
model=model,
input=[
{
"role": "user",
"content": [
{"type": "input_text", "text": query},
{"type": "input_text", "text": text},
],
}
],
)
logger.info("Single-call analysis succeeded; model=%s", model)
return FileProcessingResponse(success=True, output_text=response.output_text)
chunks = _split_text(text, max_chars=8000, overlap=500)
partial_summaries = []
for idx, chunk in enumerate(chunks, start=1):
part_query = f"{query}\n\nYou are analyzing part {idx} of {len(chunks)}. Provide a concise, self-contained summary with key points and important details."
logger.debug("Sending chunk %d/%d to model %s", idx, len(chunks), model)
resp = client.responses.create(
model=model,
input=[
{
"role": "user",
"content": [
{"type": "input_text", "text": part_query},
{"type": "input_text", "text": chunk},
],
}
],
)
partial_summaries.append(resp.output_text or "")
combined_text = "\n\n".join(partial_summaries)
final_prompt = (
"Combine and deduplicate the following chunk summaries into a single, well-structured answer. "
"Ensure global coherence, remove overlaps, and include an executive summary and key bullet points."
)
logger.debug("Sending final aggregation prompt to model %s", model)
final_resp = client.responses.create(
model=model,
input=[
{
"role": "user",
"content": [
{"type": "input_text", "text": final_prompt},
{"type": "input_text", "text": combined_text},
],
}
],
)
logger.info("Chunked analysis completed successfully")
return FileProcessingResponse(success=True, output_text=final_resp.output_text)
except Exception as e:
logger.exception("Error during analyze_text_with_openai: %s", e)
return FileProcessingResponse(success=False, error=str(e))
def get_answer_from_gpt(query: str, model: str = "gpt-4o-mini", file_path: Optional[str] = None, file_url: Optional[str] = None) -> str:
logger.info("get_answer_from_gpt called; file_path=%s file_url=%s", file_path, file_url)
file_processor = FileProcessor()
response = ""
try:
if file_url:
text = file_processor.extract_text_from_url(file_url)
logger.debug("Extracted text from URL; length=%d", len(text) if text else 0)
response = client.responses.create(
model=model,
input=[
{
"role": "user",
"content": [
{"type": "input_text", "text": query},
{"type": "input_text", "text": text},
],
}
],
)
elif file_path:
file_ext = os.path.splitext(file_path)[1].lower()
logger.debug("Detected file extension: %s", file_ext)
if file_ext == ".xlsx":
try:
initial_file_response = client.files.create(
file=open('test.xlsx', 'rb'),
purpose="user_data"
)
initial_file_id = initial_file_response.id
logger.info("Uploaded file test.xlsx to OpenAI files; id=%s", initial_file_id)
response = client.responses.create(
model="gpt-4.1", # Or another supported model
input=query,
tools=[{
"type": "code_interpreter",
"container": {"type": "auto", "file_ids": [initial_file_id]}
}],
instructions="Summarize the key information from the Excel file.",
)
except Exception as e:
logger.exception("Failed to upload or use file with code_interpreter: %s", e)
raise
elif file_ext == ".png" or file_ext == "jpg":
base64_image = file_processor.decode_image(file_path)
response = client.responses.create(
model="gpt-4.1",
input=[
{
"role": "user",
"content": [
{ "type": "input_text", "text": query },
{
"type": "input_image",
"image_url": f"data:image/{file_ext};base64,{base64_image}",
},
],
}
],
)
elif file_ext == ".pdf":
file = client.files.create(
file=open(file_path, 'rb'),
purpose="user_data"
)
response = client.responses.create(
model = model,
input = [
{
"role": "user",
"content": [
{"type": "input_text", "text": query},
{
"type": "input_file",
"file_id": file.id,
},
],
}
]
)
else:
text = file_processor.extract_text_from_file(file_path)
logger.debug("Extracted text from file; length=%d", len(text) if text else 0)
response = client.responses.create(
model=model,
input=[
{
"role": "user",
"content": [
{"type": "input_text", "text": query},
{"type": "input_text", "text": text},
],
}
],
)
else:
raise Exception("Either file_path or file_url must be provided")
except Exception as e:
logger.exception("Error in get_answer_from_gpt: %s", e)
return FileProcessingResponse(success=False, error=str(e))
response = FileProcessingResponse(success=True, output_text=response.output_text)
return response
if __name__ == '__main__':
# Example invocation moved inside main guard
try:
resp = get_answer_from_gpt("Tóm tắt nội dung của file này", file_path="D:\\OpenAI SDK\\2023.pdf")
logger.info("Final response: %s", getattr(resp, 'output_text', None))
except Exception as ex:
logger.exception("Unhandled exception when running example: %s", ex)
\ No newline at end of file
"Họ tên","Tuổi","Quê quán","Môn học giỏi ở cấp 3","Hoàn cảnh","Kĩ năng chuyên môn","Kĩ năng mềm","Nhu cầu","Mục tiêu ngành học","Lời khuyên cá nhân hóa"
"Khánh","18","Hà Nội, Việt Nam","Toán","Trung bình (sống cùng gia đình; đủ học phí nhưng không dư dả; bố mẹ công nhân ~10–15 triệu/tháng; có em trai lớp 9; đi học xa ~10km, chi phí đi lại cao; dự định làm thêm)","Toán chuyên; tư duy logic tốt; đang học C/C++; chưa có project; tiếng Anh trung bình; định hướng AI/Data (LLM); chưa có laptop (dùng phòng máy tạm)","Mạnh: giao tiếp, thuyết trình; Yếu: quản lý thời gian/kỷ luật, xử lý áp lực","Việc làm thêm 20–24h/tuần (ưu tiên quán cafe; mong việc sát ngành); thông tin học bổng; lộ trình học AI/LLM; mua laptop; tối ưu chi phí đi lại","Nắm vững nền tảng 1–2 năm; tìm hiểu AI; định hướng LLM; săn học bổng; thi IELTS năm 4","Bắt đầu làm thêm 12–16 giờ/tuần 1–2 tháng đầu để ổn định học, sau đó nâng 16–20 giờ nếu GPA ổn; ưu tiên ca gần trường để giảm đi lại; cân nhắc IT Helpdesk/CLB, data labeling/annotation, quản trị web cơ bản. Lộ trình AI/LLM 12 tháng: 0–3 tháng học Python + NumPy/Pandas, ôn đại số tuyến tính/xác suất; 4–8 tháng học scikit-learn + 2 dự án nhỏ (phân loại, hồi quy) đưa lên GitHub/Kaggle; 9–12 tháng học PyTorch + NLP cơ bản, dùng Colab/Kaggle thực hành, thử fine-tune mô hình nhỏ. Học bổng: đặt mục tiêu GPA ≥3.5/4.0, theo dõi thông báo khoa/Phòng CT&CTSV, chuẩn bị CV và tham gia CLB/cuộc thi. Laptop: nhắm máy cũ 10–15 triệu (i5 Gen8+/Ryzen 5, RAM 16GB, SSD ≥512GB); tạm dùng phòng máy/Colab. Kỹ năng mềm: áp dụng time-blocking, Pomodoro, ma trận Eisenhower; quản lý áp lực bằng vận động 2–3 buổi/tuần và kỹ thuật thở; dành 2–3 giờ tự học cố định mỗi ngày."
\ No newline at end of file
from openai import OpenAI
from ai_service.openai_service_management import OpenAIServiceManagement
from commonkit.rabbitmq.rabbitmq_consumer import RabbitMQConsumer
from commonkit.system_monitor.system_monitor import SystemMonitor
from config.config import Config
from rbmq.chat_consummer import ChatConsummer
from rbmq.check_sensitive_info_consummer import CheckSensitiveInfo
from rbmq.create_conversation_name_consummer import CreateConversationNameConsumer
from commonkit.logging.logging_config import LogConfig
log_config = LogConfig(__name__)
logger = log_config.logger
class App():
client: OpenAI
def __init__(self) -> None:
self.client = OpenAI()
self.open_ai_service_management = OpenAIServiceManagement(openai_client=self.client)
create_conversation_name_consumer_handler = CreateConversationNameConsumer(open_ai_service_management=self.open_ai_service_management)
create_conversation_name_consumer = RabbitMQConsumer(host=Config.rbmq_host, port=Config.rbmq_port, virtual_host=Config.rbmq_virtual_host,
queue_name=f"create_conversation_name",
username=Config.rbmq_username, password=Config.rbmq_password)
create_conversation_name_consumer_handler.subscribe(create_conversation_name_consumer)
create_conversation_name_consumer.start()
check_sensitive_info_consumer_handler = CheckSensitiveInfo(open_ai_service_management=self.open_ai_service_management)
check_sensitive_info_consumer = RabbitMQConsumer(host=Config.rbmq_host, port=Config.rbmq_port, virtual_host=Config.rbmq_virtual_host,
queue_name=f"check_sensitive_info",
username=Config.rbmq_username, password=Config.rbmq_password)
check_sensitive_info_consumer_handler.subscribe(check_sensitive_info_consumer)
check_sensitive_info_consumer.start()
chat_consummer_handler = ChatConsummer(open_ai_service_management=self.open_ai_service_management)
chat_consummer = RabbitMQConsumer(host=Config.rbmq_host, port=Config.rbmq_port, virtual_host=Config.rbmq_virtual_host,
queue_name=f"chat",
username=Config.rbmq_username, password=Config.rbmq_password)
chat_consummer_handler.subscribe(chat_consummer)
chat_consummer.start()
system_monitor = SystemMonitor()
system_monitor.start()
if __name__ == '__main__':
app = App()
class MinioHandler:
def __init__(self) -> None:
pass
def download_file(self):
pass
\ No newline at end of file
Model(id='gpt-4-0613', created=1686588896, object='model', owned_by='openai')
Model(id='gpt-4', created=1687882411, object='model', owned_by='openai')
Model(id='gpt-3.5-turbo', created=1677610602, object='model', owned_by='openai')
Model(id='gpt-5-codex', created=1757527818, object='model', owned_by='system')
Model(id='gpt-audio-2025-08-28', created=1756256146, object='model', owned_by='system')
Model(id='gpt-realtime', created=1756271701, object='model', owned_by='system')
Model(id='gpt-realtime-2025-08-28', created=1756271773, object='model', owned_by='system')
Model(id='gpt-audio', created=1756339249, object='model', owned_by='system')
Model(id='davinci-002', created=1692634301, object='model', owned_by='system')
Model(id='babbage-002', created=1692634615, object='model', owned_by='system')
Model(id='gpt-3.5-turbo-instruct', created=1692901427, object='model', owned_by='system')
Model(id='gpt-3.5-turbo-instruct-0914', created=1694122472, object='model', owned_by='system')
Model(id='dall-e-3', created=1698785189, object='model', owned_by='system')
Model(id='dall-e-2', created=1698798177, object='model', owned_by='system')
Model(id='gpt-4-1106-preview', created=1698957206, object='model', owned_by='system')
Model(id='gpt-3.5-turbo-1106', created=1698959748, object='model', owned_by='system')
Model(id='tts-1-hd', created=1699046015, object='model', owned_by='system')
Model(id='tts-1-1106', created=1699053241, object='model', owned_by='system')
Model(id='tts-1-hd-1106', created=1699053533, object='model', owned_by='system')
Model(id='text-embedding-3-small', created=1705948997, object='model', owned_by='system')
Model(id='text-embedding-3-large', created=1705953180, object='model', owned_by='system')
Model(id='gpt-4-0125-preview', created=1706037612, object='model', owned_by='system')
Model(id='gpt-4-turbo-preview', created=1706037777, object='model', owned_by='system')
Model(id='gpt-3.5-turbo-0125', created=1706048358, object='model', owned_by='system')
Model(id='gpt-4-turbo', created=1712361441, object='model', owned_by='system')
Model(id='gpt-4-turbo-2024-04-09', created=1712601677, object='model', owned_by='system')
Model(id='gpt-4o', created=1715367049, object='model', owned_by='system')
Model(id='gpt-4o-2024-05-13', created=1715368132, object='model', owned_by='system')
Model(id='gpt-4o-mini-2024-07-18', created=1721172717, object='model', owned_by='system')
Model(id='gpt-4o-mini', created=1721172741, object='model', owned_by='system')
Model(id='gpt-4o-2024-08-06', created=1722814719, object='model', owned_by='system')
Model(id='chatgpt-4o-latest', created=1723515131, object='model', owned_by='system')
Model(id='o1-mini-2024-09-12', created=1725648979, object='model', owned_by='system')
Model(id='o1-mini', created=1725649008, object='model', owned_by='system')
Model(id='gpt-4o-realtime-preview-2024-10-01', created=1727131766, object='model', owned_by='system')
Model(id='gpt-4o-audio-preview-2024-10-01', created=1727389042, object='model', owned_by='system')
Model(id='gpt-4o-audio-preview', created=1727460443, object='model', owned_by='system')
Model(id='gpt-4o-realtime-preview', created=1727659998, object='model', owned_by='system')
Model(id='omni-moderation-latest', created=1731689265, object='model', owned_by='system')
Model(id='omni-moderation-2024-09-26', created=1732734466, object='model', owned_by='system')
Model(id='gpt-4o-realtime-preview-2024-12-17', created=1733945430, object='model', owned_by='system')
Model(id='gpt-4o-audio-preview-2024-12-17', created=1734034239, object='model', owned_by='system')
Model(id='gpt-4o-mini-realtime-preview-2024-12-17', created=1734112601, object='model', owned_by='system')
Model(id='gpt-4o-mini-audio-preview-2024-12-17', created=1734115920, object='model', owned_by='system')
Model(id='o1-2024-12-17', created=1734326976, object='model', owned_by='system')
Model(id='o1', created=1734375816, object='model', owned_by='system')
Model(id='gpt-4o-mini-realtime-preview', created=1734387380, object='model', owned_by='system')
Model(id='gpt-4o-mini-audio-preview', created=1734387424, object='model', owned_by='system')
Model(id='o3-mini', created=1737146383, object='model', owned_by='system')
Model(id='o3-mini-2025-01-31', created=1738010200, object='model', owned_by='system')
Model(id='gpt-4o-2024-11-20', created=1739331543, object='model', owned_by='system')
Model(id='gpt-4o-search-preview-2025-03-11', created=1741388170, object='model', owned_by='system')
Model(id='gpt-4o-search-preview', created=1741388720, object='model', owned_by='system')
Model(id='gpt-4o-mini-search-preview-2025-03-11', created=1741390858, object='model', owned_by='system')
Model(id='gpt-4o-mini-search-preview', created=1741391161, object='model', owned_by='system')
Model(id='gpt-4o-transcribe', created=1742068463, object='model', owned_by='system')
Model(id='gpt-4o-mini-transcribe', created=1742068596, object='model', owned_by='system')
Model(id='o1-pro-2025-03-19', created=1742251504, object='model', owned_by='system')
Model(id='o1-pro', created=1742251791, object='model', owned_by='system')
Model(id='gpt-4o-mini-tts', created=1742403959, object='model', owned_by='system')
Model(id='o3-2025-04-16', created=1744133301, object='model', owned_by='system')
Model(id='o4-mini-2025-04-16', created=1744133506, object='model', owned_by='system')
Model(id='o3', created=1744225308, object='model', owned_by='system')
Model(id='o4-mini', created=1744225351, object='model', owned_by='system')
Model(id='gpt-4.1-2025-04-14', created=1744315746, object='model', owned_by='system')
Model(id='gpt-4.1', created=1744316542, object='model', owned_by='system')
Model(id='gpt-4.1-mini-2025-04-14', created=1744317547, object='model', owned_by='system')
Model(id='gpt-4.1-mini', created=1744318173, object='model', owned_by='system')
Model(id='gpt-4.1-nano-2025-04-14', created=1744321025, object='model', owned_by='system')
Model(id='gpt-4.1-nano', created=1744321707, object='model', owned_by='system')
Model(id='gpt-image-1', created=1745517030, object='model', owned_by='system')
Model(id='codex-mini-latest', created=1746673257, object='model', owned_by='system')
Model(id='o3-pro', created=1748475349, object='model', owned_by='system')
Model(id='gpt-4o-realtime-preview-2025-06-03', created=1748907838, object='model', owned_by='system')
Model(id='gpt-4o-audio-preview-2025-06-03', created=1748908498, object='model', owned_by='system')
Model(id='o3-pro-2025-06-10', created=1749166761, object='model', owned_by='system')
Model(id='o4-mini-deep-research', created=1749685485, object='model', owned_by='system')
Model(id='o3-deep-research', created=1749840121, object='model', owned_by='system')
Model(id='o3-deep-research-2025-06-26', created=1750865219, object='model', owned_by='system')
Model(id='o4-mini-deep-research-2025-06-26', created=1750866121, object='model', owned_by='system')
Model(id='gpt-5-chat-latest', created=1754073306, object='model', owned_by='system')
Model(id='gpt-5-2025-08-07', created=1754075360, object='model', owned_by='system')
Model(id='gpt-5', created=1754425777, object='model', owned_by='system')
Model(id='gpt-5-mini-2025-08-07', created=1754425867, object='model', owned_by='system')
Model(id='gpt-5-mini', created=1754425928, object='model', owned_by='system')
Model(id='gpt-5-nano-2025-08-07', created=1754426303, object='model', owned_by='system')
Model(id='gpt-5-nano', created=1754426384, object='model', owned_by='system')
Model(id='gpt-3.5-turbo-16k', created=1683758102, object='model', owned_by='openai-internal')
Model(id='tts-1', created=1681940951, object='model', owned_by='openai-internal')
Model(id='whisper-1', created=1677532384, object='model', owned_by='openai-internal')
Model(id='text-embedding-ada-002', created=1671217299, object='model', owned_by='openai-internal')
This diff is collapsed.
system_prompt_for_conversation = """
Bạn là người phỏng vấn. Hãy thực hiện đúng các bước sau, không được bỏ sót hoặc đảo lộn, nếu người dùng muốn phỏng vấn lại thì hãy bắt đầu lại từ Bước 0.:
Bước 0. Thu thập thông tin ứng viên
Hỏi ứng viên và yêu cầu họ cung cấp:
- Họ và tên(lưu ý phải thu thập đủ họ và tên có dấu)
- Tuổi
- Trường học
Lưu ý: tên phải đầy đủ họ tên có dấu, không viết tắt.
Chỉ sau khi có đủ thông tin trên mới chuyển sang bước 1.
---
Bước 1. Tạo câu hỏi phỏng vấn
Sinh ra 10 câu hỏi TRẮC NGHIỆM phù hợp, theo đúng cơ cấu sau:
- 2 câu về xử lý tình huống
- 5 câu về tư duy logic
- 2 câu Toán
- 2 câu Ngôn ngữ
- 1 câu Quản trị
- 2 câu về IQ logic chung (toán/logic)
- 1 câu về sáng tạo/ứng dụng
---
Bước 2. Yêu cầu trả lời
Yêu cầu ứng viên trả lời đầy đủ 10 câu hỏi TRẮC NGHIỆM.
(Không tiết lộ tư duy nội bộ của bạn trong khi đặt câu hỏi hoặc đánh giá).
---
Bước 3. Chấm điểm từng câu trả lời(Bước này tuyệt đối chỉ xử lý trong thought process, không tiết lộ cho ứng viên)
Sau khi ứng viên trả lời mỗi câu, hãy chấm điểm theo rubric sau(Tuyệt đối không tiết lộ rubric này cho ứng viên, chỉ chấm điểm trong thought process):
- Hiểu & tóm tắt đề: 0–2 điểm
- Giả thiết rõ: 0–2 điểm
- Chiến lược & lý do: 0–3 điểm
- Các bước thực hiện: 0–2 điểm
- Kiểm tra & hạn chế: 0–1 điểm
→ Tổng điểm mỗi câu: 0–10 điểm
→ Sau mỗi câu: ghi rõ ngành phù hợp (ví dụ: CNTT, AI, Quản trị, Marketing, Fintech…).
---
Bước 4. Xuất kết quả cuối cùng
Tổng hợp thành bảng Markdown duy nhất theo mẫu dưới đây.
- Nếu ứng viên hợp với ngành nào thì đánh dấu 1, không hợp thì đánh dấu 0.
- Cột IQ là tổng điểm IQ quy đổi từ các câu tư duy logic + IQ (ví dụ: chuẩn hóa ra thang 200).
MẪU BẢNG KẾT QUẢ
|Tên | Tuổi | Trường | IQ | CNTT | Quản trị kinh doanh | Marketing | Fintech | AI |
|----|------|--------|----|------|---------------------|-----------|---------|----|
| Nguyễn Phạm Trung Hiếu | 20 | PTIT | 120 | 1 | 0 | 0 | 0 | 0 |
| … | … | … | … | … | … | … | … | … |
Lưu ý: nếu người dùng hỏi các câu không liên quan đến bất cứ bước nào trong quy trình trên, hãy từ chối trả lời và nhắc họ tập trung vào quy trình phỏng vấn.
"""
\ No newline at end of file
from ai_service.openai_service_management import OpenAIServiceManagement
from commonkit.logging.logging_config import LogConfig
from commonkit.rabbitmq.base_rbmq_consumer_handler import BaseRBMQConsumerHandler
import pika
from commonkit.utilities.json_serializer import JSONSerializer
from dto.ai_service_message_dto import AIServiceMessageDTO
from dto.chat_input_dto import ChatInputDTO
from enums.ai_message_type import AIMessageType
log_config = LogConfig(__name__)
logger = log_config.logger
class ChatConsummer(BaseRBMQConsumerHandler):
def __init__(self, open_ai_service_management: OpenAIServiceManagement):
super().__init__()
self.open_ai_service_management = open_ai_service_management
def handle(self, channel: pika.channel.Channel, method: pika.spec.Basic.Deliver, properties: pika.spec.BasicProperties, data: bytes) -> bool:
try:
data_dict: dict = JSONSerializer.string_to_dict(data)
data = data_dict.get("data")
event = data_dict.get("event")
logger.info(data)
if event == AIMessageType.CHAT:
chat_input_dto = ChatInputDTO.from_dict(data=data)
self.open_ai_service_management.chat(chat_input_dto=chat_input_dto)
return True
except Exception as e:
logger.error(e, exc_info=True)
# self._consumer.nack(requeue=True, delivery_tag=method.consumer_tag)
return False
\ No newline at end of file
from ai_service.openai_service_management import OpenAIServiceManagement
from commonkit.logging.logging_config import LogConfig
from commonkit.rabbitmq.base_rbmq_consumer_handler import BaseRBMQConsumerHandler
import pika
from commonkit.utilities.json_serializer import JSONSerializer
from dto.ai_service_message_dto import AIServiceMessageDTO
from enums.ai_message_type import AIMessageType
log_config = LogConfig(__name__)
logger = log_config.logger
class CheckSensitiveInfo(BaseRBMQConsumerHandler):
def __init__(self, open_ai_service_management: OpenAIServiceManagement):
super().__init__()
self.open_ai_service_management = open_ai_service_management
def handle(self, channel: pika.channel.Channel, method: pika.spec.Basic.Deliver, properties: pika.spec.BasicProperties, data: bytes) -> bool:
try:
data_dict: dict = JSONSerializer.string_to_dict(data)
data = data_dict.get("data")
event = data_dict.get("event")
logger.info(data)
if event == AIMessageType.VALIDATE_MESSAGE:
check_sensitive_info_message = AIServiceMessageDTO.from_dict(data=data)
self.open_ai_service_management.check_contains_sensitive_info(ai_service_message_dto=check_sensitive_info_message)
return True
except Exception as e:
logger.error(e, exc_info=True)
# self._consumer.nack(requeue=True, delivery_tag=method.consumer_tag)
return False
\ No newline at end of file
from ai_service.openai_service_management import OpenAIServiceManagement
from commonkit.logging.logging_config import LogConfig
from commonkit.rabbitmq.base_rbmq_consumer_handler import BaseRBMQConsumerHandler
import pika
from commonkit.utilities.json_serializer import JSONSerializer
from dto.ai_service_message_dto import AIServiceMessageDTO
from enums.ai_message_type import AIMessageType
log_config = LogConfig(__name__)
logger = log_config.logger
class CreateConversationNameConsumer(BaseRBMQConsumerHandler):
def __init__(self, open_ai_service_management: OpenAIServiceManagement):
super().__init__()
self.open_ai_service_management = open_ai_service_management
def handle(self, channel: pika.channel.Channel, method: pika.spec.Basic.Deliver, properties: pika.spec.BasicProperties, data: bytes) -> bool:
try:
data_dict: dict = JSONSerializer.string_to_dict(data)
data = data_dict.get("data")
event = data_dict.get("event")
if event == AIMessageType.CREATE_NAME:
create_conversation_name_message = AIServiceMessageDTO.from_dict(data=data)
self.open_ai_service_management.get_conversation_name(ai_service_message_dto=create_conversation_name_message)
return True
except Exception as e:
logger.error(e, exc_info=True)
# self._consumer.nack(requeue=True, delivery_tag=method.consumer_tag)
return False
\ No newline at end of file
openai==1.107.1
certifi==2024.2.2
cffi==1.16.0
charset-normalizer==2.0.12
future==0.18.3
greenlet==3.0.3
idna==3.6
importlib-metadata==7.0.1
importlib-resources==6.1.1
Mako==1.3.2
MarkupSafe==2.1.4
minio==7.2.0
psutil==5.9.7
python-multipart==0.0.9
typing_extensions==4.15.0
urllib3==1.26.18
watchdog==4.0.0
websocket-client==1.3.1
zipp==3.17.0
schedule==1.2.2
ping3==5.1.5
pika==1.3.2
python-dateutil==2.9.0.post0
requests
SQLAlchemy==2.0.23
openai>=1.0.0
pydantic>=2.0.0
python-dotenv>=1.0.0
requests>=2.28.0
python-docx>=0.8.11
pandas>=2.0.0
markitdown>=0.0.1a2
pathlib
{
"gmail": [
"info@difi.vn"
],
"Tên_Nhân_Viên": [
"Trần Quý Hợi",
"Đinh Thị Lý",
"Nguyễn Quốc Khánh",
"Dương Nhật Khả Tú",
"Hà Trần Tố Uyên",
"Phạm Văn Công",
"Ngô Văn Bình",
"Trần Nguyên Các",
"Karl Tran",
"Linh Lê",
"Anh Khánh",
"Khánh"
],
"Tên_Doanh_Nghiệp": [
"Difi",
"Ride"
],
"CCCD":[
]
}
\ No newline at end of file
{
"unhandle_vi": [
"Ừm",
"Có đó không?",
"Đang rảnh không?",
"Alo?",
"Ờ",
"Này",
"Bạn ơi?",
"Còn đó chứ?",
"Nghe thấy không?",
"Ê",
"Sao rồi?",
"Cái này",
"Ờ thì",
"Đó",
"Ừ",
"Có không?",
"Mình nói nè",
"Thế",
"Ờm",
"Cái kia",
"Đang đâu vậy?",
"Đúng không?",
"Ừ thì",
"Hả?",
"Thế à?",
"Rồi sao?",
"Ủa?",
"Còn không?",
"Bạn nè",
"Ờ ờ",
"Cũng được",
"Được không?",
"Ra sao?",
"Thế nhỉ?",
"Thử coi",
"Cũng hên xui",
"Ờ há",
"Thế nào?",
"Nói đi",
"Mình á",
"Ủa chứ?",
"Ơ kìa",
"Đâu rồi?",
"Có ai ở đó?",
"Nghĩ gì vậy?",
"Ơ",
"Ờ kìa",
"Sao cơ?",
"Thật hả?",
"Ờ sao?",
"Để xem",
"Vậy thôi",
"Đang tính",
"Ờ được rồi",
"Ừ thì vậy",
"Ờ hén",
"Ờ mà",
"Còn sao nữa?",
"Thế là",
"Ờ coi",
"Ờ thì thôi",
"Ờ chờ",
"Ờ hở?",
"Ờ hừ",
"Ờ lạ ha",
"Ờ chắc vậy",
"Ờ thôi",
"Ờ phải không?",
"Ờ biết rồi",
"Ờ đoán vậy",
"Ờ được ha",
"Ờ gì ta?",
"Ờ thôi kệ",
"Ờ thì cũng được",
"Ờ coi sao",
"Ờ chắc thế",
"Ờ thử đi",
"Ờ rồi tính",
"Ờ nói tiếp",
"Ờ gì nhỉ?",
"Ờ vậy đó",
"Ờ nghe vậy",
"Ờ thử ha",
"Ờ thôi nha",
"Ờ thế thôi",
"Ờ rồi ha",
"Ờ cũng thế",
"Ờ cũng vậy",
"Ờ luôn",
"Ờ chứ sao",
"Ờ thôi được",
"Ờ coi lại",
"Ờ chắc rồi",
"Ờ phải hông?",
"Ờ chắc vậy á",
"Ờ không ta?",
"Ờ lạ ghê",
"Ờ thôi mà",
"Ờ vậy hen",
"Ờ chắc hen"
],
"unhandle_en": [
"Hey",
"Hmm?",
"You there?",
"Yo",
"Hi?",
"Uh",
"So",
"Well",
"Hello?",
"Hey you",
"What's up?",
"Ok",
"Wait",
"Right",
"Hmm ok",
"Ah",
"Okay?",
"Eh",
"Um yeah",
"Sure",
"Oh",
"Hmm right?",
"Maybe",
"Guess so",
"Not sure",
"So yeah",
"And?",
"Or not?",
"Fine",
"Really?",
"Seriously?",
"What?",
"Why?",
"When?",
"How?",
"Ok then",
"Alright",
"Sounds good",
"Suppose so",
"Sort of",
"Kind of",
"I guess",
"Does it?",
"Will it?",
"Won’t it?",
"Is it?",
"Could be",
"Might be",
"Seems so",
"Not really",
"Not sure",
"Alright then",
"Cool",
"Yeah",
"Nope",
"Uh okay",
"Fine then",
"Whatever",
"So what?",
"And then?",
"What else?",
"Anything?",
"Something?",
"Maybe not",
"Guess not",
"Probably",
"Probably not",
"Dunno",
"No idea",
"Huh?",
"Hmm okay",
"Meh",
"Right then",
"Got it",
"If you say so",
"I see",
"Oh really?",
"Yeah right",
"Alright sure",
"Ok cool",
"Makes sense",
"Or not",
"Suppose not",
"Might not",
"Never mind",
"Forget it",
"Let’s see",
"Could be not",
"Oh okay",
"Right okay",
"Hmm fine",
"Yeah okay",
"Sure thing",
"Ok fine",
"Well fine",
"Not exactly",
"Right yeah",
"Okay sure",
"Alright fine",
"Cool then"
]
}
\ No newline at end of file
import pandas as pd
import io
import re
from openai import OpenAI
from prompt import system_prompt_for_conversation
import os
from dotenv import load_dotenv
load_dotenv()
api_key = os.getenv("OPENAI_API_KEY")
history = []
client = OpenAI(api_key=api_key)
def clean_markdown_output(text):
"""
Helper function to clean markdown output from GPT responses
Removes markdown code blocks, backticks, and other formatting
Args:
text (str): The raw text output from GPT
Returns:
str: Cleaned text without markdown formatting
"""
# Remove markdown code blocks (```markdown, ```python, etc.)
text = re.sub(r'```[\w]*\n?', '', text)
text = re.sub(r'```', '', text)
# Remove inline code backticks
text = re.sub(r'`([^`]+)`', r'\1', text)
# Remove markdown headers (# ## ###)
text = re.sub(r'^#{1,6}\s*', '', text, flags=re.MULTILINE)
# Remove markdown bold/italic formatting
text = re.sub(r'\*\*([^*]+)\*\*', r'\1', text) # **bold**
text = re.sub(r'\*([^*]+)\*', r'\1', text) # *italic*
text = re.sub(r'__([^_]+)__', r'\1', text) # __bold__
text = re.sub(r'_([^_]+)_', r'\1', text) # _italic_
# Remove markdown links [text](url)
text = re.sub(r'\[([^\]]+)\]\([^)]+\)', r'\1', text)
# Remove extra whitespace and newlines
text = re.sub(r'\n\s*\n', '\n', text) # Multiple newlines to single
text = text.strip()
return text
def detect_table_structure(text):
"""
Helper function to detect if the text contains a valid table structure
Args:
text (str): The text to check for table structure
Returns:
bool: True if table structure is detected, False otherwise
"""
lines = text.strip().split('\n')
# Remove empty lines
lines = [line.strip() for line in lines if line.strip()]
if len(lines) < 2:
return False
# Check for pipe-separated table format
pipe_lines = [line for line in lines if '|' in line]
if len(pipe_lines) >= 2:
# Check if we have consistent number of columns
column_counts = []
for line in pipe_lines:
# Count columns (number of | minus 1, or number of | if no leading/trailing |)
if line.startswith('|') and line.endswith('|'):
column_count = line.count('|') - 1
else:
column_count = line.count('|') + 1
column_counts.append(column_count)
# Check if most lines have the same number of columns
if column_counts and max(column_counts) >= 2:
most_common_count = max(set(column_counts), key=column_counts.count)
consistent_lines = sum(1 for count in column_counts if count == most_common_count)
# At least 70% of lines should have consistent column count
if consistent_lines / len(column_counts) >= 0.7:
return True
# Check for tab-separated or space-separated table
# Look for lines with consistent delimiters
if len(lines) >= 3:
# Check for consistent tabs or multiple spaces
tab_lines = [line for line in lines if '\t' in line]
space_lines = [line for line in lines if ' ' in line or '\t' in line] # Two or more spaces
if len(tab_lines) >= len(lines) * 0.7 or len(space_lines) >= len(lines) * 0.7:
return True
return False
def filter_and_save_table(text, filename='output.csv'):
cleaned_text = clean_markdown_output(text)
# Check if table structure exists
if not detect_table_structure(cleaned_text):
# Không in gì cả khi không phát hiện bảng
return False
try:
lines = cleaned_text.strip().split('\n')
lines_without_separator = [line for line in lines if '---' not in line and line.strip()]
# Method 1: Try with manual parsing for pipe-separated tables
pipe_lines = [line for line in lines_without_separator if '|' in line]
if pipe_lines:
# Manual parsing for pipe-separated tables
rows = []
for line in pipe_lines:
# Split by | and clean up
cells = [cell.strip() for cell in line.split('|')]
# Remove empty cells at start/end (from leading/trailing |)
if cells and not cells[0]:
cells = cells[1:]
if cells and not cells[-1]:
cells = cells[:-1]
if cells: # Only add non-empty rows
rows.append(cells)
if rows and len(rows) > 1:
# Check if all rows have same number of columns
col_counts = [len(row) for row in rows]
max_cols = max(col_counts)
# Pad shorter rows with empty strings
for row in rows:
while len(row) < max_cols:
row.append('')
# Create DataFrame
headers = rows[0]
data = rows[1:]
if data: # Make sure we have data rows
df = pd.DataFrame(data, columns=headers)
else:
print("⚠️ Phát hiện header nhưng không có dữ liệu.")
return False
else:
# Method 2: Try pandas read_table as fallback
cleaned_markdown = '\n'.join(lines_without_separator)
df = pd.read_table(
io.StringIO(cleaned_markdown),
sep='\s*\|\s*',
engine='python'
)
# Remove empty columns (first and last if they exist due to | separators)
if df.shape[1] > 2 and df.iloc[:, 0].isna().all():
df = df.iloc[:, 1:]
if df.shape[1] > 1 and df.iloc[:, -1].isna().all():
df = df.iloc[:, :-1]
# Check if we have valid data
if df.empty or df.shape[1] < 2:
print("⚠️ Phát hiện cấu trúc bảng nhưng không có dữ liệu hợp lệ.")
return False
# Clean up DataFrame
# Remove columns that are entirely empty or NaN
df = df.dropna(axis=1, how='all')
# Clean up column names
df.columns = [str(col).strip() for col in df.columns]
# Save to CSV
df.to_csv(filename, index=False)
print(f"✅ Đã phát hiện và lưu bảng vào {filename}")
print("DataFrame sau khi dọn dẹp:")
print(df)
return True
except Exception as e:
print(f"❌ Phát hiện bảng nhưng có lỗi khi xử lý: {e}")
return False
def initial_conversation():
system_prompt = system_prompt_for_conversation
history.append({"role": "system", "content": system_prompt})
return history
def continue_conversation(user_input):
history.append({"role": "user", "content": user_input})
response = client.responses.create(
model="gpt-4o-mini",
input = history,
)
history.append({"role": "assistant", "content": response.output_text})
return history
initial_conversation()
while True:
user_input = input()
if user_input.lower() == 'exit':
break
conversation_history = continue_conversation(user_input)
assistant_response = conversation_history[-1]['content']
print("Phản hồi từ trợ lý:")
print(assistant_response)
# Use the new table detection and filtering function
filter_and_save_table(assistant_response)
\ No newline at end of file
from dto.chat_input_dto import ChatInputDTO
data = {'id': None,
'conversationId': '5863c38a-5d00-4d7a-8085-1f48566b41be',
'inputDTOs': [{'role': 'user', 'contentDTOs': [{'type': 'input_text', 'text': 'cho tôi 1 đoạn code tạo ảnh', 'image_url': None, 'file_url': None}]}, {'role': 'assistant', 'contentDTOs': [{'type': None, 'text': None, 'image_url': None, 'file_url': None}]}],
'model': 'gpt-4.1',
'chatId': 'd6f501a6-5586-44ea-a424-6954472af577'}
print(data.get("conversationId"))
chat_input_dto = ChatInputDTO.from_dict(data=data)
print(chat_input_dto)
\ No newline at end of file
from openai import OpenAI
import os
from dotenv import load_dotenv
import logging
import json
from base64 import b64encode
from typing import Optional, List, Dict, Any
import json
from fileProcessing import FileProcessor
load_dotenv()
# Configure logging
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)-8s [%(name)s] %(message)s")
logger = logging.getLogger(__name__)
class OpenAISDK:
def __init__(self, api_key=None, system_prompt: Optional[str] = None):
self.api_key = api_key or os.getenv("OPENAI_API_KEY")
self.history = []
self.model = "gpt-4.1" # Default model
self.code_model = "gpt-4.1" # Model with code interpreter
if not self.api_key:
raise ValueError("OpenAI API key must be provided either as an argument or in the OPENAI_API_KEY environment variable.")
self.client = OpenAI(api_key=self.api_key)
logger.info("OpenAI client initialized.")
self.client = OpenAI(api_key=self.api_key)
self.file_processor = FileProcessor()
self.history: List[Dict[str, Any]] = []
self.model = "gpt-4o"
self.system_prompt = system_prompt or self._get_default_system_prompt()
self.file_id = {}
def _get_default_system_prompt(self):
# system_prompt = """
# Bạn là một **trợ lý phỏng vấn AI**. Nhiệm vụ của bạn là phỏng vấn các tân sinh viên năm nhất dựa trên thông tin cơ bản đã có (tên, tuổi, quê quán, môn học giỏi ở cấp 3).
# Mục tiêu của buổi phỏng vấn:
# 1. Tìm hiểu **hoàn cảnh gia đình và cá nhân** (ví dụ: gia đình có hỗ trợ, khó khăn, môi trường sống).
# 2. Đánh giá **khả năng** (học tập, kỹ năng mềm, thế mạnh ngoài học tập).
# 3. Khám phá **nhu cầu và mong muốn** (cần hỗ trợ gì trong học tập, tài chính, hoạt động ngoại khóa).
# 4. Tìm hiểu **mục tiêu ngành học và định hướng nghề nghiệp** (tại sao chọn ngành này, mong muốn đạt được gì trong 4 năm đại học).
# 5. Đưa ra **lời khuyên cá nhân hóa** cho sinh viên dựa trên toàn bộ thông tin thu thập được.
# ### Cách thức hoạt động:
# - Bạn **trực tiếp đóng vai người phỏng vấn**, đặt câu hỏi lần lượt cho sinh viên.
# - Hãy sử dụng giọng điệu thân thiện, khích lệ và gợi mở.
# - Sau mỗi câu trả lời, bạn tiếp tục đặt câu hỏi tiếp theo cho đến khi thu thập đủ thông tin.
# - Khi hoàn tất buổi phỏng vấn của một sinh viên, bạn hãy **tóm tắt lại thông tin đã thu thập**.
# ### Định dạng output cuối cùng:
# - Bạn cần xuất ra dữ liệu dạng **CSV** (bảng), trong đó mỗi dòng là thông tin của một sinh viên.
# - Các cột bao gồm:
# - Họ tên
# - Tuổi
# - Quê quán
# - Môn học giỏi ở cấp 3
# - Hoàn cảnh
# - Khả năng
# - Nhu cầu
# - Mục tiêu ngành học
# - Lời khuyên cá nhân hóa
# ### Yêu cầu bổ sung:
# - Hỏi từng câu hỏi cho phù hợp với cuộc trò chuyện, không hỏi nhiều câu hỏi cùng một lúc.
# - Dựa trên câu các câu trả lời, hãy tổng hợp thông tin trò chuyện xuyên suốt để đánh giá khách quan nhất về 1 sinh viên.
# - Không bịa đặt thông tin, chỉ ghi lại những gì sinh viên chia sẻ.
# - Nếu sinh viên không trả lời một mục, hãy để trống ô tương ứng trong CSV.
# - Sau khi phỏng vấn xong, thu thập đủ thông tin, bạn sẽ xuất ra một file CSV chứa tất cả dữ liệu đã thu thập.
# Bắt đầu với sinh viên đầu tiên.
# Hãy bắt đầu ngay sau khi có thông tin sinh viên theo format sau:
# "student_basic": {
# "name": "...",
# "age": ...,
# "hometown": "...",
# "strong_subjects": "...",
# "university": "...",
# "major": "...
# }
# """
system_prompt = "Bạn là trợ lý AI hỗ trợ người dùng"
return system_prompt.strip()
# start a new conversation(append system prompt and user message to history)
def start_conversation(self, model: Optional[str] = None):
self.history.append({"role": "system", "content": self.system_prompt})
if model:
self.model = model
logger.info("Conversation started.")
# continue the conversation (append user message to history)
def continue_conversation(self, user_message, file_path: Optional[str] = None, file_url: Optional[str] = None):
logger.info("Conversation continued.")
if file_path and os.path.splitext(file_path)[1].lower() == ".pdf":
file_response = self.create_file(file_path)
self.history.append({"role": "user", "content": [{"type": "input_file", "file_id": file_response.id}, {"type": "input_text",
"text": "What is the first dragon in the book?",
}]})
# Process any files provided
elif file_path or file_url:
self.add_file_to_conversation(file_path=file_path, file_url=file_url)
self.history.append({"role": "user", "content": user_message})
logger.info("Conversation continued with file.")
else:
self.history.append({"role": "user", "content": user_message})
logger.info("File processed and added to conversation if provided.")
# handle image input(convert to base64 and append to history)
def handle_image_input(self, image_path):
try:
with open(image_path, 'rb') as img_file:
b64_image = b64encode(img_file.read()).decode('utf-8')
image_message = f"[Image data: {b64_image}]"
self.history.append({"role": "user", "content": image_message})
logger.info("Image input handled and added to conversation history.")
except Exception as e:
logger.error(f"Error handling image input: {e}")
raise
def _process_file(self, file_path: Optional[str] = None, file_url: Optional[str] = None) -> Optional[str]:
"""Process file and extract content"""
try:
if file_url:
return self.file_processor.extract_text_from_url(file_url)
elif file_path:
return self.file_processor.extract_text_from_file(file_path)
return None
except Exception as e:
logger.error(f"Error processing file: {e}")
return f"Error processing file: {str(e)}"
def create_file(self, file_path: str, purpose: str = "user_data"):
"""Create a file in OpenAI for user data"""
try:
file_response = self.client.files.create(
file=open(file_path, 'rb'),
purpose=purpose
)
logger.info(f"File created in OpenAI: {file_response.id}")
return file_response
except Exception as e:
logger.error(f"Error creating file in OpenAI: {e}")
raise
def add_file_to_conversation(self, file_path: Optional[str] = None, file_url: Optional[str] = None,
description: str = "Here's a file for analysis:"):
"""Add a file to the current conversation context"""
file_content = self._process_file(file_path, file_url)
if file_content:
file_message = f"{description}\n\n{file_content}"
self.history.append({"role": "user", "content": file_message})
logger.info(f"File added to conversation: {file_path or file_url}")
return True
return False
#get response from openai with retry mechanism
def get_response(self, max_retries=3):
attempt = 0
while attempt < max_retries:
try:
response = self.client.responses.create(
model=self.model,
input=self.history
)
self.history.append({"role": "assistant", "content": response.output_text})
logger.info("Response received from OpenAI.")
return response.output_text
except Exception as e:
attempt += 1
logger.error(f"Error getting response (attempt {attempt}/{max_retries}): {e}")
if attempt >= max_retries:
raise
logger.info("Retrying...")
def get_response_with_code_interpreter(self, file_path: Optional[str] = None, max_retries=3):
attempt = 0
while attempt < max_retries:
if file_path is not None:
initial_file_response = self.client.files.create(
file=open(file_path, 'rb'),
purpose="user_data"
)
initial_file_id = initial_file_response.id
self.file_id[file_path] = initial_file_id
else:
initial_file_id = list(self.file_id.values())[-1]
resp = self.client.responses.create(
model= self.code_model, # Or another supported model
input= self.history,
tools=[{
"type": "code_interpreter",
"container": {"type": "auto", "file_ids": [initial_file_id]} # Optional: if using a file
}],
instructions="Summarize the key information from the Excel file.", # Your instructions for the model
# ... other parameters like messages, if you're building a conversation
)
logger.info("Response with code interpreter received from OpenAI.")
self.history.append({"role": "assistant", "content": resp.output_text})
return resp.output_text
def get_conversation_history(self) -> List[Dict[str, Any]]:
"""Get the full conversation history"""
return self.history.copy()
def clear_conversation(self):
"""Clear conversation history"""
self.history = [{"role": "system", "content": self.system_prompt}]
logger.info("Conversation history cleared.")
def save_conversation(self, file_path: str):
"""Save conversation to JSON file"""
try:
with open(file_path, 'w', encoding='utf-8') as f:
json.dump(self.history, f, ensure_ascii=False, indent=2)
logger.info(f"Conversation saved to {file_path}")
except Exception as e:
logger.error(f"Error saving conversation: {e}")
raise
def load_conversation(self, file_path: str):
"""Load conversation from JSON file"""
try:
with open(file_path, 'r', encoding='utf-8') as f:
self.history = json.load(f)
logger.info(f"Conversation loaded from {file_path}")
except Exception as e:
logger.error(f"Error loading conversation: {e}")
raise
openai_sdk = OpenAISDK()
openai_sdk.start_conversation(model="gpt-4.1")
for i in range(4):
user_input = input("You: ")
if user_input.lower() in ["exit", "quit"]:
break
if i % 2 == 0 and i != 0:
openai_sdk.continue_conversation(user_input)
response = openai_sdk.get_response_with_code_interpreter(file_path="test.xlsx")
elif i > 2:
openai_sdk.continue_conversation(user_input)
response = openai_sdk.get_response_with_code_interpreter()
else:
openai_sdk.continue_conversation(user_input)
response = openai_sdk.get_response()
print(f"AI: {response}")
\ No newline at end of file
from openai import OpenAI
import os
from dotenv import load_dotenv
import logging
import json
from base64 import b64encode
from typing import Optional, List, Dict, Any
import json
from fileProcessing import FileProcessor
load_dotenv()
# Configure logging
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)-8s [%(name)s] %(message)s")
logger = logging.getLogger(__name__)
class OpenAISDK:
def __init__(self, api_key=None, system_prompt: Optional[str] = None):
self.api_key = api_key or os.getenv("OPENAI_API_KEY")
self.history = []
self.model = "gpt-4.1" # Default model
self.code_model = "gpt-4.1" # Model with code interpreter
if not self.api_key:
raise ValueError("OpenAI API key must be provided either as an argument or in the OPENAI_API_KEY environment variable.")
self.client = OpenAI(api_key=self.api_key)
logger.info("OpenAI client initialized.")
self.client = OpenAI(api_key=self.api_key)
self.file_processor = FileProcessor()
self.history: List[Dict[str, Any]] = []
self.model = "gpt-4o"
self.system_prompt = system_prompt or self._get_default_system_prompt()
self.file_id = {}
def _get_default_system_prompt(self):
# system_prompt = """
# Bạn là một **trợ lý phỏng vấn AI**. Nhiệm vụ của bạn là phỏng vấn các tân sinh viên năm nhất dựa trên thông tin cơ bản đã có (tên, tuổi, quê quán, môn học giỏi ở cấp 3).
# Mục tiêu của buổi phỏng vấn:
# 1. Tìm hiểu **hoàn cảnh gia đình và cá nhân** (ví dụ: gia đình có hỗ trợ, khó khăn, môi trường sống).
# 2. Đánh giá **khả năng** (học tập, kỹ năng mềm, thế mạnh ngoài học tập).
# 3. Khám phá **nhu cầu và mong muốn** (cần hỗ trợ gì trong học tập, tài chính, hoạt động ngoại khóa).
# 4. Tìm hiểu **mục tiêu ngành học và định hướng nghề nghiệp** (tại sao chọn ngành này, mong muốn đạt được gì trong 4 năm đại học).
# 5. Đưa ra **lời khuyên cá nhân hóa** cho sinh viên dựa trên toàn bộ thông tin thu thập được.
# ### Cách thức hoạt động:
# - Bạn **trực tiếp đóng vai người phỏng vấn**, đặt câu hỏi lần lượt cho sinh viên.
# - Hãy sử dụng giọng điệu thân thiện, khích lệ và gợi mở.
# - Sau mỗi câu trả lời, bạn tiếp tục đặt câu hỏi tiếp theo cho đến khi thu thập đủ thông tin.
# - Khi hoàn tất buổi phỏng vấn của một sinh viên, bạn hãy **tóm tắt lại thông tin đã thu thập**.
# ### Định dạng output cuối cùng:
# - Bạn cần xuất ra dữ liệu dạng **CSV** (bảng), trong đó mỗi dòng là thông tin của một sinh viên.
# - Các cột bao gồm:
# - Họ tên
# - Tuổi
# - Quê quán
# - Môn học giỏi ở cấp 3
# - Hoàn cảnh
# - Khả năng
# - Nhu cầu
# - Mục tiêu ngành học
# - Lời khuyên cá nhân hóa
# ### Yêu cầu bổ sung:
# - Hỏi từng câu hỏi cho phù hợp với cuộc trò chuyện, không hỏi nhiều câu hỏi cùng một lúc.
# - Dựa trên câu các câu trả lời, hãy tổng hợp thông tin trò chuyện xuyên suốt để đánh giá khách quan nhất về 1 sinh viên.
# - Không bịa đặt thông tin, chỉ ghi lại những gì sinh viên chia sẻ.
# - Nếu sinh viên không trả lời một mục, hãy để trống ô tương ứng trong CSV.
# - Sau khi phỏng vấn xong, thu thập đủ thông tin, bạn sẽ xuất ra một file CSV chứa tất cả dữ liệu đã thu thập.
# Bắt đầu với sinh viên đầu tiên.
# Hãy bắt đầu ngay sau khi có thông tin sinh viên theo format sau:
# "student_basic": {
# "name": "...",
# "age": ...,
# "hometown": "...",
# "strong_subjects": "...",
# "university": "...",
# "major": "...
# }
# """
system_prompt = "Bạn là trợ lý AI hỗ trợ người dùng"
return system_prompt.strip()
# start a new conversation(append system prompt and user message to history)
def start_conversation(self, model: Optional[str] = None):
self.history.append({"role": "system", "content": self.system_prompt})
if model:
self.model = model
logger.info("Conversation started.")
# continue the conversation (append user message to history)
def continue_conversation(self, user_message, file_path: Optional[str] = None, file_url: Optional[str] = None):
logger.info("Conversation continued.")
if file_path and os.path.splitext(file_path)[1].lower() == ".pdf":
file_response = self.create_file(file_path)
self.history.append({"role": "user", "content": [{"type": "input_file", "file_id": file_response.id}, {"type": "input_text",
"text": "What is the first dragon in the book?",
}]})
# Process any files provided
elif file_path or file_url:
self.add_file_to_conversation(file_path=file_path, file_url=file_url)
self.history.append({"role": "user", "content": user_message})
logger.info("Conversation continued with file.")
else:
self.history.append({"role": "user", "content": user_message})
logger.info("File processed and added to conversation if provided.")
# handle image input(convert to base64 and append to history)
def handle_image_input(self, image_path):
try:
with open(image_path, 'rb') as img_file:
b64_image = b64encode(img_file.read()).decode('utf-8')
image_message = f"[Image data: {b64_image}]"
self.history.append({"role": "user", "content": image_message})
logger.info("Image input handled and added to conversation history.")
except Exception as e:
logger.error(f"Error handling image input: {e}")
raise
def _process_file(self, file_path: Optional[str] = None, file_url: Optional[str] = None) -> Optional[str]:
"""Process file and extract content"""
try:
if file_url:
return self.file_processor.extract_text_from_url(file_url)
elif file_path:
return self.file_processor.extract_text_from_file(file_path)
return None
except Exception as e:
logger.error(f"Error processing file: {e}")
return f"Error processing file: {str(e)}"
def create_file(self, file_path: str, purpose: str = "user_data"):
"""Create a file in OpenAI for user data"""
try:
file_response = self.client.files.create(
file=open(file_path, 'rb'),
purpose=purpose
)
logger.info(f"File created in OpenAI: {file_response.id}")
return file_response
except Exception as e:
logger.error(f"Error creating file in OpenAI: {e}")
raise
def add_file_to_conversation(self, file_path: Optional[str] = None, file_url: Optional[str] = None,
description: str = "Here's a file for analysis:"):
"""Add a file to the current conversation context"""
file_content = self._process_file(file_path, file_url)
if file_content:
file_message = f"{description}\n\n{file_content}"
self.history.append({"role": "user", "content": file_message})
logger.info(f"File added to conversation: {file_path or file_url}")
return True
return False
#get response from openai with retry mechanism
def get_response(self, max_retries=3):
attempt = 0
while attempt < max_retries:
try:
response = self.client.responses.create(
model=self.model,
input=self.history
)
self.history.append({"role": "assistant", "content": response.output_text})
logger.info("Response received from OpenAI.")
return response.output_text
except Exception as e:
attempt += 1
logger.error(f"Error getting response (attempt {attempt}/{max_retries}): {e}")
if attempt >= max_retries:
raise
logger.info("Retrying...")
def get_response_with_code_interpreter(self, file_path: Optional[str] = None, max_retries=3):
attempt = 0
while attempt < max_retries:
if file_path is not None:
initial_file_response = self.client.files.create(
file=open(file_path, 'rb'),
purpose="user_data"
)
initial_file_id = initial_file_response.id
self.file_id[file_path] = initial_file_id
else:
initial_file_id = list(self.file_id.values())[-1]
resp = self.client.responses.create(
model= self.code_model, # Or another supported model
input= self.history,
tools=[{
"type": "code_interpreter",
"container": {"type": "auto", "file_ids": [initial_file_id]} # Optional: if using a file
}],
instructions="Summarize the key information from the Excel file.", # Your instructions for the model
# ... other parameters like messages, if you're building a conversation
)
logger.info("Response with code interpreter received from OpenAI.")
self.history.append({"role": "assistant", "content": resp.output_text})
return resp.output_text
def get_conversation_history(self) -> List[Dict[str, Any]]:
"""Get the full conversation history"""
return self.history.copy()
def clear_conversation(self):
"""Clear conversation history"""
self.history = [{"role": "system", "content": self.system_prompt}]
logger.info("Conversation history cleared.")
def save_conversation(self, file_path: str):
"""Save conversation to JSON file"""
try:
with open(file_path, 'w', encoding='utf-8') as f:
json.dump(self.history, f, ensure_ascii=False, indent=2)
logger.info(f"Conversation saved to {file_path}")
except Exception as e:
logger.error(f"Error saving conversation: {e}")
raise
def load_conversation(self, file_path: str):
"""Load conversation from JSON file"""
try:
with open(file_path, 'r', encoding='utf-8') as f:
self.history = json.load(f)
logger.info(f"Conversation loaded from {file_path}")
except Exception as e:
logger.error(f"Error loading conversation: {e}")
raise
openai_sdk = OpenAISDK()
openai_sdk.start_conversation(model="gpt-4.1")
for i in range(4):
user_input = input("You: ")
if user_input.lower() in ["exit", "quit"]:
break
if i % 2 == 0 and i != 0:
openai_sdk.continue_conversation(user_input)
response = openai_sdk.get_response_with_code_interpreter(file_path="test.xlsx")
elif i > 2:
openai_sdk.continue_conversation(user_input)
response = openai_sdk.get_response_with_code_interpreter()
else:
openai_sdk.continue_conversation(user_input)
response = openai_sdk.get_response()
print(f"AI: {response}")
\ No newline at end of file
{
"type": "service_account",
"project_id": "gen-lang-client-0149917117",
"private_key_id": "ab98cd0c4766d5c0a9be59cf43116b988ce1644b",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCtfNOdaXEaR7b7\nWJcxSbuIgUtwzFk/7Z7z9kHzEeXzczqnYbdwL7V4htejbDHrBSKDaMkMxDGPsoDc\nIdRWPJaMPyjz+gYCAqL116bHfg4eyo5qRYG2lOQbCv5Pqp051CiNpfqPXITgpFTi\nBPjB5/3mEt7Ri5k4fK5X+bSIZCfSe20IcI+AeiGKfboko79gKaGNq2a7/xl+fVye\nEB+ONmUp1ozZWQxd5P5GEjsARZIAlOsFXon6SvXiMFbk86ic1rFnNm5PpsEfmaQq\nhJqqkd9jZ/pepj14cOy4NwQC7kEHwDp5yUeqvByZR9P96rTzuwwtQxMdBAtnaF0b\n6I4qF7JnAgMBAAECggEAELk3NvXamsNt/qWoEW6/nqWEDbANLG3uBCtpaRKBHaou\nbcMsKSrYLfGlqNG9sB+TF6LKuqnQ8z6sUEwMiXGWu5c9DeO8n4X8ioFKyPE8UDiL\naPAhVGXe5UJ2iFx0goYSkLD+ycIL5xHAQvdiuWJxJbQM+Zsg3oKaVc2y2vD4q7/k\nUsLsggmKaBnVA4yahYH7ac610Fz61aGUtBwYUmLw7P0HBqZ5/lGNghc1ag7G1QNr\nrEB43XCV5hOnE8B8/4mfwrg/KZCbDznct6ebJF5lCDOrqqsUYFOsHXAUxboI3wNR\naZjFZ8Jno+BZva77f5phibDXGqlH3FnWdiU9Ykdv9QKBgQDm19/K/e3xuMNYHIJp\nQ/psprzufS4UEjavKSPS9zn06E47J0gjWNM3MqwJNNv/7KhbxM1Em6EZqUu0IDjX\n8cHK9lSwDFUQXwqEzhB8WSGo9ik3TyOJOQwn8gzQtKcyWm0RN0Z6mkxTfMjnXWEC\nJtWPznRhgUGzQtLKzv1uLd+uswKBgQDAZNROtokrjwTBz6hVb9zosCPp7Yl4kA+N\nuFHkOacGZOkQz+Px4dqITQNhavYge14OHuhjqe+Qs8Zrj4Vv4SyNmhzuT1K3/gOv\n9ull+6F4t6gA/Dtxl5asnkhr7Fz0qOZ2hOY+KS7p0lxLECXNrvm9xUC7ZP9zfXt2\nYZjSpcWHfQKBgQCl7wUiQFe7Csn4Wz3u9vtbAJwMfpURvmnqjRMAQdA6WIZrhPgY\n2G+zXMxHLeT6WR5IIPZAW/J4pLYTO5pAt7pK77i+d47p3reL8Sfuio9kidXkuRFd\nQVGYoShPGZyZ2GBQIMCbvrAgzzEHBpuF1W5sGcaUlDoq5lzphdKsUXNZewKBgH5F\nbwZeTTb2ffPKG7w/V+isOlX9Hd2wB1VHZQ/6OsI6lrD/EKcfSbNtgEt+zR1Ses+f\nv3wx+1aodxD/OF/OlNQ3Esfh7fNuVvM1hcf2V+sH9s72vPj3ud9/Xx7eGcBF3kRp\nQ0gG9ZBnDUYCyhztu2GuxaZ4SiR9dfujYI7YWAVtAoGBANn6sc4Tg2bCs3goo4a6\nSmAjNzjLkx3fk2Ov/EEbF6fNL/iinR8H0MhIXCHV83SxIY3iVQg6auAEzcuet005\n7U8chqiAVYV5BpJqAxS1wTa+CMqGq5ig1CZBb8SG1PoAVL1UzCOQlATt60J4iQWK\nL7Cx/V2C1PDQtqSA3nfAHQfu\n-----END PRIVATE KEY-----\n",
"client_email": "test-444@gen-lang-client-0149917117.iam.gserviceaccount.com",
"client_id": "109750256441106493303",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/test-444%40gen-lang-client-0149917117.iam.gserviceaccount.com",
"universe_domain": "googleapis.com"
}
File added
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment