Spaces:
Paused
Paused
Paul commited on
Commit ·
0748ff8
1
Parent(s): 136f619
update
Browse files- __pycache__/app.cpython-313.pyc +0 -0
- app.py +57 -23
- config/ai_models.json +512 -0
- finetune_model.py +40 -40
- finetuned_reply_service.py +11 -11
- openai_models_cache.json +512 -0
- openai_service.py +250 -0
- setup_and_finetune.py +8 -8
- test_all_openai_models.py +139 -0
- test_openai_list_models.py +103 -0
- test_openai_response.py +94 -0
- trigger_move_identifier.py +7 -7
__pycache__/app.cpython-313.pyc
CHANGED
|
Binary files a/__pycache__/app.cpython-313.pyc and b/__pycache__/app.cpython-313.pyc differ
|
|
|
app.py
CHANGED
|
@@ -4,7 +4,7 @@ from typing import Dict, Any, Tuple
|
|
| 4 |
|
| 5 |
from reply_service import get_reply_service
|
| 6 |
from trigger_move_identifier import get_trigger_move_identifier
|
| 7 |
-
from
|
| 8 |
from gemini_service import get_gemini_service, get_available_gemini_models
|
| 9 |
|
| 10 |
|
|
@@ -77,8 +77,8 @@ def parse_conversation(text: str) -> Tuple[str, str]:
|
|
| 77 |
return male, female
|
| 78 |
|
| 79 |
|
| 80 |
-
def run_full_pipeline(conversation: str, wingman_prompt: str = "", gemini_model_name: str = "gemini-2.
|
| 81 |
-
"""Run trigger detector and generate replies from 5 models (3 prompt styles +
|
| 82 |
try:
|
| 83 |
male, female = parse_conversation(conversation)
|
| 84 |
identifier = get_trigger_move_identifier(
|
|
@@ -129,20 +129,20 @@ def run_full_pipeline(conversation: str, wingman_prompt: str = "", gemini_model_
|
|
| 129 |
wingman_reply = ""
|
| 130 |
wingman_error = str(exc)
|
| 131 |
|
| 132 |
-
# Model 4 –
|
| 133 |
try:
|
| 134 |
-
|
| 135 |
-
# Format conversation for
|
| 136 |
formatted_conversation = f"Male: {male} ||| Female: {female}"
|
| 137 |
-
|
| 138 |
conversation=formatted_conversation,
|
| 139 |
trigger=trigger,
|
| 140 |
move=move,
|
| 141 |
)
|
| 142 |
-
|
| 143 |
except Exception as exc:
|
| 144 |
-
|
| 145 |
-
|
| 146 |
|
| 147 |
models_output["llama"] = {
|
| 148 |
"label": "Model 1 – Prompt style: an toàn / nhẹ nhàng",
|
|
@@ -162,10 +162,10 @@ def run_full_pipeline(conversation: str, wingman_prompt: str = "", gemini_model_
|
|
| 162 |
"error": wingman_error,
|
| 163 |
}
|
| 164 |
|
| 165 |
-
models_output["
|
| 166 |
-
"label": "Model 4 –
|
| 167 |
-
"reply":
|
| 168 |
-
"error":
|
| 169 |
}
|
| 170 |
|
| 171 |
# Model 5 – Google Gemini API
|
|
@@ -265,7 +265,7 @@ with gr.Blocks(title=title) as demo:
|
|
| 265 |
|
| 266 |
# Main Reply Suggestion Tab
|
| 267 |
gr.Markdown("### 🎯 Generate AI Reply Suggestions (5 Models)")
|
| 268 |
-
gr.Markdown("Nhập hội thoại và hệ thống sẽ chạy pipeline Trigger → Move → 5 models (3 prompt styles +
|
| 269 |
|
| 270 |
with gr.Row():
|
| 271 |
with gr.Column(scale=2):
|
|
@@ -298,6 +298,40 @@ with gr.Blocks(title=title) as demo:
|
|
| 298 |
)
|
| 299 |
gr.Markdown("Leave as-is for default behavior. Edits apply to Model 3 when its LoRA is used.")
|
| 300 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 301 |
# Model 5 – Gemini Model Selection (using whitelist - 15 tested models)
|
| 302 |
try:
|
| 303 |
gemini_models = get_available_gemini_models(use_whitelist=True)
|
|
@@ -361,11 +395,11 @@ with gr.Blocks(title=title) as demo:
|
|
| 361 |
placeholder="Reply từ mô hình Wingman LoRA (hoặc fallback prompt) sẽ xuất hiện tại đây.",
|
| 362 |
)
|
| 363 |
|
| 364 |
-
|
| 365 |
lines=3,
|
| 366 |
-
label="Model 4 –
|
| 367 |
interactive=False,
|
| 368 |
-
placeholder="Reply từ
|
| 369 |
)
|
| 370 |
|
| 371 |
gemini_box = gr.Textbox(
|
|
@@ -375,9 +409,9 @@ with gr.Blocks(title=title) as demo:
|
|
| 375 |
placeholder="Reply từ Gemini API sẽ xuất hiện tại đây.",
|
| 376 |
)
|
| 377 |
|
| 378 |
-
def generate_reply_with_extraction(conversation: str, wingman_prompt: str, gemini_model_name: str) -> Tuple[Dict[str, Any], str, str, str, str, str]:
|
| 379 |
"""Generate replies from five models."""
|
| 380 |
-
result = run_full_pipeline(conversation, wingman_prompt, gemini_model_name)
|
| 381 |
if "error" in result:
|
| 382 |
error_msg = f"❌ {result['error']}"
|
| 383 |
return result, error_msg, error_msg, error_msg, error_msg, error_msg
|
|
@@ -397,14 +431,14 @@ with gr.Blocks(title=title) as demo:
|
|
| 397 |
extract_text("llama"),
|
| 398 |
extract_text("pho"),
|
| 399 |
extract_text("wingman"),
|
| 400 |
-
extract_text("
|
| 401 |
extract_text("gemini"),
|
| 402 |
)
|
| 403 |
|
| 404 |
reply_btn.click(
|
| 405 |
generate_reply_with_extraction,
|
| 406 |
-
inputs=[reply_in, wingman_prompt_in, gemini_model_dropdown],
|
| 407 |
-
outputs=[reply_out, llama_box, pho_box, wingman_box,
|
| 408 |
api_name="reply"
|
| 409 |
)
|
| 410 |
|
|
|
|
| 4 |
|
| 5 |
from reply_service import get_reply_service
|
| 6 |
from trigger_move_identifier import get_trigger_move_identifier
|
| 7 |
+
from openai_service import get_openai_service, get_available_models
|
| 8 |
from gemini_service import get_gemini_service, get_available_gemini_models
|
| 9 |
|
| 10 |
|
|
|
|
| 77 |
return male, female
|
| 78 |
|
| 79 |
|
| 80 |
+
def run_full_pipeline(conversation: str, wingman_prompt: str = "", gemini_model_name: str = "models/gemini-2.0-flash", openai_model_name: str = "gpt-4o-mini") -> Dict[str, Any]:
|
| 81 |
+
"""Run trigger detector and generate replies from 5 models (3 prompt styles + OpenAI + Gemini)."""
|
| 82 |
try:
|
| 83 |
male, female = parse_conversation(conversation)
|
| 84 |
identifier = get_trigger_move_identifier(
|
|
|
|
| 129 |
wingman_reply = ""
|
| 130 |
wingman_error = str(exc)
|
| 131 |
|
| 132 |
+
# Model 4 – OpenAI API
|
| 133 |
try:
|
| 134 |
+
openai_service = get_openai_service(model_name=openai_model_name)
|
| 135 |
+
# Format conversation for OpenAI: "Male: ... ||| Female: ..."
|
| 136 |
formatted_conversation = f"Male: {male} ||| Female: {female}"
|
| 137 |
+
openai_reply = openai_service.generate_reply(
|
| 138 |
conversation=formatted_conversation,
|
| 139 |
trigger=trigger,
|
| 140 |
move=move,
|
| 141 |
)
|
| 142 |
+
openai_error = ""
|
| 143 |
except Exception as exc:
|
| 144 |
+
openai_reply = ""
|
| 145 |
+
openai_error = str(exc)
|
| 146 |
|
| 147 |
models_output["llama"] = {
|
| 148 |
"label": "Model 1 – Prompt style: an toàn / nhẹ nhàng",
|
|
|
|
| 162 |
"error": wingman_error,
|
| 163 |
}
|
| 164 |
|
| 165 |
+
models_output["openai"] = {
|
| 166 |
+
"label": f"Model 4 – OpenAI API ({openai_model_name})",
|
| 167 |
+
"reply": openai_reply,
|
| 168 |
+
"error": openai_error,
|
| 169 |
}
|
| 170 |
|
| 171 |
# Model 5 – Google Gemini API
|
|
|
|
| 265 |
|
| 266 |
# Main Reply Suggestion Tab
|
| 267 |
gr.Markdown("### 🎯 Generate AI Reply Suggestions (5 Models)")
|
| 268 |
+
gr.Markdown("Nhập hội thoại và hệ thống sẽ chạy pipeline Trigger → Move → 5 models (3 prompt styles + OpenAI API + Gemini API).")
|
| 269 |
|
| 270 |
with gr.Row():
|
| 271 |
with gr.Column(scale=2):
|
|
|
|
| 298 |
)
|
| 299 |
gr.Markdown("Leave as-is for default behavior. Edits apply to Model 3 when its LoRA is used.")
|
| 300 |
|
| 301 |
+
# Model 4 – OpenAI Model Selection (with search)
|
| 302 |
+
try:
|
| 303 |
+
# Load all available models from cache
|
| 304 |
+
all_model_ids = get_available_models(prefix_filters=["gpt-", "o1-", "o3-"])
|
| 305 |
+
|
| 306 |
+
# Create choices with (label, value) format for better display
|
| 307 |
+
openai_dropdown_choices = []
|
| 308 |
+
openai_model_choices = []
|
| 309 |
+
|
| 310 |
+
for model_id in all_model_ids:
|
| 311 |
+
# Use model_id as both label and value, but format nicely
|
| 312 |
+
label = model_id.replace("gpt-", "GPT-").replace("o1-", "O1-").replace("o3-", "O3-")
|
| 313 |
+
openai_dropdown_choices.append((label, model_id))
|
| 314 |
+
openai_model_choices.append(model_id)
|
| 315 |
+
|
| 316 |
+
default_openai_model = "gpt-4o-mini" if "gpt-4o-mini" in openai_model_choices else (openai_model_choices[0] if openai_model_choices else "gpt-4o-mini")
|
| 317 |
+
|
| 318 |
+
print(f"✓ Loaded {len(openai_model_choices)} OpenAI models for dropdown")
|
| 319 |
+
except Exception as e:
|
| 320 |
+
print(f"⚠ Error loading OpenAI models: {e}")
|
| 321 |
+
openai_dropdown_choices = [("gpt-4o-mini (default - API key may be missing)", "gpt-4o-mini")]
|
| 322 |
+
openai_model_choices = ["gpt-4o-mini"]
|
| 323 |
+
default_openai_model = "gpt-4o-mini"
|
| 324 |
+
|
| 325 |
+
openai_model_dropdown = gr.Dropdown(
|
| 326 |
+
choices=openai_dropdown_choices,
|
| 327 |
+
value=default_openai_model,
|
| 328 |
+
label="Model 4 – Select OpenAI Model",
|
| 329 |
+
info=f"Search and choose from {len(openai_model_choices)} available OpenAI models",
|
| 330 |
+
interactive=True,
|
| 331 |
+
filterable=True, # Enable search/filter functionality
|
| 332 |
+
scale=1,
|
| 333 |
+
)
|
| 334 |
+
|
| 335 |
# Model 5 – Gemini Model Selection (using whitelist - 15 tested models)
|
| 336 |
try:
|
| 337 |
gemini_models = get_available_gemini_models(use_whitelist=True)
|
|
|
|
| 395 |
placeholder="Reply từ mô hình Wingman LoRA (hoặc fallback prompt) sẽ xuất hiện tại đây.",
|
| 396 |
)
|
| 397 |
|
| 398 |
+
openai_box = gr.Textbox(
|
| 399 |
lines=3,
|
| 400 |
+
label="Model 4 – OpenAI API",
|
| 401 |
interactive=False,
|
| 402 |
+
placeholder="Reply từ OpenAI API sẽ xuất hiện tại đây.",
|
| 403 |
)
|
| 404 |
|
| 405 |
gemini_box = gr.Textbox(
|
|
|
|
| 409 |
placeholder="Reply từ Gemini API sẽ xuất hiện tại đây.",
|
| 410 |
)
|
| 411 |
|
| 412 |
+
def generate_reply_with_extraction(conversation: str, wingman_prompt: str, openai_model_name: str, gemini_model_name: str) -> Tuple[Dict[str, Any], str, str, str, str, str]:
|
| 413 |
"""Generate replies from five models."""
|
| 414 |
+
result = run_full_pipeline(conversation, wingman_prompt, gemini_model_name, openai_model_name)
|
| 415 |
if "error" in result:
|
| 416 |
error_msg = f"❌ {result['error']}"
|
| 417 |
return result, error_msg, error_msg, error_msg, error_msg, error_msg
|
|
|
|
| 431 |
extract_text("llama"),
|
| 432 |
extract_text("pho"),
|
| 433 |
extract_text("wingman"),
|
| 434 |
+
extract_text("openai"),
|
| 435 |
extract_text("gemini"),
|
| 436 |
)
|
| 437 |
|
| 438 |
reply_btn.click(
|
| 439 |
generate_reply_with_extraction,
|
| 440 |
+
inputs=[reply_in, wingman_prompt_in, openai_model_dropdown, gemini_model_dropdown],
|
| 441 |
+
outputs=[reply_out, llama_box, pho_box, wingman_box, openai_box, gemini_box],
|
| 442 |
api_name="reply"
|
| 443 |
)
|
| 444 |
|
config/ai_models.json
ADDED
|
@@ -0,0 +1,512 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
{
|
| 3 |
+
"id": "gpt-4-0613",
|
| 4 |
+
"created": 1686588896,
|
| 5 |
+
"owned_by": "openai"
|
| 6 |
+
},
|
| 7 |
+
{
|
| 8 |
+
"id": "gpt-4",
|
| 9 |
+
"created": 1687882411,
|
| 10 |
+
"owned_by": "openai"
|
| 11 |
+
},
|
| 12 |
+
{
|
| 13 |
+
"id": "gpt-3.5-turbo",
|
| 14 |
+
"created": 1677610602,
|
| 15 |
+
"owned_by": "openai"
|
| 16 |
+
},
|
| 17 |
+
{
|
| 18 |
+
"id": "gpt-5.1-codex-mini",
|
| 19 |
+
"created": 1763007109,
|
| 20 |
+
"owned_by": "system"
|
| 21 |
+
},
|
| 22 |
+
{
|
| 23 |
+
"id": "gpt-5.1-chat-latest",
|
| 24 |
+
"created": 1762547951,
|
| 25 |
+
"owned_by": "system"
|
| 26 |
+
},
|
| 27 |
+
{
|
| 28 |
+
"id": "gpt-5.1-2025-11-13",
|
| 29 |
+
"created": 1762800353,
|
| 30 |
+
"owned_by": "system"
|
| 31 |
+
},
|
| 32 |
+
{
|
| 33 |
+
"id": "gpt-5.1",
|
| 34 |
+
"created": 1762800673,
|
| 35 |
+
"owned_by": "system"
|
| 36 |
+
},
|
| 37 |
+
{
|
| 38 |
+
"id": "gpt-5.1-codex",
|
| 39 |
+
"created": 1762988221,
|
| 40 |
+
"owned_by": "system"
|
| 41 |
+
},
|
| 42 |
+
{
|
| 43 |
+
"id": "davinci-002",
|
| 44 |
+
"created": 1692634301,
|
| 45 |
+
"owned_by": "system"
|
| 46 |
+
},
|
| 47 |
+
{
|
| 48 |
+
"id": "babbage-002",
|
| 49 |
+
"created": 1692634615,
|
| 50 |
+
"owned_by": "system"
|
| 51 |
+
},
|
| 52 |
+
{
|
| 53 |
+
"id": "gpt-3.5-turbo-instruct",
|
| 54 |
+
"created": 1692901427,
|
| 55 |
+
"owned_by": "system"
|
| 56 |
+
},
|
| 57 |
+
{
|
| 58 |
+
"id": "gpt-3.5-turbo-instruct-0914",
|
| 59 |
+
"created": 1694122472,
|
| 60 |
+
"owned_by": "system"
|
| 61 |
+
},
|
| 62 |
+
{
|
| 63 |
+
"id": "dall-e-3",
|
| 64 |
+
"created": 1698785189,
|
| 65 |
+
"owned_by": "system"
|
| 66 |
+
},
|
| 67 |
+
{
|
| 68 |
+
"id": "dall-e-2",
|
| 69 |
+
"created": 1698798177,
|
| 70 |
+
"owned_by": "system"
|
| 71 |
+
},
|
| 72 |
+
{
|
| 73 |
+
"id": "gpt-4-1106-preview",
|
| 74 |
+
"created": 1698957206,
|
| 75 |
+
"owned_by": "system"
|
| 76 |
+
},
|
| 77 |
+
{
|
| 78 |
+
"id": "gpt-3.5-turbo-1106",
|
| 79 |
+
"created": 1698959748,
|
| 80 |
+
"owned_by": "system"
|
| 81 |
+
},
|
| 82 |
+
{
|
| 83 |
+
"id": "tts-1-hd",
|
| 84 |
+
"created": 1699046015,
|
| 85 |
+
"owned_by": "system"
|
| 86 |
+
},
|
| 87 |
+
{
|
| 88 |
+
"id": "tts-1-1106",
|
| 89 |
+
"created": 1699053241,
|
| 90 |
+
"owned_by": "system"
|
| 91 |
+
},
|
| 92 |
+
{
|
| 93 |
+
"id": "tts-1-hd-1106",
|
| 94 |
+
"created": 1699053533,
|
| 95 |
+
"owned_by": "system"
|
| 96 |
+
},
|
| 97 |
+
{
|
| 98 |
+
"id": "text-embedding-3-small",
|
| 99 |
+
"created": 1705948997,
|
| 100 |
+
"owned_by": "system"
|
| 101 |
+
},
|
| 102 |
+
{
|
| 103 |
+
"id": "text-embedding-3-large",
|
| 104 |
+
"created": 1705953180,
|
| 105 |
+
"owned_by": "system"
|
| 106 |
+
},
|
| 107 |
+
{
|
| 108 |
+
"id": "gpt-4-0125-preview",
|
| 109 |
+
"created": 1706037612,
|
| 110 |
+
"owned_by": "system"
|
| 111 |
+
},
|
| 112 |
+
{
|
| 113 |
+
"id": "gpt-4-turbo-preview",
|
| 114 |
+
"created": 1706037777,
|
| 115 |
+
"owned_by": "system"
|
| 116 |
+
},
|
| 117 |
+
{
|
| 118 |
+
"id": "gpt-3.5-turbo-0125",
|
| 119 |
+
"created": 1706048358,
|
| 120 |
+
"owned_by": "system"
|
| 121 |
+
},
|
| 122 |
+
{
|
| 123 |
+
"id": "gpt-4-turbo",
|
| 124 |
+
"created": 1712361441,
|
| 125 |
+
"owned_by": "system"
|
| 126 |
+
},
|
| 127 |
+
{
|
| 128 |
+
"id": "gpt-4-turbo-2024-04-09",
|
| 129 |
+
"created": 1712601677,
|
| 130 |
+
"owned_by": "system"
|
| 131 |
+
},
|
| 132 |
+
{
|
| 133 |
+
"id": "gpt-4o",
|
| 134 |
+
"created": 1715367049,
|
| 135 |
+
"owned_by": "system"
|
| 136 |
+
},
|
| 137 |
+
{
|
| 138 |
+
"id": "gpt-4o-2024-05-13",
|
| 139 |
+
"created": 1715368132,
|
| 140 |
+
"owned_by": "system"
|
| 141 |
+
},
|
| 142 |
+
{
|
| 143 |
+
"id": "gpt-4o-mini-2024-07-18",
|
| 144 |
+
"created": 1721172717,
|
| 145 |
+
"owned_by": "system"
|
| 146 |
+
},
|
| 147 |
+
{
|
| 148 |
+
"id": "gpt-4o-mini",
|
| 149 |
+
"created": 1721172741,
|
| 150 |
+
"owned_by": "system"
|
| 151 |
+
},
|
| 152 |
+
{
|
| 153 |
+
"id": "gpt-4o-2024-08-06",
|
| 154 |
+
"created": 1722814719,
|
| 155 |
+
"owned_by": "system"
|
| 156 |
+
},
|
| 157 |
+
{
|
| 158 |
+
"id": "chatgpt-4o-latest",
|
| 159 |
+
"created": 1723515131,
|
| 160 |
+
"owned_by": "system"
|
| 161 |
+
},
|
| 162 |
+
{
|
| 163 |
+
"id": "gpt-4o-realtime-preview-2024-10-01",
|
| 164 |
+
"created": 1727131766,
|
| 165 |
+
"owned_by": "system"
|
| 166 |
+
},
|
| 167 |
+
{
|
| 168 |
+
"id": "gpt-4o-audio-preview-2024-10-01",
|
| 169 |
+
"created": 1727389042,
|
| 170 |
+
"owned_by": "system"
|
| 171 |
+
},
|
| 172 |
+
{
|
| 173 |
+
"id": "gpt-4o-audio-preview",
|
| 174 |
+
"created": 1727460443,
|
| 175 |
+
"owned_by": "system"
|
| 176 |
+
},
|
| 177 |
+
{
|
| 178 |
+
"id": "gpt-4o-realtime-preview",
|
| 179 |
+
"created": 1727659998,
|
| 180 |
+
"owned_by": "system"
|
| 181 |
+
},
|
| 182 |
+
{
|
| 183 |
+
"id": "omni-moderation-latest",
|
| 184 |
+
"created": 1731689265,
|
| 185 |
+
"owned_by": "system"
|
| 186 |
+
},
|
| 187 |
+
{
|
| 188 |
+
"id": "omni-moderation-2024-09-26",
|
| 189 |
+
"created": 1732734466,
|
| 190 |
+
"owned_by": "system"
|
| 191 |
+
},
|
| 192 |
+
{
|
| 193 |
+
"id": "gpt-4o-realtime-preview-2024-12-17",
|
| 194 |
+
"created": 1733945430,
|
| 195 |
+
"owned_by": "system"
|
| 196 |
+
},
|
| 197 |
+
{
|
| 198 |
+
"id": "gpt-4o-audio-preview-2024-12-17",
|
| 199 |
+
"created": 1734034239,
|
| 200 |
+
"owned_by": "system"
|
| 201 |
+
},
|
| 202 |
+
{
|
| 203 |
+
"id": "gpt-4o-mini-realtime-preview-2024-12-17",
|
| 204 |
+
"created": 1734112601,
|
| 205 |
+
"owned_by": "system"
|
| 206 |
+
},
|
| 207 |
+
{
|
| 208 |
+
"id": "gpt-4o-mini-audio-preview-2024-12-17",
|
| 209 |
+
"created": 1734115920,
|
| 210 |
+
"owned_by": "system"
|
| 211 |
+
},
|
| 212 |
+
{
|
| 213 |
+
"id": "o1-2024-12-17",
|
| 214 |
+
"created": 1734326976,
|
| 215 |
+
"owned_by": "system"
|
| 216 |
+
},
|
| 217 |
+
{
|
| 218 |
+
"id": "o1",
|
| 219 |
+
"created": 1734375816,
|
| 220 |
+
"owned_by": "system"
|
| 221 |
+
},
|
| 222 |
+
{
|
| 223 |
+
"id": "gpt-4o-mini-realtime-preview",
|
| 224 |
+
"created": 1734387380,
|
| 225 |
+
"owned_by": "system"
|
| 226 |
+
},
|
| 227 |
+
{
|
| 228 |
+
"id": "gpt-4o-mini-audio-preview",
|
| 229 |
+
"created": 1734387424,
|
| 230 |
+
"owned_by": "system"
|
| 231 |
+
},
|
| 232 |
+
{
|
| 233 |
+
"id": "o3-mini",
|
| 234 |
+
"created": 1737146383,
|
| 235 |
+
"owned_by": "system"
|
| 236 |
+
},
|
| 237 |
+
{
|
| 238 |
+
"id": "o3-mini-2025-01-31",
|
| 239 |
+
"created": 1738010200,
|
| 240 |
+
"owned_by": "system"
|
| 241 |
+
},
|
| 242 |
+
{
|
| 243 |
+
"id": "gpt-4o-2024-11-20",
|
| 244 |
+
"created": 1739331543,
|
| 245 |
+
"owned_by": "system"
|
| 246 |
+
},
|
| 247 |
+
{
|
| 248 |
+
"id": "gpt-4o-search-preview-2025-03-11",
|
| 249 |
+
"created": 1741388170,
|
| 250 |
+
"owned_by": "system"
|
| 251 |
+
},
|
| 252 |
+
{
|
| 253 |
+
"id": "gpt-4o-search-preview",
|
| 254 |
+
"created": 1741388720,
|
| 255 |
+
"owned_by": "system"
|
| 256 |
+
},
|
| 257 |
+
{
|
| 258 |
+
"id": "gpt-4o-mini-search-preview-2025-03-11",
|
| 259 |
+
"created": 1741390858,
|
| 260 |
+
"owned_by": "system"
|
| 261 |
+
},
|
| 262 |
+
{
|
| 263 |
+
"id": "gpt-4o-mini-search-preview",
|
| 264 |
+
"created": 1741391161,
|
| 265 |
+
"owned_by": "system"
|
| 266 |
+
},
|
| 267 |
+
{
|
| 268 |
+
"id": "gpt-4o-transcribe",
|
| 269 |
+
"created": 1742068463,
|
| 270 |
+
"owned_by": "system"
|
| 271 |
+
},
|
| 272 |
+
{
|
| 273 |
+
"id": "gpt-4o-mini-transcribe",
|
| 274 |
+
"created": 1742068596,
|
| 275 |
+
"owned_by": "system"
|
| 276 |
+
},
|
| 277 |
+
{
|
| 278 |
+
"id": "o1-pro-2025-03-19",
|
| 279 |
+
"created": 1742251504,
|
| 280 |
+
"owned_by": "system"
|
| 281 |
+
},
|
| 282 |
+
{
|
| 283 |
+
"id": "o1-pro",
|
| 284 |
+
"created": 1742251791,
|
| 285 |
+
"owned_by": "system"
|
| 286 |
+
},
|
| 287 |
+
{
|
| 288 |
+
"id": "gpt-4o-mini-tts",
|
| 289 |
+
"created": 1742403959,
|
| 290 |
+
"owned_by": "system"
|
| 291 |
+
},
|
| 292 |
+
{
|
| 293 |
+
"id": "o3-2025-04-16",
|
| 294 |
+
"created": 1744133301,
|
| 295 |
+
"owned_by": "system"
|
| 296 |
+
},
|
| 297 |
+
{
|
| 298 |
+
"id": "o4-mini-2025-04-16",
|
| 299 |
+
"created": 1744133506,
|
| 300 |
+
"owned_by": "system"
|
| 301 |
+
},
|
| 302 |
+
{
|
| 303 |
+
"id": "o3",
|
| 304 |
+
"created": 1744225308,
|
| 305 |
+
"owned_by": "system"
|
| 306 |
+
},
|
| 307 |
+
{
|
| 308 |
+
"id": "o4-mini",
|
| 309 |
+
"created": 1744225351,
|
| 310 |
+
"owned_by": "system"
|
| 311 |
+
},
|
| 312 |
+
{
|
| 313 |
+
"id": "gpt-4.1-2025-04-14",
|
| 314 |
+
"created": 1744315746,
|
| 315 |
+
"owned_by": "system"
|
| 316 |
+
},
|
| 317 |
+
{
|
| 318 |
+
"id": "gpt-4.1",
|
| 319 |
+
"created": 1744316542,
|
| 320 |
+
"owned_by": "system"
|
| 321 |
+
},
|
| 322 |
+
{
|
| 323 |
+
"id": "gpt-4.1-mini-2025-04-14",
|
| 324 |
+
"created": 1744317547,
|
| 325 |
+
"owned_by": "system"
|
| 326 |
+
},
|
| 327 |
+
{
|
| 328 |
+
"id": "gpt-4.1-mini",
|
| 329 |
+
"created": 1744318173,
|
| 330 |
+
"owned_by": "system"
|
| 331 |
+
},
|
| 332 |
+
{
|
| 333 |
+
"id": "gpt-4.1-nano-2025-04-14",
|
| 334 |
+
"created": 1744321025,
|
| 335 |
+
"owned_by": "system"
|
| 336 |
+
},
|
| 337 |
+
{
|
| 338 |
+
"id": "gpt-4.1-nano",
|
| 339 |
+
"created": 1744321707,
|
| 340 |
+
"owned_by": "system"
|
| 341 |
+
},
|
| 342 |
+
{
|
| 343 |
+
"id": "gpt-image-1",
|
| 344 |
+
"created": 1745517030,
|
| 345 |
+
"owned_by": "system"
|
| 346 |
+
},
|
| 347 |
+
{
|
| 348 |
+
"id": "codex-mini-latest",
|
| 349 |
+
"created": 1746673257,
|
| 350 |
+
"owned_by": "system"
|
| 351 |
+
},
|
| 352 |
+
{
|
| 353 |
+
"id": "gpt-4o-realtime-preview-2025-06-03",
|
| 354 |
+
"created": 1748907838,
|
| 355 |
+
"owned_by": "system"
|
| 356 |
+
},
|
| 357 |
+
{
|
| 358 |
+
"id": "gpt-4o-audio-preview-2025-06-03",
|
| 359 |
+
"created": 1748908498,
|
| 360 |
+
"owned_by": "system"
|
| 361 |
+
},
|
| 362 |
+
{
|
| 363 |
+
"id": "o4-mini-deep-research",
|
| 364 |
+
"created": 1749685485,
|
| 365 |
+
"owned_by": "system"
|
| 366 |
+
},
|
| 367 |
+
{
|
| 368 |
+
"id": "gpt-4o-transcribe-diarize",
|
| 369 |
+
"created": 1750798887,
|
| 370 |
+
"owned_by": "system"
|
| 371 |
+
},
|
| 372 |
+
{
|
| 373 |
+
"id": "o4-mini-deep-research-2025-06-26",
|
| 374 |
+
"created": 1750866121,
|
| 375 |
+
"owned_by": "system"
|
| 376 |
+
},
|
| 377 |
+
{
|
| 378 |
+
"id": "gpt-5-chat-latest",
|
| 379 |
+
"created": 1754073306,
|
| 380 |
+
"owned_by": "system"
|
| 381 |
+
},
|
| 382 |
+
{
|
| 383 |
+
"id": "gpt-5-2025-08-07",
|
| 384 |
+
"created": 1754075360,
|
| 385 |
+
"owned_by": "system"
|
| 386 |
+
},
|
| 387 |
+
{
|
| 388 |
+
"id": "gpt-5",
|
| 389 |
+
"created": 1754425777,
|
| 390 |
+
"owned_by": "system"
|
| 391 |
+
},
|
| 392 |
+
{
|
| 393 |
+
"id": "gpt-5-mini-2025-08-07",
|
| 394 |
+
"created": 1754425867,
|
| 395 |
+
"owned_by": "system"
|
| 396 |
+
},
|
| 397 |
+
{
|
| 398 |
+
"id": "gpt-5-mini",
|
| 399 |
+
"created": 1754425928,
|
| 400 |
+
"owned_by": "system"
|
| 401 |
+
},
|
| 402 |
+
{
|
| 403 |
+
"id": "gpt-5-nano-2025-08-07",
|
| 404 |
+
"created": 1754426303,
|
| 405 |
+
"owned_by": "system"
|
| 406 |
+
},
|
| 407 |
+
{
|
| 408 |
+
"id": "gpt-5-nano",
|
| 409 |
+
"created": 1754426384,
|
| 410 |
+
"owned_by": "system"
|
| 411 |
+
},
|
| 412 |
+
{
|
| 413 |
+
"id": "gpt-audio-2025-08-28",
|
| 414 |
+
"created": 1756256146,
|
| 415 |
+
"owned_by": "system"
|
| 416 |
+
},
|
| 417 |
+
{
|
| 418 |
+
"id": "gpt-realtime",
|
| 419 |
+
"created": 1756271701,
|
| 420 |
+
"owned_by": "system"
|
| 421 |
+
},
|
| 422 |
+
{
|
| 423 |
+
"id": "gpt-realtime-2025-08-28",
|
| 424 |
+
"created": 1756271773,
|
| 425 |
+
"owned_by": "system"
|
| 426 |
+
},
|
| 427 |
+
{
|
| 428 |
+
"id": "gpt-audio",
|
| 429 |
+
"created": 1756339249,
|
| 430 |
+
"owned_by": "system"
|
| 431 |
+
},
|
| 432 |
+
{
|
| 433 |
+
"id": "gpt-5-codex",
|
| 434 |
+
"created": 1757527818,
|
| 435 |
+
"owned_by": "system"
|
| 436 |
+
},
|
| 437 |
+
{
|
| 438 |
+
"id": "gpt-image-1-mini",
|
| 439 |
+
"created": 1758845821,
|
| 440 |
+
"owned_by": "system"
|
| 441 |
+
},
|
| 442 |
+
{
|
| 443 |
+
"id": "gpt-5-pro-2025-10-06",
|
| 444 |
+
"created": 1759469707,
|
| 445 |
+
"owned_by": "system"
|
| 446 |
+
},
|
| 447 |
+
{
|
| 448 |
+
"id": "gpt-5-pro",
|
| 449 |
+
"created": 1759469822,
|
| 450 |
+
"owned_by": "system"
|
| 451 |
+
},
|
| 452 |
+
{
|
| 453 |
+
"id": "gpt-audio-mini",
|
| 454 |
+
"created": 1759512027,
|
| 455 |
+
"owned_by": "system"
|
| 456 |
+
},
|
| 457 |
+
{
|
| 458 |
+
"id": "gpt-audio-mini-2025-10-06",
|
| 459 |
+
"created": 1759512137,
|
| 460 |
+
"owned_by": "system"
|
| 461 |
+
},
|
| 462 |
+
{
|
| 463 |
+
"id": "gpt-5-search-api",
|
| 464 |
+
"created": 1759514629,
|
| 465 |
+
"owned_by": "system"
|
| 466 |
+
},
|
| 467 |
+
{
|
| 468 |
+
"id": "gpt-realtime-mini",
|
| 469 |
+
"created": 1759517133,
|
| 470 |
+
"owned_by": "system"
|
| 471 |
+
},
|
| 472 |
+
{
|
| 473 |
+
"id": "gpt-realtime-mini-2025-10-06",
|
| 474 |
+
"created": 1759517175,
|
| 475 |
+
"owned_by": "system"
|
| 476 |
+
},
|
| 477 |
+
{
|
| 478 |
+
"id": "sora-2",
|
| 479 |
+
"created": 1759708615,
|
| 480 |
+
"owned_by": "system"
|
| 481 |
+
},
|
| 482 |
+
{
|
| 483 |
+
"id": "sora-2-pro",
|
| 484 |
+
"created": 1759708663,
|
| 485 |
+
"owned_by": "system"
|
| 486 |
+
},
|
| 487 |
+
{
|
| 488 |
+
"id": "gpt-5-search-api-2025-10-14",
|
| 489 |
+
"created": 1760043960,
|
| 490 |
+
"owned_by": "system"
|
| 491 |
+
},
|
| 492 |
+
{
|
| 493 |
+
"id": "gpt-3.5-turbo-16k",
|
| 494 |
+
"created": 1683758102,
|
| 495 |
+
"owned_by": "openai-internal"
|
| 496 |
+
},
|
| 497 |
+
{
|
| 498 |
+
"id": "tts-1",
|
| 499 |
+
"created": 1681940951,
|
| 500 |
+
"owned_by": "openai-internal"
|
| 501 |
+
},
|
| 502 |
+
{
|
| 503 |
+
"id": "whisper-1",
|
| 504 |
+
"created": 1677532384,
|
| 505 |
+
"owned_by": "openai-internal"
|
| 506 |
+
},
|
| 507 |
+
{
|
| 508 |
+
"id": "text-embedding-ada-002",
|
| 509 |
+
"created": 1671217299,
|
| 510 |
+
"owned_by": "openai-internal"
|
| 511 |
+
}
|
| 512 |
+
]
|
finetune_model.py
CHANGED
|
@@ -76,7 +76,7 @@ def prepare_training_data(df, use_history=True, persona="default"):
|
|
| 76 |
"""
|
| 77 |
training_data = []
|
| 78 |
conversation_history = []
|
| 79 |
-
|
| 80 |
has_clean_reply = {"conversation", "trigger", "move", "male_reply"}.issubset(set(df.columns))
|
| 81 |
|
| 82 |
if has_clean_reply:
|
|
@@ -102,20 +102,20 @@ def prepare_training_data(df, use_history=True, persona="default"):
|
|
| 102 |
# Fallback: dùng dữ liệu gốc (kém lý tưởng hơn)
|
| 103 |
trigger_cols = [col for col in df.columns if col.startswith("trigger_")]
|
| 104 |
move_cols = [col for col in df.columns if col.startswith("move_")]
|
| 105 |
-
|
| 106 |
for _, row in df.iterrows():
|
| 107 |
user_text = str(row["user_text"]) if pd.notna(row.get("user_text")) else ""
|
| 108 |
partner_text = str(row["partner_text"]) if pd.notna(row.get("partner_text")) else ""
|
| 109 |
-
|
| 110 |
if not partner_text or partner_text.strip() == "_":
|
| 111 |
continue
|
| 112 |
-
|
| 113 |
active_triggers = get_active_labels(row, trigger_cols)
|
| 114 |
active_moves = get_active_labels(row, move_cols)
|
| 115 |
-
|
| 116 |
trigger = active_triggers[0] if active_triggers[0] != "none" else "neutral"
|
| 117 |
move = active_moves[0] if active_moves[0] != "none" else "neutral"
|
| 118 |
-
|
| 119 |
if use_history and conversation_history:
|
| 120 |
history_str = "\n".join(conversation_history)
|
| 121 |
if user_text and user_text.strip() != "_":
|
|
@@ -128,27 +128,27 @@ def prepare_training_data(df, use_history=True, persona="default"):
|
|
| 128 |
conversation = f"Male: {user_text} ||| Female: {partner_text}"
|
| 129 |
else:
|
| 130 |
conversation = f"Female: {partner_text}"
|
| 131 |
-
|
| 132 |
prompt = build_instruction(conversation, trigger, move, persona)
|
| 133 |
response = partner_text.strip()
|
| 134 |
-
|
| 135 |
training_data.append(
|
| 136 |
{
|
| 137 |
-
|
| 138 |
-
|
| 139 |
"output": response,
|
| 140 |
}
|
| 141 |
)
|
| 142 |
-
|
| 143 |
if user_text and user_text.strip() != "_":
|
| 144 |
conversation_history.append(f"Male: {user_text}")
|
| 145 |
if partner_text and partner_text.strip() != "_":
|
| 146 |
conversation_history.append(f"Female: {partner_text}")
|
| 147 |
-
|
| 148 |
max_history = 4
|
| 149 |
if len(conversation_history) > max_history:
|
| 150 |
conversation_history = conversation_history[-max_history:]
|
| 151 |
-
|
| 152 |
return training_data
|
| 153 |
|
| 154 |
|
|
@@ -268,18 +268,18 @@ def main():
|
|
| 268 |
use_quantization = False
|
| 269 |
quant_config = None
|
| 270 |
if args.model_arch == "causal":
|
| 271 |
-
|
| 272 |
-
|
| 273 |
quant_config = BitsAndBytesConfig(
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
|
| 283 |
|
| 284 |
model = None
|
| 285 |
last_error = None
|
|
@@ -313,15 +313,15 @@ def main():
|
|
| 313 |
if use_quantization:
|
| 314 |
try:
|
| 315 |
model = load_base_model(use_quant=True)
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
|
| 320 |
model = None
|
| 321 |
-
|
| 322 |
-
|
| 323 |
model = load_base_model(use_quant=False)
|
| 324 |
-
|
| 325 |
except Exception as e:
|
| 326 |
if last_error:
|
| 327 |
print(f"Original error: {last_error}")
|
|
@@ -356,14 +356,14 @@ def main():
|
|
| 356 |
|
| 357 |
# Configure LoRA
|
| 358 |
if args.model_arch == "causal":
|
| 359 |
-
|
| 360 |
-
|
| 361 |
-
|
| 362 |
-
|
| 363 |
-
|
| 364 |
-
|
| 365 |
-
|
| 366 |
-
|
| 367 |
else:
|
| 368 |
lora_config = LoraConfig(
|
| 369 |
r=16,
|
|
@@ -416,7 +416,7 @@ def main():
|
|
| 416 |
training_args = TrainingArguments(
|
| 417 |
eval_strategy="steps",
|
| 418 |
**training_common_kwargs,
|
| 419 |
-
|
| 420 |
|
| 421 |
# Create Trainer
|
| 422 |
trainer = Trainer(
|
|
|
|
| 76 |
"""
|
| 77 |
training_data = []
|
| 78 |
conversation_history = []
|
| 79 |
+
|
| 80 |
has_clean_reply = {"conversation", "trigger", "move", "male_reply"}.issubset(set(df.columns))
|
| 81 |
|
| 82 |
if has_clean_reply:
|
|
|
|
| 102 |
# Fallback: dùng dữ liệu gốc (kém lý tưởng hơn)
|
| 103 |
trigger_cols = [col for col in df.columns if col.startswith("trigger_")]
|
| 104 |
move_cols = [col for col in df.columns if col.startswith("move_")]
|
| 105 |
+
|
| 106 |
for _, row in df.iterrows():
|
| 107 |
user_text = str(row["user_text"]) if pd.notna(row.get("user_text")) else ""
|
| 108 |
partner_text = str(row["partner_text"]) if pd.notna(row.get("partner_text")) else ""
|
| 109 |
+
|
| 110 |
if not partner_text or partner_text.strip() == "_":
|
| 111 |
continue
|
| 112 |
+
|
| 113 |
active_triggers = get_active_labels(row, trigger_cols)
|
| 114 |
active_moves = get_active_labels(row, move_cols)
|
| 115 |
+
|
| 116 |
trigger = active_triggers[0] if active_triggers[0] != "none" else "neutral"
|
| 117 |
move = active_moves[0] if active_moves[0] != "none" else "neutral"
|
| 118 |
+
|
| 119 |
if use_history and conversation_history:
|
| 120 |
history_str = "\n".join(conversation_history)
|
| 121 |
if user_text and user_text.strip() != "_":
|
|
|
|
| 128 |
conversation = f"Male: {user_text} ||| Female: {partner_text}"
|
| 129 |
else:
|
| 130 |
conversation = f"Female: {partner_text}"
|
| 131 |
+
|
| 132 |
prompt = build_instruction(conversation, trigger, move, persona)
|
| 133 |
response = partner_text.strip()
|
| 134 |
+
|
| 135 |
training_data.append(
|
| 136 |
{
|
| 137 |
+
"instruction": prompt,
|
| 138 |
+
"input": "",
|
| 139 |
"output": response,
|
| 140 |
}
|
| 141 |
)
|
| 142 |
+
|
| 143 |
if user_text and user_text.strip() != "_":
|
| 144 |
conversation_history.append(f"Male: {user_text}")
|
| 145 |
if partner_text and partner_text.strip() != "_":
|
| 146 |
conversation_history.append(f"Female: {partner_text}")
|
| 147 |
+
|
| 148 |
max_history = 4
|
| 149 |
if len(conversation_history) > max_history:
|
| 150 |
conversation_history = conversation_history[-max_history:]
|
| 151 |
+
|
| 152 |
return training_data
|
| 153 |
|
| 154 |
|
|
|
|
| 268 |
use_quantization = False
|
| 269 |
quant_config = None
|
| 270 |
if args.model_arch == "causal":
|
| 271 |
+
try:
|
| 272 |
+
import bitsandbytes as bnb
|
| 273 |
quant_config = BitsAndBytesConfig(
|
| 274 |
+
load_in_4bit=True,
|
| 275 |
+
bnb_4bit_quant_type="nf4",
|
| 276 |
+
bnb_4bit_compute_dtype=torch.float16,
|
| 277 |
+
bnb_4bit_use_double_quant=True,
|
| 278 |
+
)
|
| 279 |
+
use_quantization = True
|
| 280 |
+
print("4-bit quantization enabled")
|
| 281 |
+
except (ImportError, ModuleNotFoundError) as e:
|
| 282 |
+
print(f"Warning: BitsAndBytesConfig not available ({e}), loading model without quantization...")
|
| 283 |
|
| 284 |
model = None
|
| 285 |
last_error = None
|
|
|
|
| 313 |
if use_quantization:
|
| 314 |
try:
|
| 315 |
model = load_base_model(use_quant=True)
|
| 316 |
+
print("Model loaded with 4-bit quantization")
|
| 317 |
+
except Exception as e:
|
| 318 |
+
last_error = e
|
| 319 |
+
print(f"Failed to load with quantization: {e}")
|
| 320 |
model = None
|
| 321 |
+
if model is None:
|
| 322 |
+
try:
|
| 323 |
model = load_base_model(use_quant=False)
|
| 324 |
+
print("Model loaded without quantization (may use more memory)")
|
| 325 |
except Exception as e:
|
| 326 |
if last_error:
|
| 327 |
print(f"Original error: {last_error}")
|
|
|
|
| 356 |
|
| 357 |
# Configure LoRA
|
| 358 |
if args.model_arch == "causal":
|
| 359 |
+
lora_config = LoraConfig(
|
| 360 |
+
r=16,
|
| 361 |
+
lora_alpha=32,
|
| 362 |
+
target_modules=["q_proj", "v_proj", "k_proj", "o_proj", "gate_proj", "up_proj", "down_proj"],
|
| 363 |
+
lora_dropout=0.05,
|
| 364 |
+
bias="none",
|
| 365 |
+
task_type="CAUSAL_LM",
|
| 366 |
+
)
|
| 367 |
else:
|
| 368 |
lora_config = LoraConfig(
|
| 369 |
r=16,
|
|
|
|
| 416 |
training_args = TrainingArguments(
|
| 417 |
eval_strategy="steps",
|
| 418 |
**training_common_kwargs,
|
| 419 |
+
)
|
| 420 |
|
| 421 |
# Create Trainer
|
| 422 |
trainer = Trainer(
|
finetuned_reply_service.py
CHANGED
|
@@ -86,12 +86,12 @@ class FinetunedReplyService:
|
|
| 86 |
|
| 87 |
if self.model_arch == "causal" and torch.cuda.is_available():
|
| 88 |
try:
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
quant_kwargs["quantization_config"] = bnb_config
|
| 96 |
except Exception as exc:
|
| 97 |
print(f"Warning: Could not enable 4-bit quantization ({exc}). Falling back to full precision.")
|
|
@@ -100,7 +100,7 @@ class FinetunedReplyService:
|
|
| 100 |
if self.model_arch == "encoder_decoder":
|
| 101 |
model = EncoderDecoderModel.from_encoder_decoder_pretrained(
|
| 102 |
self.base_model_name,
|
| 103 |
-
|
| 104 |
tie_encoder_decoder=True,
|
| 105 |
)
|
| 106 |
model.config.decoder_start_token_id = getattr(self.tokenizer, "bos_token_id", self.tokenizer.cls_token_id)
|
|
@@ -108,7 +108,7 @@ class FinetunedReplyService:
|
|
| 108 |
model.config.vocab_size = model.config.encoder.vocab_size
|
| 109 |
return model.to(self.device)
|
| 110 |
kwargs = dict(
|
| 111 |
-
|
| 112 |
torch_dtype=dtype,
|
| 113 |
device_map=device_map,
|
| 114 |
token=self.api_token,
|
|
@@ -130,8 +130,8 @@ class FinetunedReplyService:
|
|
| 130 |
has_lora = os.path.exists(adapter_config)
|
| 131 |
if has_lora:
|
| 132 |
try:
|
| 133 |
-
|
| 134 |
-
|
| 135 |
except FileNotFoundError as exc:
|
| 136 |
print(f"Adapter files incomplete ({exc}). Falling back to base model.")
|
| 137 |
self.model = base_model
|
|
@@ -185,7 +185,7 @@ class FinetunedReplyService:
|
|
| 185 |
attention_mask=inputs.get("attention_mask"),
|
| 186 |
)
|
| 187 |
else:
|
| 188 |
-
|
| 189 |
generate_kwargs = dict(**inputs)
|
| 190 |
|
| 191 |
with torch.no_grad():
|
|
|
|
| 86 |
|
| 87 |
if self.model_arch == "causal" and torch.cuda.is_available():
|
| 88 |
try:
|
| 89 |
+
bnb_config = BitsAndBytesConfig(
|
| 90 |
+
load_in_4bit=True,
|
| 91 |
+
bnb_4bit_quant_type="nf4",
|
| 92 |
+
bnb_4bit_compute_dtype=torch.float16,
|
| 93 |
+
bnb_4bit_use_double_quant=True,
|
| 94 |
+
)
|
| 95 |
quant_kwargs["quantization_config"] = bnb_config
|
| 96 |
except Exception as exc:
|
| 97 |
print(f"Warning: Could not enable 4-bit quantization ({exc}). Falling back to full precision.")
|
|
|
|
| 100 |
if self.model_arch == "encoder_decoder":
|
| 101 |
model = EncoderDecoderModel.from_encoder_decoder_pretrained(
|
| 102 |
self.base_model_name,
|
| 103 |
+
self.base_model_name,
|
| 104 |
tie_encoder_decoder=True,
|
| 105 |
)
|
| 106 |
model.config.decoder_start_token_id = getattr(self.tokenizer, "bos_token_id", self.tokenizer.cls_token_id)
|
|
|
|
| 108 |
model.config.vocab_size = model.config.encoder.vocab_size
|
| 109 |
return model.to(self.device)
|
| 110 |
kwargs = dict(
|
| 111 |
+
trust_remote_code=True,
|
| 112 |
torch_dtype=dtype,
|
| 113 |
device_map=device_map,
|
| 114 |
token=self.api_token,
|
|
|
|
| 130 |
has_lora = os.path.exists(adapter_config)
|
| 131 |
if has_lora:
|
| 132 |
try:
|
| 133 |
+
print(f"Loading fine-tuned weights from {self.finetuned_model_path}")
|
| 134 |
+
self.model = PeftModel.from_pretrained(base_model, self.finetuned_model_path)
|
| 135 |
except FileNotFoundError as exc:
|
| 136 |
print(f"Adapter files incomplete ({exc}). Falling back to base model.")
|
| 137 |
self.model = base_model
|
|
|
|
| 185 |
attention_mask=inputs.get("attention_mask"),
|
| 186 |
)
|
| 187 |
else:
|
| 188 |
+
inputs = self.tokenizer(prompt, return_tensors="pt").to(self.device)
|
| 189 |
generate_kwargs = dict(**inputs)
|
| 190 |
|
| 191 |
with torch.no_grad():
|
openai_models_cache.json
ADDED
|
@@ -0,0 +1,512 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
{
|
| 3 |
+
"id": "gpt-4-0613",
|
| 4 |
+
"created": 1686588896,
|
| 5 |
+
"owned_by": "openai"
|
| 6 |
+
},
|
| 7 |
+
{
|
| 8 |
+
"id": "gpt-4",
|
| 9 |
+
"created": 1687882411,
|
| 10 |
+
"owned_by": "openai"
|
| 11 |
+
},
|
| 12 |
+
{
|
| 13 |
+
"id": "gpt-3.5-turbo",
|
| 14 |
+
"created": 1677610602,
|
| 15 |
+
"owned_by": "openai"
|
| 16 |
+
},
|
| 17 |
+
{
|
| 18 |
+
"id": "gpt-5.1-codex-mini",
|
| 19 |
+
"created": 1763007109,
|
| 20 |
+
"owned_by": "system"
|
| 21 |
+
},
|
| 22 |
+
{
|
| 23 |
+
"id": "gpt-5.1-chat-latest",
|
| 24 |
+
"created": 1762547951,
|
| 25 |
+
"owned_by": "system"
|
| 26 |
+
},
|
| 27 |
+
{
|
| 28 |
+
"id": "gpt-5.1-2025-11-13",
|
| 29 |
+
"created": 1762800353,
|
| 30 |
+
"owned_by": "system"
|
| 31 |
+
},
|
| 32 |
+
{
|
| 33 |
+
"id": "gpt-5.1",
|
| 34 |
+
"created": 1762800673,
|
| 35 |
+
"owned_by": "system"
|
| 36 |
+
},
|
| 37 |
+
{
|
| 38 |
+
"id": "gpt-5.1-codex",
|
| 39 |
+
"created": 1762988221,
|
| 40 |
+
"owned_by": "system"
|
| 41 |
+
},
|
| 42 |
+
{
|
| 43 |
+
"id": "davinci-002",
|
| 44 |
+
"created": 1692634301,
|
| 45 |
+
"owned_by": "system"
|
| 46 |
+
},
|
| 47 |
+
{
|
| 48 |
+
"id": "babbage-002",
|
| 49 |
+
"created": 1692634615,
|
| 50 |
+
"owned_by": "system"
|
| 51 |
+
},
|
| 52 |
+
{
|
| 53 |
+
"id": "gpt-3.5-turbo-instruct",
|
| 54 |
+
"created": 1692901427,
|
| 55 |
+
"owned_by": "system"
|
| 56 |
+
},
|
| 57 |
+
{
|
| 58 |
+
"id": "gpt-3.5-turbo-instruct-0914",
|
| 59 |
+
"created": 1694122472,
|
| 60 |
+
"owned_by": "system"
|
| 61 |
+
},
|
| 62 |
+
{
|
| 63 |
+
"id": "dall-e-3",
|
| 64 |
+
"created": 1698785189,
|
| 65 |
+
"owned_by": "system"
|
| 66 |
+
},
|
| 67 |
+
{
|
| 68 |
+
"id": "dall-e-2",
|
| 69 |
+
"created": 1698798177,
|
| 70 |
+
"owned_by": "system"
|
| 71 |
+
},
|
| 72 |
+
{
|
| 73 |
+
"id": "gpt-4-1106-preview",
|
| 74 |
+
"created": 1698957206,
|
| 75 |
+
"owned_by": "system"
|
| 76 |
+
},
|
| 77 |
+
{
|
| 78 |
+
"id": "gpt-3.5-turbo-1106",
|
| 79 |
+
"created": 1698959748,
|
| 80 |
+
"owned_by": "system"
|
| 81 |
+
},
|
| 82 |
+
{
|
| 83 |
+
"id": "tts-1-hd",
|
| 84 |
+
"created": 1699046015,
|
| 85 |
+
"owned_by": "system"
|
| 86 |
+
},
|
| 87 |
+
{
|
| 88 |
+
"id": "tts-1-1106",
|
| 89 |
+
"created": 1699053241,
|
| 90 |
+
"owned_by": "system"
|
| 91 |
+
},
|
| 92 |
+
{
|
| 93 |
+
"id": "tts-1-hd-1106",
|
| 94 |
+
"created": 1699053533,
|
| 95 |
+
"owned_by": "system"
|
| 96 |
+
},
|
| 97 |
+
{
|
| 98 |
+
"id": "text-embedding-3-small",
|
| 99 |
+
"created": 1705948997,
|
| 100 |
+
"owned_by": "system"
|
| 101 |
+
},
|
| 102 |
+
{
|
| 103 |
+
"id": "text-embedding-3-large",
|
| 104 |
+
"created": 1705953180,
|
| 105 |
+
"owned_by": "system"
|
| 106 |
+
},
|
| 107 |
+
{
|
| 108 |
+
"id": "gpt-4-0125-preview",
|
| 109 |
+
"created": 1706037612,
|
| 110 |
+
"owned_by": "system"
|
| 111 |
+
},
|
| 112 |
+
{
|
| 113 |
+
"id": "gpt-4-turbo-preview",
|
| 114 |
+
"created": 1706037777,
|
| 115 |
+
"owned_by": "system"
|
| 116 |
+
},
|
| 117 |
+
{
|
| 118 |
+
"id": "gpt-3.5-turbo-0125",
|
| 119 |
+
"created": 1706048358,
|
| 120 |
+
"owned_by": "system"
|
| 121 |
+
},
|
| 122 |
+
{
|
| 123 |
+
"id": "gpt-4-turbo",
|
| 124 |
+
"created": 1712361441,
|
| 125 |
+
"owned_by": "system"
|
| 126 |
+
},
|
| 127 |
+
{
|
| 128 |
+
"id": "gpt-4-turbo-2024-04-09",
|
| 129 |
+
"created": 1712601677,
|
| 130 |
+
"owned_by": "system"
|
| 131 |
+
},
|
| 132 |
+
{
|
| 133 |
+
"id": "gpt-4o",
|
| 134 |
+
"created": 1715367049,
|
| 135 |
+
"owned_by": "system"
|
| 136 |
+
},
|
| 137 |
+
{
|
| 138 |
+
"id": "gpt-4o-2024-05-13",
|
| 139 |
+
"created": 1715368132,
|
| 140 |
+
"owned_by": "system"
|
| 141 |
+
},
|
| 142 |
+
{
|
| 143 |
+
"id": "gpt-4o-mini-2024-07-18",
|
| 144 |
+
"created": 1721172717,
|
| 145 |
+
"owned_by": "system"
|
| 146 |
+
},
|
| 147 |
+
{
|
| 148 |
+
"id": "gpt-4o-mini",
|
| 149 |
+
"created": 1721172741,
|
| 150 |
+
"owned_by": "system"
|
| 151 |
+
},
|
| 152 |
+
{
|
| 153 |
+
"id": "gpt-4o-2024-08-06",
|
| 154 |
+
"created": 1722814719,
|
| 155 |
+
"owned_by": "system"
|
| 156 |
+
},
|
| 157 |
+
{
|
| 158 |
+
"id": "chatgpt-4o-latest",
|
| 159 |
+
"created": 1723515131,
|
| 160 |
+
"owned_by": "system"
|
| 161 |
+
},
|
| 162 |
+
{
|
| 163 |
+
"id": "gpt-4o-realtime-preview-2024-10-01",
|
| 164 |
+
"created": 1727131766,
|
| 165 |
+
"owned_by": "system"
|
| 166 |
+
},
|
| 167 |
+
{
|
| 168 |
+
"id": "gpt-4o-audio-preview-2024-10-01",
|
| 169 |
+
"created": 1727389042,
|
| 170 |
+
"owned_by": "system"
|
| 171 |
+
},
|
| 172 |
+
{
|
| 173 |
+
"id": "gpt-4o-audio-preview",
|
| 174 |
+
"created": 1727460443,
|
| 175 |
+
"owned_by": "system"
|
| 176 |
+
},
|
| 177 |
+
{
|
| 178 |
+
"id": "gpt-4o-realtime-preview",
|
| 179 |
+
"created": 1727659998,
|
| 180 |
+
"owned_by": "system"
|
| 181 |
+
},
|
| 182 |
+
{
|
| 183 |
+
"id": "omni-moderation-latest",
|
| 184 |
+
"created": 1731689265,
|
| 185 |
+
"owned_by": "system"
|
| 186 |
+
},
|
| 187 |
+
{
|
| 188 |
+
"id": "omni-moderation-2024-09-26",
|
| 189 |
+
"created": 1732734466,
|
| 190 |
+
"owned_by": "system"
|
| 191 |
+
},
|
| 192 |
+
{
|
| 193 |
+
"id": "gpt-4o-realtime-preview-2024-12-17",
|
| 194 |
+
"created": 1733945430,
|
| 195 |
+
"owned_by": "system"
|
| 196 |
+
},
|
| 197 |
+
{
|
| 198 |
+
"id": "gpt-4o-audio-preview-2024-12-17",
|
| 199 |
+
"created": 1734034239,
|
| 200 |
+
"owned_by": "system"
|
| 201 |
+
},
|
| 202 |
+
{
|
| 203 |
+
"id": "gpt-4o-mini-realtime-preview-2024-12-17",
|
| 204 |
+
"created": 1734112601,
|
| 205 |
+
"owned_by": "system"
|
| 206 |
+
},
|
| 207 |
+
{
|
| 208 |
+
"id": "gpt-4o-mini-audio-preview-2024-12-17",
|
| 209 |
+
"created": 1734115920,
|
| 210 |
+
"owned_by": "system"
|
| 211 |
+
},
|
| 212 |
+
{
|
| 213 |
+
"id": "o1-2024-12-17",
|
| 214 |
+
"created": 1734326976,
|
| 215 |
+
"owned_by": "system"
|
| 216 |
+
},
|
| 217 |
+
{
|
| 218 |
+
"id": "o1",
|
| 219 |
+
"created": 1734375816,
|
| 220 |
+
"owned_by": "system"
|
| 221 |
+
},
|
| 222 |
+
{
|
| 223 |
+
"id": "gpt-4o-mini-realtime-preview",
|
| 224 |
+
"created": 1734387380,
|
| 225 |
+
"owned_by": "system"
|
| 226 |
+
},
|
| 227 |
+
{
|
| 228 |
+
"id": "gpt-4o-mini-audio-preview",
|
| 229 |
+
"created": 1734387424,
|
| 230 |
+
"owned_by": "system"
|
| 231 |
+
},
|
| 232 |
+
{
|
| 233 |
+
"id": "o3-mini",
|
| 234 |
+
"created": 1737146383,
|
| 235 |
+
"owned_by": "system"
|
| 236 |
+
},
|
| 237 |
+
{
|
| 238 |
+
"id": "o3-mini-2025-01-31",
|
| 239 |
+
"created": 1738010200,
|
| 240 |
+
"owned_by": "system"
|
| 241 |
+
},
|
| 242 |
+
{
|
| 243 |
+
"id": "gpt-4o-2024-11-20",
|
| 244 |
+
"created": 1739331543,
|
| 245 |
+
"owned_by": "system"
|
| 246 |
+
},
|
| 247 |
+
{
|
| 248 |
+
"id": "gpt-4o-search-preview-2025-03-11",
|
| 249 |
+
"created": 1741388170,
|
| 250 |
+
"owned_by": "system"
|
| 251 |
+
},
|
| 252 |
+
{
|
| 253 |
+
"id": "gpt-4o-search-preview",
|
| 254 |
+
"created": 1741388720,
|
| 255 |
+
"owned_by": "system"
|
| 256 |
+
},
|
| 257 |
+
{
|
| 258 |
+
"id": "gpt-4o-mini-search-preview-2025-03-11",
|
| 259 |
+
"created": 1741390858,
|
| 260 |
+
"owned_by": "system"
|
| 261 |
+
},
|
| 262 |
+
{
|
| 263 |
+
"id": "gpt-4o-mini-search-preview",
|
| 264 |
+
"created": 1741391161,
|
| 265 |
+
"owned_by": "system"
|
| 266 |
+
},
|
| 267 |
+
{
|
| 268 |
+
"id": "gpt-4o-transcribe",
|
| 269 |
+
"created": 1742068463,
|
| 270 |
+
"owned_by": "system"
|
| 271 |
+
},
|
| 272 |
+
{
|
| 273 |
+
"id": "gpt-4o-mini-transcribe",
|
| 274 |
+
"created": 1742068596,
|
| 275 |
+
"owned_by": "system"
|
| 276 |
+
},
|
| 277 |
+
{
|
| 278 |
+
"id": "o1-pro-2025-03-19",
|
| 279 |
+
"created": 1742251504,
|
| 280 |
+
"owned_by": "system"
|
| 281 |
+
},
|
| 282 |
+
{
|
| 283 |
+
"id": "o1-pro",
|
| 284 |
+
"created": 1742251791,
|
| 285 |
+
"owned_by": "system"
|
| 286 |
+
},
|
| 287 |
+
{
|
| 288 |
+
"id": "gpt-4o-mini-tts",
|
| 289 |
+
"created": 1742403959,
|
| 290 |
+
"owned_by": "system"
|
| 291 |
+
},
|
| 292 |
+
{
|
| 293 |
+
"id": "o3-2025-04-16",
|
| 294 |
+
"created": 1744133301,
|
| 295 |
+
"owned_by": "system"
|
| 296 |
+
},
|
| 297 |
+
{
|
| 298 |
+
"id": "o4-mini-2025-04-16",
|
| 299 |
+
"created": 1744133506,
|
| 300 |
+
"owned_by": "system"
|
| 301 |
+
},
|
| 302 |
+
{
|
| 303 |
+
"id": "o3",
|
| 304 |
+
"created": 1744225308,
|
| 305 |
+
"owned_by": "system"
|
| 306 |
+
},
|
| 307 |
+
{
|
| 308 |
+
"id": "o4-mini",
|
| 309 |
+
"created": 1744225351,
|
| 310 |
+
"owned_by": "system"
|
| 311 |
+
},
|
| 312 |
+
{
|
| 313 |
+
"id": "gpt-4.1-2025-04-14",
|
| 314 |
+
"created": 1744315746,
|
| 315 |
+
"owned_by": "system"
|
| 316 |
+
},
|
| 317 |
+
{
|
| 318 |
+
"id": "gpt-4.1",
|
| 319 |
+
"created": 1744316542,
|
| 320 |
+
"owned_by": "system"
|
| 321 |
+
},
|
| 322 |
+
{
|
| 323 |
+
"id": "gpt-4.1-mini-2025-04-14",
|
| 324 |
+
"created": 1744317547,
|
| 325 |
+
"owned_by": "system"
|
| 326 |
+
},
|
| 327 |
+
{
|
| 328 |
+
"id": "gpt-4.1-mini",
|
| 329 |
+
"created": 1744318173,
|
| 330 |
+
"owned_by": "system"
|
| 331 |
+
},
|
| 332 |
+
{
|
| 333 |
+
"id": "gpt-4.1-nano-2025-04-14",
|
| 334 |
+
"created": 1744321025,
|
| 335 |
+
"owned_by": "system"
|
| 336 |
+
},
|
| 337 |
+
{
|
| 338 |
+
"id": "gpt-4.1-nano",
|
| 339 |
+
"created": 1744321707,
|
| 340 |
+
"owned_by": "system"
|
| 341 |
+
},
|
| 342 |
+
{
|
| 343 |
+
"id": "gpt-image-1",
|
| 344 |
+
"created": 1745517030,
|
| 345 |
+
"owned_by": "system"
|
| 346 |
+
},
|
| 347 |
+
{
|
| 348 |
+
"id": "codex-mini-latest",
|
| 349 |
+
"created": 1746673257,
|
| 350 |
+
"owned_by": "system"
|
| 351 |
+
},
|
| 352 |
+
{
|
| 353 |
+
"id": "gpt-4o-realtime-preview-2025-06-03",
|
| 354 |
+
"created": 1748907838,
|
| 355 |
+
"owned_by": "system"
|
| 356 |
+
},
|
| 357 |
+
{
|
| 358 |
+
"id": "gpt-4o-audio-preview-2025-06-03",
|
| 359 |
+
"created": 1748908498,
|
| 360 |
+
"owned_by": "system"
|
| 361 |
+
},
|
| 362 |
+
{
|
| 363 |
+
"id": "o4-mini-deep-research",
|
| 364 |
+
"created": 1749685485,
|
| 365 |
+
"owned_by": "system"
|
| 366 |
+
},
|
| 367 |
+
{
|
| 368 |
+
"id": "gpt-4o-transcribe-diarize",
|
| 369 |
+
"created": 1750798887,
|
| 370 |
+
"owned_by": "system"
|
| 371 |
+
},
|
| 372 |
+
{
|
| 373 |
+
"id": "o4-mini-deep-research-2025-06-26",
|
| 374 |
+
"created": 1750866121,
|
| 375 |
+
"owned_by": "system"
|
| 376 |
+
},
|
| 377 |
+
{
|
| 378 |
+
"id": "gpt-5-chat-latest",
|
| 379 |
+
"created": 1754073306,
|
| 380 |
+
"owned_by": "system"
|
| 381 |
+
},
|
| 382 |
+
{
|
| 383 |
+
"id": "gpt-5-2025-08-07",
|
| 384 |
+
"created": 1754075360,
|
| 385 |
+
"owned_by": "system"
|
| 386 |
+
},
|
| 387 |
+
{
|
| 388 |
+
"id": "gpt-5",
|
| 389 |
+
"created": 1754425777,
|
| 390 |
+
"owned_by": "system"
|
| 391 |
+
},
|
| 392 |
+
{
|
| 393 |
+
"id": "gpt-5-mini-2025-08-07",
|
| 394 |
+
"created": 1754425867,
|
| 395 |
+
"owned_by": "system"
|
| 396 |
+
},
|
| 397 |
+
{
|
| 398 |
+
"id": "gpt-5-mini",
|
| 399 |
+
"created": 1754425928,
|
| 400 |
+
"owned_by": "system"
|
| 401 |
+
},
|
| 402 |
+
{
|
| 403 |
+
"id": "gpt-5-nano-2025-08-07",
|
| 404 |
+
"created": 1754426303,
|
| 405 |
+
"owned_by": "system"
|
| 406 |
+
},
|
| 407 |
+
{
|
| 408 |
+
"id": "gpt-5-nano",
|
| 409 |
+
"created": 1754426384,
|
| 410 |
+
"owned_by": "system"
|
| 411 |
+
},
|
| 412 |
+
{
|
| 413 |
+
"id": "gpt-audio-2025-08-28",
|
| 414 |
+
"created": 1756256146,
|
| 415 |
+
"owned_by": "system"
|
| 416 |
+
},
|
| 417 |
+
{
|
| 418 |
+
"id": "gpt-realtime",
|
| 419 |
+
"created": 1756271701,
|
| 420 |
+
"owned_by": "system"
|
| 421 |
+
},
|
| 422 |
+
{
|
| 423 |
+
"id": "gpt-realtime-2025-08-28",
|
| 424 |
+
"created": 1756271773,
|
| 425 |
+
"owned_by": "system"
|
| 426 |
+
},
|
| 427 |
+
{
|
| 428 |
+
"id": "gpt-audio",
|
| 429 |
+
"created": 1756339249,
|
| 430 |
+
"owned_by": "system"
|
| 431 |
+
},
|
| 432 |
+
{
|
| 433 |
+
"id": "gpt-5-codex",
|
| 434 |
+
"created": 1757527818,
|
| 435 |
+
"owned_by": "system"
|
| 436 |
+
},
|
| 437 |
+
{
|
| 438 |
+
"id": "gpt-image-1-mini",
|
| 439 |
+
"created": 1758845821,
|
| 440 |
+
"owned_by": "system"
|
| 441 |
+
},
|
| 442 |
+
{
|
| 443 |
+
"id": "gpt-5-pro-2025-10-06",
|
| 444 |
+
"created": 1759469707,
|
| 445 |
+
"owned_by": "system"
|
| 446 |
+
},
|
| 447 |
+
{
|
| 448 |
+
"id": "gpt-5-pro",
|
| 449 |
+
"created": 1759469822,
|
| 450 |
+
"owned_by": "system"
|
| 451 |
+
},
|
| 452 |
+
{
|
| 453 |
+
"id": "gpt-audio-mini",
|
| 454 |
+
"created": 1759512027,
|
| 455 |
+
"owned_by": "system"
|
| 456 |
+
},
|
| 457 |
+
{
|
| 458 |
+
"id": "gpt-audio-mini-2025-10-06",
|
| 459 |
+
"created": 1759512137,
|
| 460 |
+
"owned_by": "system"
|
| 461 |
+
},
|
| 462 |
+
{
|
| 463 |
+
"id": "gpt-5-search-api",
|
| 464 |
+
"created": 1759514629,
|
| 465 |
+
"owned_by": "system"
|
| 466 |
+
},
|
| 467 |
+
{
|
| 468 |
+
"id": "gpt-realtime-mini",
|
| 469 |
+
"created": 1759517133,
|
| 470 |
+
"owned_by": "system"
|
| 471 |
+
},
|
| 472 |
+
{
|
| 473 |
+
"id": "gpt-realtime-mini-2025-10-06",
|
| 474 |
+
"created": 1759517175,
|
| 475 |
+
"owned_by": "system"
|
| 476 |
+
},
|
| 477 |
+
{
|
| 478 |
+
"id": "sora-2",
|
| 479 |
+
"created": 1759708615,
|
| 480 |
+
"owned_by": "system"
|
| 481 |
+
},
|
| 482 |
+
{
|
| 483 |
+
"id": "sora-2-pro",
|
| 484 |
+
"created": 1759708663,
|
| 485 |
+
"owned_by": "system"
|
| 486 |
+
},
|
| 487 |
+
{
|
| 488 |
+
"id": "gpt-5-search-api-2025-10-14",
|
| 489 |
+
"created": 1760043960,
|
| 490 |
+
"owned_by": "system"
|
| 491 |
+
},
|
| 492 |
+
{
|
| 493 |
+
"id": "gpt-3.5-turbo-16k",
|
| 494 |
+
"created": 1683758102,
|
| 495 |
+
"owned_by": "openai-internal"
|
| 496 |
+
},
|
| 497 |
+
{
|
| 498 |
+
"id": "tts-1",
|
| 499 |
+
"created": 1681940951,
|
| 500 |
+
"owned_by": "openai-internal"
|
| 501 |
+
},
|
| 502 |
+
{
|
| 503 |
+
"id": "whisper-1",
|
| 504 |
+
"created": 1677532384,
|
| 505 |
+
"owned_by": "openai-internal"
|
| 506 |
+
},
|
| 507 |
+
{
|
| 508 |
+
"id": "text-embedding-ada-002",
|
| 509 |
+
"created": 1671217299,
|
| 510 |
+
"owned_by": "openai-internal"
|
| 511 |
+
}
|
| 512 |
+
]
|
openai_service.py
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Service for generating replies using OpenAI API.
|
| 3 |
+
"""
|
| 4 |
+
import os
|
| 5 |
+
import json
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
from typing import Optional, List, Dict, Any
|
| 8 |
+
from openai import OpenAI
|
| 9 |
+
|
| 10 |
+
# Reuse the same system prompt from Perplexity service
|
| 11 |
+
SYSTEM_PROMPT = """
|
| 12 |
+
Bạn là một wingman AI tinh tế, chuyên giúp Nam soạn 1 tin nhắn trả lời duy nhất trong hội thoại hẹn hò tiếng Việt. Bạn luôn nhìn từ góc nhìn của Nam, xưng "anh" và gọi đối phương là "em".
|
| 13 |
+
|
| 14 |
+
Bạn được cung cấp:
|
| 15 |
+
|
| 16 |
+
- HỘI THOẠI: đoạn hội thoại gần nhất giữa Nam (Male) và Nữ (Female), phân tách các tin bằng ký hiệu "|||".
|
| 17 |
+
|
| 18 |
+
- TRIGGER: intent hiện tại (ví dụ: neutral, positive, negative, confused...).
|
| 19 |
+
|
| 20 |
+
- MOVE: chiến lược hiện tại (ví dụ: escalate, hold, de-escalate, tease, comfort...).
|
| 21 |
+
|
| 22 |
+
Nhiệm vụ của bạn:
|
| 23 |
+
|
| 24 |
+
- Dựa trên HỘI THOẠI + TRIGGER + MOVE, hãy chọn một hướng phản hồi tự nhiên, duyên dáng, đúng chiến lược (không quá đẩy hay quá lùi so với MOVE).
|
| 25 |
+
|
| 26 |
+
- Ưu tiên giữ mạch cảm xúc nhất quán với hội thoại, tránh tạo thông tin fact mới về thế giới bên ngoài hoặc về hai người.
|
| 27 |
+
|
| 28 |
+
QUY TẮC CỨNG:
|
| 29 |
+
|
| 30 |
+
- Chỉ trả về đúng 1 câu duy nhất.
|
| 31 |
+
|
| 32 |
+
- Tối đa 25 từ tiếng Việt.
|
| 33 |
+
|
| 34 |
+
- Lịch sự, ấm áp, thân thiện; không phán xét, không thô lỗ.
|
| 35 |
+
|
| 36 |
+
- Không giải thích meta (không nói về "prompt", "AI", "chiến lược", "MOVE", "TRIGGER"...).
|
| 37 |
+
|
| 38 |
+
- Không lặp lại nguyên văn câu của đối phương.
|
| 39 |
+
|
| 40 |
+
- Không thêm fact mới (chỉ dựa trên những gì có trong hội thoại, hoặc các câu nói chung chung, không cụ thể hóa thông tin chưa có).
|
| 41 |
+
|
| 42 |
+
Khi TRIGGER hoặc MOVE có vẻ mâu thuẫn với HỘI THOẠI:
|
| 43 |
+
|
| 44 |
+
- Hãy ưu tiên sự an toàn và mềm mại.
|
| 45 |
+
|
| 46 |
+
- Có thể hỏi lại nhẹ nhàng để làm rõ, nhưng vẫn giữ frame chủ động, tự tin của Nam.
|
| 47 |
+
|
| 48 |
+
PHONG CÁCH:
|
| 49 |
+
|
| 50 |
+
- Ấm áp, tự tin nhưng không tự cao.
|
| 51 |
+
|
| 52 |
+
- Có thể dùng từ đệm tự nhiên (nha, nhé, ạ, dạ) khi phù hợp với ngữ cảnh.
|
| 53 |
+
|
| 54 |
+
- Phản chiếu cảm xúc của đối phương.
|
| 55 |
+
|
| 56 |
+
- Giữ mạch trò chuyện mở để còn đất tăng tương tác về sau.
|
| 57 |
+
|
| 58 |
+
Nếu vì bất kỳ lý do gì bạn không thể tuân thủ tất cả quy tắc trên:
|
| 59 |
+
|
| 60 |
+
- Hãy ưu tiên vẫn trả về đúng 1 câu, ≤25 từ, không chứa meta, không chứa thông tin fact mới.
|
| 61 |
+
""".strip()
|
| 62 |
+
|
| 63 |
+
MODELS_CACHE_PATH = Path(__file__).resolve().parent / "config" / "ai_models.json"
|
| 64 |
+
ENV = os.getenv("APP_ENV", "development").lower()
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
def get_openai_client() -> OpenAI:
|
| 68 |
+
"""Get OpenAI client with API key from environment."""
|
| 69 |
+
api_key = os.getenv("OPENAI_API_KEY")
|
| 70 |
+
if not api_key:
|
| 71 |
+
raise ValueError(
|
| 72 |
+
"OpenAI API key is required.\n\n"
|
| 73 |
+
"Set environment variable:\n"
|
| 74 |
+
" export OPENAI_API_KEY=sk-...\n\n"
|
| 75 |
+
"Or add to .env file."
|
| 76 |
+
)
|
| 77 |
+
return OpenAI(api_key=api_key)
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
def fetch_and_cache_models_if_needed() -> List[Dict[str, Any]]:
|
| 81 |
+
"""
|
| 82 |
+
Local/dev:
|
| 83 |
+
- If cache file does not exist -> call OpenAI models.list() and save to JSON
|
| 84 |
+
- If cache file exists -> just read
|
| 85 |
+
Production:
|
| 86 |
+
- Never call OpenAI models.list(), only read from JSON
|
| 87 |
+
"""
|
| 88 |
+
# If cache exists, always use it
|
| 89 |
+
if MODELS_CACHE_PATH.exists():
|
| 90 |
+
with MODELS_CACHE_PATH.open("r", encoding="utf-8") as f:
|
| 91 |
+
return json.load(f)
|
| 92 |
+
|
| 93 |
+
# If production and cache missing: fail fast
|
| 94 |
+
if ENV in ("production", "prod"):
|
| 95 |
+
raise RuntimeError(
|
| 96 |
+
f"Models cache not found at {MODELS_CACHE_PATH}. "
|
| 97 |
+
f"Generate it in development before deploying."
|
| 98 |
+
)
|
| 99 |
+
|
| 100 |
+
# Dev/test: call OpenAI and write cache
|
| 101 |
+
client = get_openai_client()
|
| 102 |
+
models = client.models.list() # returns a ModelList object
|
| 103 |
+
data = []
|
| 104 |
+
for m in models.data:
|
| 105 |
+
data.append(
|
| 106 |
+
{
|
| 107 |
+
"id": m.id,
|
| 108 |
+
"created": getattr(m, "created", None),
|
| 109 |
+
"owned_by": getattr(m, "owned_by", None),
|
| 110 |
+
}
|
| 111 |
+
)
|
| 112 |
+
MODELS_CACHE_PATH.parent.mkdir(parents=True, exist_ok=True)
|
| 113 |
+
with MODELS_CACHE_PATH.open("w", encoding="utf-8") as f:
|
| 114 |
+
json.dump(data, f, ensure_ascii=False, indent=2)
|
| 115 |
+
return data
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
def get_available_models(prefix_filters: Optional[List[str]] = None) -> List[str]:
|
| 119 |
+
"""
|
| 120 |
+
Return list of model ids from cache (generate cache in dev if needed).
|
| 121 |
+
Optional: filter by prefix, e.g. ["gpt-4", "gpt-3.5"].
|
| 122 |
+
"""
|
| 123 |
+
models = fetch_and_cache_models_if_needed()
|
| 124 |
+
ids = [m["id"] for m in models]
|
| 125 |
+
if prefix_filters:
|
| 126 |
+
filtered = []
|
| 127 |
+
for mid in ids:
|
| 128 |
+
if any(mid.startswith(p) for p in prefix_filters):
|
| 129 |
+
filtered.append(mid)
|
| 130 |
+
return filtered
|
| 131 |
+
return ids
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
def create_chat_response(
|
| 135 |
+
model: str,
|
| 136 |
+
user_message: str,
|
| 137 |
+
) -> str:
|
| 138 |
+
"""
|
| 139 |
+
Use chosen model to create a simple text response via Responses API.
|
| 140 |
+
"""
|
| 141 |
+
client = get_openai_client()
|
| 142 |
+
resp = client.responses.create(
|
| 143 |
+
model=model,
|
| 144 |
+
input=user_message,
|
| 145 |
+
)
|
| 146 |
+
# Responses API trả về output dạng structured
|
| 147 |
+
# resp.output[0].content[0].text là string trực tiếp
|
| 148 |
+
if resp.output and len(resp.output) > 0:
|
| 149 |
+
first_output = resp.output[0]
|
| 150 |
+
if first_output.content and len(first_output.content) > 0:
|
| 151 |
+
first_content = first_output.content[0]
|
| 152 |
+
if hasattr(first_content, 'text'):
|
| 153 |
+
text = first_content.text
|
| 154 |
+
# text là string trực tiếp
|
| 155 |
+
return str(text) if text else ""
|
| 156 |
+
|
| 157 |
+
# Fallback: use output_text if available
|
| 158 |
+
if hasattr(resp, 'output_text') and resp.output_text:
|
| 159 |
+
return str(resp.output_text)
|
| 160 |
+
|
| 161 |
+
raise ValueError("No text found in response")
|
| 162 |
+
|
| 163 |
+
|
| 164 |
+
class OpenAIReplyService:
|
| 165 |
+
"""Service for generating replies using OpenAI API."""
|
| 166 |
+
|
| 167 |
+
def __init__(self, api_key: Optional[str] = None, model_name: str = "gpt-4o-mini"):
|
| 168 |
+
"""
|
| 169 |
+
Initialize OpenAI service.
|
| 170 |
+
|
| 171 |
+
Args:
|
| 172 |
+
api_key: OpenAI API key. If None, will try to get from OPENAI_API_KEY env var.
|
| 173 |
+
model_name: Model name to use (default: "gpt-4o-mini")
|
| 174 |
+
"""
|
| 175 |
+
if api_key:
|
| 176 |
+
os.environ["OPENAI_API_KEY"] = api_key
|
| 177 |
+
|
| 178 |
+
self.model_name = model_name
|
| 179 |
+
self.client = get_openai_client()
|
| 180 |
+
|
| 181 |
+
def generate_reply(
|
| 182 |
+
self,
|
| 183 |
+
conversation: str,
|
| 184 |
+
trigger: str,
|
| 185 |
+
move: str,
|
| 186 |
+
) -> str:
|
| 187 |
+
"""
|
| 188 |
+
Generate reply using OpenAI API.
|
| 189 |
+
|
| 190 |
+
Args:
|
| 191 |
+
conversation: Conversation text in format "Male: ... ||| Female: ..."
|
| 192 |
+
trigger: Trigger label (e.g., "rapport_bid", "flirt_charm")
|
| 193 |
+
move: Move label (e.g., "charm", "invite", "validate")
|
| 194 |
+
|
| 195 |
+
Returns:
|
| 196 |
+
Generated reply text (1 sentence, ≤25 words)
|
| 197 |
+
"""
|
| 198 |
+
user_content = f"""
|
| 199 |
+
HỘI THOẠI: "{conversation}"
|
| 200 |
+
TRIGGER: "{trigger}"
|
| 201 |
+
MOVE: "{move}"
|
| 202 |
+
""".strip()
|
| 203 |
+
|
| 204 |
+
# Combine system prompt and user content
|
| 205 |
+
full_prompt = f"{SYSTEM_PROMPT}\n\n{user_content}"
|
| 206 |
+
|
| 207 |
+
try:
|
| 208 |
+
# Use Responses API
|
| 209 |
+
response = create_chat_response(
|
| 210 |
+
model=self.model_name,
|
| 211 |
+
user_message=full_prompt,
|
| 212 |
+
)
|
| 213 |
+
|
| 214 |
+
raw = response.strip() if response else ""
|
| 215 |
+
|
| 216 |
+
# Hậu xử lý: lấy câu đầu, giới hạn 25 từ
|
| 217 |
+
import re
|
| 218 |
+
# Tách theo dấu câu, lấy câu đầu
|
| 219 |
+
sentences = re.split(r'[.!?]', raw)
|
| 220 |
+
one_sentence = sentences[0].strip() if sentences else raw.strip()
|
| 221 |
+
|
| 222 |
+
# Giới hạn 25 từ
|
| 223 |
+
words = one_sentence.split()
|
| 224 |
+
limited = " ".join(words[:25])
|
| 225 |
+
|
| 226 |
+
# Đảm bảo kết thúc bằng dấu câu nếu cần
|
| 227 |
+
if limited and not limited[-1] in ".!?":
|
| 228 |
+
limited = limited.rstrip(",;:") + "."
|
| 229 |
+
|
| 230 |
+
return limited
|
| 231 |
+
|
| 232 |
+
except Exception as e:
|
| 233 |
+
error_msg = str(e)
|
| 234 |
+
raise Exception(f"OpenAI API error: {error_msg}")
|
| 235 |
+
|
| 236 |
+
|
| 237 |
+
# Global singleton instance
|
| 238 |
+
_openai_service = None
|
| 239 |
+
|
| 240 |
+
|
| 241 |
+
def get_openai_service(
|
| 242 |
+
api_key: Optional[str] = None,
|
| 243 |
+
model_name: str = "gpt-4o-mini",
|
| 244 |
+
) -> OpenAIReplyService:
|
| 245 |
+
"""Get or create the global OpenAI service instance."""
|
| 246 |
+
global _openai_service
|
| 247 |
+
if _openai_service is None or _openai_service.model_name != model_name:
|
| 248 |
+
_openai_service = OpenAIReplyService(api_key=api_key, model_name=model_name)
|
| 249 |
+
return _openai_service
|
| 250 |
+
|
setup_and_finetune.py
CHANGED
|
@@ -166,17 +166,17 @@ def run_training_plan(tasks):
|
|
| 166 |
continue
|
| 167 |
print(f"[START] Training {task['name']} ...")
|
| 168 |
try:
|
| 169 |
-
|
| 170 |
task["command"],
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
print(f"[DONE] {task['name']} completed successfully.")
|
| 177 |
-
|
| 178 |
print(f"[FAIL] {task['name']} exited with error code {result.returncode}")
|
| 179 |
-
|
| 180 |
except Exception as exc:
|
| 181 |
print(f"[ERROR] {task['name']} -> {exc}")
|
| 182 |
print("All auto-training tasks finished.")
|
|
|
|
| 166 |
continue
|
| 167 |
print(f"[START] Training {task['name']} ...")
|
| 168 |
try:
|
| 169 |
+
result = subprocess.run(
|
| 170 |
task["command"],
|
| 171 |
+
capture_output=True,
|
| 172 |
+
text=True,
|
| 173 |
+
check=False
|
| 174 |
+
)
|
| 175 |
+
if result.returncode == 0:
|
| 176 |
print(f"[DONE] {task['name']} completed successfully.")
|
| 177 |
+
else:
|
| 178 |
print(f"[FAIL] {task['name']} exited with error code {result.returncode}")
|
| 179 |
+
print(result.stderr)
|
| 180 |
except Exception as exc:
|
| 181 |
print(f"[ERROR] {task['name']} -> {exc}")
|
| 182 |
print("All auto-training tasks finished.")
|
test_all_openai_models.py
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Test all OpenAI models and create whitelist of working models.
|
| 3 |
+
"""
|
| 4 |
+
import os
|
| 5 |
+
import json
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
from dotenv import load_dotenv
|
| 8 |
+
from openai_service import get_available_models, get_openai_service
|
| 9 |
+
|
| 10 |
+
# Load .env file
|
| 11 |
+
env_path = Path(__file__).parent / '.env'
|
| 12 |
+
if env_path.exists():
|
| 13 |
+
load_dotenv(env_path)
|
| 14 |
+
|
| 15 |
+
# Test data
|
| 16 |
+
TEST_CONVERSATION = "Male: Tối nay anh có lịch đột xuất. ||| Female: Thế mai được không?"
|
| 17 |
+
TEST_TRIGGER = "neutral"
|
| 18 |
+
TEST_MOVE = "escalate"
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def test_model(model_name: str, max_retries: int = 2) -> tuple[bool, str, str]:
|
| 22 |
+
"""
|
| 23 |
+
Test a single OpenAI model.
|
| 24 |
+
|
| 25 |
+
Returns:
|
| 26 |
+
(success: bool, reply: str, error: str)
|
| 27 |
+
"""
|
| 28 |
+
for attempt in range(max_retries):
|
| 29 |
+
try:
|
| 30 |
+
service = get_openai_service(model_name=model_name)
|
| 31 |
+
formatted_conversation = f"Male: {TEST_CONVERSATION.split('|||')[0].strip()} ||| Female: {TEST_CONVERSATION.split('|||')[1].strip()}"
|
| 32 |
+
|
| 33 |
+
reply = service.generate_reply(
|
| 34 |
+
conversation=formatted_conversation,
|
| 35 |
+
trigger=TEST_TRIGGER,
|
| 36 |
+
move=TEST_MOVE,
|
| 37 |
+
)
|
| 38 |
+
|
| 39 |
+
if reply and len(reply.strip()) > 0:
|
| 40 |
+
return True, reply, ""
|
| 41 |
+
else:
|
| 42 |
+
return False, "", "Empty reply"
|
| 43 |
+
|
| 44 |
+
except Exception as e:
|
| 45 |
+
error_msg = str(e)
|
| 46 |
+
if attempt < max_retries - 1:
|
| 47 |
+
continue # Retry
|
| 48 |
+
return False, "", error_msg
|
| 49 |
+
|
| 50 |
+
return False, "", "Max retries exceeded"
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
def main():
|
| 54 |
+
"""Test all OpenAI models and create whitelist."""
|
| 55 |
+
print("=" * 60)
|
| 56 |
+
print("Testing All OpenAI Models for Whitelist")
|
| 57 |
+
print("=" * 60)
|
| 58 |
+
|
| 59 |
+
# Fetch all available models
|
| 60 |
+
try:
|
| 61 |
+
print("\nFetching available OpenAI models...")
|
| 62 |
+
# Filter for common chat models
|
| 63 |
+
all_model_ids = get_available_models(prefix_filters=["gpt-", "o1-", "o3-"])
|
| 64 |
+
print(f"✓ Found {len(all_model_ids)} models with prefixes: gpt-, o1-, o3-")
|
| 65 |
+
except Exception as e:
|
| 66 |
+
print(f"✗ Error fetching models: {str(e)}")
|
| 67 |
+
return 1
|
| 68 |
+
|
| 69 |
+
# Test each model
|
| 70 |
+
whitelist = []
|
| 71 |
+
failed_models = []
|
| 72 |
+
|
| 73 |
+
print(f"\nTesting {len(all_model_ids)} models...")
|
| 74 |
+
print("=" * 60)
|
| 75 |
+
|
| 76 |
+
for idx, model_id in enumerate(all_model_ids, 1):
|
| 77 |
+
print(f"\n[{idx}/{len(all_model_ids)}] Testing: {model_id}")
|
| 78 |
+
|
| 79 |
+
success, reply, error = test_model(model_id)
|
| 80 |
+
|
| 81 |
+
if success:
|
| 82 |
+
print(f" ✓ PASSED - Reply: {reply[:60]}...")
|
| 83 |
+
whitelist.append({
|
| 84 |
+
"id": model_id,
|
| 85 |
+
"name": model_id,
|
| 86 |
+
"displayName": model_id,
|
| 87 |
+
"test_reply": reply[:100], # Store sample reply
|
| 88 |
+
})
|
| 89 |
+
else:
|
| 90 |
+
print(f" ✗ FAILED - {error[:100]}")
|
| 91 |
+
failed_models.append({
|
| 92 |
+
"id": model_id,
|
| 93 |
+
"name": model_id,
|
| 94 |
+
"error": error[:200],
|
| 95 |
+
})
|
| 96 |
+
|
| 97 |
+
# Save whitelist
|
| 98 |
+
whitelist_file = Path(__file__).parent / "openai_models_whitelist.json"
|
| 99 |
+
with open(whitelist_file, "w", encoding="utf-8") as f:
|
| 100 |
+
json.dump({
|
| 101 |
+
"whitelist": whitelist,
|
| 102 |
+
"failed": failed_models,
|
| 103 |
+
"test_data": {
|
| 104 |
+
"conversation": TEST_CONVERSATION,
|
| 105 |
+
"trigger": TEST_TRIGGER,
|
| 106 |
+
"move": TEST_MOVE,
|
| 107 |
+
},
|
| 108 |
+
"total_tested": len(all_model_ids),
|
| 109 |
+
"passed": len(whitelist),
|
| 110 |
+
"failed": len(failed_models),
|
| 111 |
+
}, f, indent=2, ensure_ascii=False)
|
| 112 |
+
|
| 113 |
+
# Print summary
|
| 114 |
+
print("\n" + "=" * 60)
|
| 115 |
+
print("Test Summary")
|
| 116 |
+
print("=" * 60)
|
| 117 |
+
print(f"Total models tested: {len(all_model_ids)}")
|
| 118 |
+
print(f"✓ Passed (whitelist): {len(whitelist)}")
|
| 119 |
+
print(f"✗ Failed: {len(failed_models)}")
|
| 120 |
+
print(f"\nWhitelist saved to: {whitelist_file}")
|
| 121 |
+
|
| 122 |
+
if whitelist:
|
| 123 |
+
print("\n✓ Working models (whitelist):")
|
| 124 |
+
for model in whitelist:
|
| 125 |
+
print(f" - {model['displayName']} ({model['id']})")
|
| 126 |
+
|
| 127 |
+
if failed_models:
|
| 128 |
+
print("\n✗ Failed models:")
|
| 129 |
+
for model in failed_models[:10]: # Show first 10
|
| 130 |
+
print(f" - {model['name']}: {model['error'][:50]}...")
|
| 131 |
+
if len(failed_models) > 10:
|
| 132 |
+
print(f" ... and {len(failed_models) - 10} more")
|
| 133 |
+
|
| 134 |
+
return 0 if whitelist else 1
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
if __name__ == "__main__":
|
| 138 |
+
exit(main())
|
| 139 |
+
|
test_openai_list_models.py
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Test script to fetch OpenAI models list.
|
| 3 |
+
"""
|
| 4 |
+
import os
|
| 5 |
+
from pathlib import Path
|
| 6 |
+
from dotenv import load_dotenv
|
| 7 |
+
from openai import OpenAI
|
| 8 |
+
|
| 9 |
+
# Load .env file
|
| 10 |
+
env_path = Path(__file__).parent / '.env'
|
| 11 |
+
if env_path.exists():
|
| 12 |
+
load_dotenv(env_path)
|
| 13 |
+
print(f"✓ Loaded .env file from {env_path}")
|
| 14 |
+
else:
|
| 15 |
+
print(f"⚠ .env file not found at {env_path}, using environment variables only")
|
| 16 |
+
|
| 17 |
+
# Get API key
|
| 18 |
+
api_key = os.getenv("OPENAI_API_KEY")
|
| 19 |
+
if not api_key:
|
| 20 |
+
print("❌ OPENAI_API_KEY not found in environment variables")
|
| 21 |
+
print("\nPlease set it:")
|
| 22 |
+
print(" export OPENAI_API_KEY=sk-...")
|
| 23 |
+
exit(1)
|
| 24 |
+
|
| 25 |
+
print(f"✓ OpenAI API Key found: {api_key[:10]}...{api_key[-4:]}")
|
| 26 |
+
|
| 27 |
+
# Create client
|
| 28 |
+
try:
|
| 29 |
+
client = OpenAI(api_key=api_key)
|
| 30 |
+
print("✓ OpenAI client created successfully")
|
| 31 |
+
except Exception as e:
|
| 32 |
+
print(f"✗ Error creating OpenAI client: {e}")
|
| 33 |
+
exit(1)
|
| 34 |
+
|
| 35 |
+
# Fetch models
|
| 36 |
+
print("\n" + "=" * 60)
|
| 37 |
+
print("Fetching OpenAI models list...")
|
| 38 |
+
print("=" * 60)
|
| 39 |
+
|
| 40 |
+
try:
|
| 41 |
+
models = client.models.list()
|
| 42 |
+
print(f"✓ Successfully fetched models list")
|
| 43 |
+
print(f" Total models: {len(models.data)}")
|
| 44 |
+
|
| 45 |
+
# Filter for common chat models
|
| 46 |
+
chat_models = []
|
| 47 |
+
prefixes = ["gpt-", "o1-", "o3-"]
|
| 48 |
+
|
| 49 |
+
for model in models.data:
|
| 50 |
+
model_id = model.id
|
| 51 |
+
if any(model_id.startswith(prefix) for prefix in prefixes):
|
| 52 |
+
chat_models.append({
|
| 53 |
+
"id": model_id,
|
| 54 |
+
"created": getattr(model, "created", None),
|
| 55 |
+
"owned_by": getattr(model, "owned_by", None),
|
| 56 |
+
})
|
| 57 |
+
|
| 58 |
+
print(f"\n✓ Found {len(chat_models)} chat models (filtered by prefixes: {prefixes})")
|
| 59 |
+
|
| 60 |
+
print("\n" + "=" * 60)
|
| 61 |
+
print("Chat Models List:")
|
| 62 |
+
print("=" * 60)
|
| 63 |
+
for idx, model in enumerate(chat_models[:20], 1): # Show first 20
|
| 64 |
+
created = model.get("created")
|
| 65 |
+
owned_by = model.get("owned_by", "unknown")
|
| 66 |
+
print(f"{idx}. {model['id']} (owned_by: {owned_by}, created: {created})")
|
| 67 |
+
|
| 68 |
+
if len(chat_models) > 20:
|
| 69 |
+
print(f"\n... and {len(chat_models) - 20} more models")
|
| 70 |
+
|
| 71 |
+
# Save to cache
|
| 72 |
+
cache_path = Path(__file__).parent / "config" / "ai_models.json"
|
| 73 |
+
cache_path.parent.mkdir(parents=True, exist_ok=True)
|
| 74 |
+
|
| 75 |
+
import json
|
| 76 |
+
all_models_data = []
|
| 77 |
+
for model in models.data:
|
| 78 |
+
all_models_data.append({
|
| 79 |
+
"id": model.id,
|
| 80 |
+
"created": getattr(model, "created", None),
|
| 81 |
+
"owned_by": getattr(model, "owned_by", None),
|
| 82 |
+
})
|
| 83 |
+
|
| 84 |
+
with open(cache_path, "w", encoding="utf-8") as f:
|
| 85 |
+
json.dump(all_models_data, f, ensure_ascii=False, indent=2)
|
| 86 |
+
|
| 87 |
+
print(f"\n✓ Saved all models to cache: {cache_path}")
|
| 88 |
+
print(f" Total models cached: {len(all_models_data)}")
|
| 89 |
+
|
| 90 |
+
print("\n" + "=" * 60)
|
| 91 |
+
print("Test Summary")
|
| 92 |
+
print("=" * 60)
|
| 93 |
+
print(f"✓ API connection: SUCCESS")
|
| 94 |
+
print(f"✓ Models fetched: {len(models.data)}")
|
| 95 |
+
print(f"✓ Chat models (filtered): {len(chat_models)}")
|
| 96 |
+
print(f"✓ Cache saved: {cache_path}")
|
| 97 |
+
|
| 98 |
+
except Exception as e:
|
| 99 |
+
print(f"\n✗ Error fetching models: {str(e)}")
|
| 100 |
+
import traceback
|
| 101 |
+
traceback.print_exc()
|
| 102 |
+
exit(1)
|
| 103 |
+
|
test_openai_response.py
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Test OpenAI Responses API với một model.
|
| 3 |
+
"""
|
| 4 |
+
import os
|
| 5 |
+
from pathlib import Path
|
| 6 |
+
from dotenv import load_dotenv
|
| 7 |
+
from openai import OpenAI
|
| 8 |
+
|
| 9 |
+
# Load .env file
|
| 10 |
+
env_path = Path(__file__).parent / '.env'
|
| 11 |
+
if env_path.exists():
|
| 12 |
+
load_dotenv(env_path)
|
| 13 |
+
|
| 14 |
+
api_key = os.getenv("OPENAI_API_KEY")
|
| 15 |
+
if not api_key:
|
| 16 |
+
print("❌ OPENAI_API_KEY not found")
|
| 17 |
+
exit(1)
|
| 18 |
+
|
| 19 |
+
client = OpenAI(api_key=api_key)
|
| 20 |
+
|
| 21 |
+
# Test data
|
| 22 |
+
SYSTEM_PROMPT = "You are a helpful assistant. Respond briefly in Vietnamese."
|
| 23 |
+
USER_MESSAGE = "Xin chào, bạn khỏe không?"
|
| 24 |
+
|
| 25 |
+
print("=" * 60)
|
| 26 |
+
print("Testing OpenAI Responses API")
|
| 27 |
+
print("=" * 60)
|
| 28 |
+
|
| 29 |
+
# Test với gpt-4o-mini
|
| 30 |
+
model = "gpt-4o-mini"
|
| 31 |
+
print(f"\nTesting model: {model}")
|
| 32 |
+
print(f"System prompt: {SYSTEM_PROMPT}")
|
| 33 |
+
print(f"User message: {USER_MESSAGE}")
|
| 34 |
+
|
| 35 |
+
try:
|
| 36 |
+
full_prompt = f"{SYSTEM_PROMPT}\n\n{USER_MESSAGE}"
|
| 37 |
+
|
| 38 |
+
print("\nCalling client.responses.create()...")
|
| 39 |
+
resp = client.responses.create(
|
| 40 |
+
model=model,
|
| 41 |
+
input=full_prompt,
|
| 42 |
+
)
|
| 43 |
+
|
| 44 |
+
print(f"✓ Response received")
|
| 45 |
+
print(f" Response type: {type(resp)}")
|
| 46 |
+
print(f" Response attributes: {dir(resp)}")
|
| 47 |
+
|
| 48 |
+
# Extract text
|
| 49 |
+
if hasattr(resp, 'output') and resp.output:
|
| 50 |
+
print(f" Output type: {type(resp.output)}")
|
| 51 |
+
print(f" Output length: {len(resp.output) if hasattr(resp.output, '__len__') else 'N/A'}")
|
| 52 |
+
|
| 53 |
+
if len(resp.output) > 0:
|
| 54 |
+
first_output = resp.output[0]
|
| 55 |
+
print(f" First output type: {type(first_output)}")
|
| 56 |
+
print(f" First output attributes: {dir(first_output)}")
|
| 57 |
+
|
| 58 |
+
if hasattr(first_output, 'content') and first_output.content:
|
| 59 |
+
print(f" Content type: {type(first_output.content)}")
|
| 60 |
+
print(f" Content length: {len(first_output.content) if hasattr(first_output.content, '__len__') else 'N/A'}")
|
| 61 |
+
|
| 62 |
+
if len(first_output.content) > 0:
|
| 63 |
+
first_content = first_output.content[0]
|
| 64 |
+
print(f" First content type: {type(first_content)}")
|
| 65 |
+
print(f" First content attributes: {dir(first_content)}")
|
| 66 |
+
|
| 67 |
+
if hasattr(first_content, 'text'):
|
| 68 |
+
text_obj = first_content.text
|
| 69 |
+
print(f" Text object type: {type(text_obj)}")
|
| 70 |
+
print(f" Text object attributes: {dir(text_obj)}")
|
| 71 |
+
|
| 72 |
+
if hasattr(text_obj, 'value'):
|
| 73 |
+
text_value = text_obj.value
|
| 74 |
+
print(f"\n✓ SUCCESS!")
|
| 75 |
+
print(f" Response text: {text_value}")
|
| 76 |
+
else:
|
| 77 |
+
print(f" Text object: {text_obj}")
|
| 78 |
+
else:
|
| 79 |
+
print(f" First content: {first_content}")
|
| 80 |
+
else:
|
| 81 |
+
print(" No content found")
|
| 82 |
+
else:
|
| 83 |
+
print(" No content attribute found")
|
| 84 |
+
else:
|
| 85 |
+
print(" Output is empty")
|
| 86 |
+
else:
|
| 87 |
+
print(f" Response object: {resp}")
|
| 88 |
+
print(f" Full response: {resp}")
|
| 89 |
+
|
| 90 |
+
except Exception as e:
|
| 91 |
+
print(f"\n✗ Error: {str(e)}")
|
| 92 |
+
import traceback
|
| 93 |
+
traceback.print_exc()
|
| 94 |
+
|
trigger_move_identifier.py
CHANGED
|
@@ -64,7 +64,7 @@ class TriggerMoveIdentifier:
|
|
| 64 |
"pivot_disreengage",
|
| 65 |
"neutral",
|
| 66 |
]
|
| 67 |
-
|
| 68 |
DEFAULT_MOVE_LABELS = [
|
| 69 |
"spark",
|
| 70 |
"intrigue",
|
|
@@ -92,7 +92,7 @@ class TriggerMoveIdentifier:
|
|
| 92 |
"relate",
|
| 93 |
"neutral",
|
| 94 |
]
|
| 95 |
-
|
| 96 |
def __init__(
|
| 97 |
self,
|
| 98 |
model_dir: str = "./models/trigger_detector",
|
|
@@ -212,13 +212,13 @@ class TriggerMoveIdentifier:
|
|
| 212 |
"Respond using format 'Trigger: <trigger> | Move: <move>'"
|
| 213 |
)
|
| 214 |
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
max_new_tokens=64,
|
| 219 |
-
|
| 220 |
return_full_text=False,
|
| 221 |
-
|
| 222 |
return self._parse_response(response)
|
| 223 |
except Exception as exc:
|
| 224 |
print(f"Inference API error: {exc}, falling back to heuristics.")
|
|
|
|
| 64 |
"pivot_disreengage",
|
| 65 |
"neutral",
|
| 66 |
]
|
| 67 |
+
|
| 68 |
DEFAULT_MOVE_LABELS = [
|
| 69 |
"spark",
|
| 70 |
"intrigue",
|
|
|
|
| 92 |
"relate",
|
| 93 |
"neutral",
|
| 94 |
]
|
| 95 |
+
|
| 96 |
def __init__(
|
| 97 |
self,
|
| 98 |
model_dir: str = "./models/trigger_detector",
|
|
|
|
| 212 |
"Respond using format 'Trigger: <trigger> | Move: <move>'"
|
| 213 |
)
|
| 214 |
|
| 215 |
+
try:
|
| 216 |
+
response = self.client.text_generation(
|
| 217 |
+
prompt,
|
| 218 |
max_new_tokens=64,
|
| 219 |
+
temperature=0.3,
|
| 220 |
return_full_text=False,
|
| 221 |
+
)
|
| 222 |
return self._parse_response(response)
|
| 223 |
except Exception as exc:
|
| 224 |
print(f"Inference API error: {exc}, falling back to heuristics.")
|