lovebird25 / perplexity_service.py
Paul
update
136f619
"""
Service for generating replies using Perplexity API.
"""
import os
from typing import Optional
from openai import OpenAI
SYSTEM_PROMPT = """
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".
Bạn được cung cấp:
- 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 "|||".
- TRIGGER: intent hiện tại (ví dụ: neutral, positive, negative, confused...).
- MOVE: chiến lược hiện tại (ví dụ: escalate, hold, de-escalate, tease, comfort...).
Nhiệm vụ của bạn:
- 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).
- Ư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.
QUY TẮC CỨNG:
- Chỉ trả về đúng 1 câu duy nhất.
- Tối đa 25 từ tiếng Việt.
- Lịch sự, ấm áp, thân thiện; không phán xét, không thô lỗ.
- Không giải thích meta (không nói về "prompt", "AI", "chiến lược", "MOVE", "TRIGGER"...).
- Không lặp lại nguyên văn câu của đối phương.
- 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ó).
Khi TRIGGER hoặc MOVE có vẻ mâu thuẫn với HỘI THOẠI:
- Hãy ưu tiên sự an toàn và mềm mại.
- 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.
PHONG CÁCH:
- Ấm áp, tự tin nhưng không tự cao.
- Có thể dùng từ đệm tự nhiên (nha, nhé, ạ, dạ) khi phù hợp với ngữ cảnh.
- Phản chiếu cảm xúc của đối phương.
- Giữ mạch trò chuyện mở để còn đất tăng tương tác về sau.
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:
- 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.
""".strip()
class PerplexityReplyService:
"""Service for generating replies using Perplexity API."""
def __init__(self, api_key: Optional[str] = None, model: str = "sonar"):
"""
Initialize Perplexity service.
Args:
api_key: Perplexity API key. If None, will try to get from PERPLEXITY_API_KEY env var.
model: Model name to use (default: "sonar")
Valid models: sonar, sonar-pro, llama-3.1-sonar-small-32k-online, etc.
See: https://docs.perplexity.ai/getting-started/models
Note: Model names may vary. Check documentation for current valid models.
"""
self.api_key = api_key or os.getenv("PERPLEXITY_API_KEY")
if not self.api_key:
raise ValueError(
"Perplexity API key is required.\n\n"
"Set environment variable:\n"
" export PERPLEXITY_API_KEY=pplx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\n"
"Or pass api_key parameter when initializing PerplexityReplyService."
)
self.client = OpenAI(
api_key=self.api_key,
base_url="https://api.perplexity.ai",
)
self.model = model
def generate_reply(
self,
conversation: str,
trigger: str,
move: str,
temperature: float = 0.2,
max_tokens: int = 80,
) -> str:
"""
Generate reply using Perplexity API.
Args:
conversation: Conversation text in format "Male: ... ||| Female: ..."
trigger: Trigger label (e.g., "rapport_bid", "flirt_charm")
move: Move label (e.g., "charm", "invite", "validate")
temperature: Sampling temperature
max_tokens: Maximum tokens to generate
Returns:
Generated reply text (1 sentence, ≤25 words)
"""
user_content = f"""
HỘI THOẠI: "{conversation}"
TRIGGER: "{trigger}"
MOVE: "{move}"
""".strip()
try:
completion = self.client.chat.completions.create(
model=self.model,
temperature=temperature,
max_tokens=max_tokens,
messages=[
{
"role": "system",
"content": SYSTEM_PROMPT,
},
{
"role": "user",
"content": user_content,
},
],
)
raw = completion.choices[0].message.content.strip() if completion.choices[0].message.content else ""
# Hậu xử lý: lấy câu đầu, giới hạn 25 từ
import re
# Tách theo dấu câu, lấy câu đầu
sentences = re.split(r'[.!?]', raw)
one_sentence = sentences[0].strip() if sentences else raw.strip()
# Giới hạn 25 từ
words = one_sentence.split()
limited = " ".join(words[:25])
# Đảm bảo kết thúc bằng dấu câu nếu cần
if limited and not limited[-1] in ".!?":
limited = limited.rstrip(",;:") + "."
return limited
except Exception as e:
raise Exception(f"Perplexity API error: {str(e)}")
# Global singleton instance
_perplexity_service = None
def get_perplexity_service(
api_key: Optional[str] = None,
model: str = "sonar",
) -> PerplexityReplyService:
"""Get or create the global Perplexity service instance."""
global _perplexity_service
if _perplexity_service is None:
_perplexity_service = PerplexityReplyService(api_key=api_key, model=model)
return _perplexity_service