99 lines
3.3 KiB
Python
99 lines
3.3 KiB
Python
# services/dify_client.py
|
||
import os, json, re, requests
|
||
from dotenv import load_dotenv
|
||
from flask import current_app
|
||
|
||
load_dotenv()
|
||
|
||
DIFY_BASE = os.getenv("DIFY_API_BASE_URL", "https://api.dify.ai/v1")
|
||
TIMEOUT = 60
|
||
|
||
def _post_request(endpoint: str, api_key: str, payload: dict):
|
||
"""Generic function to post a request to a Dify endpoint."""
|
||
if not api_key:
|
||
raise RuntimeError("Dify API key is not set")
|
||
|
||
url = f"{DIFY_BASE}{endpoint}"
|
||
headers = {
|
||
"Authorization": f"Bearer {api_key}",
|
||
"Content-Type": "application/json",
|
||
}
|
||
|
||
# For debugging the exact payload sent to the API
|
||
current_app.logger.debug(f"Sending Dify request to {url}: {json.dumps(payload, indent=2, ensure_ascii=False)}")
|
||
|
||
resp = requests.post(url, headers=headers, json=payload, timeout=TIMEOUT)
|
||
|
||
current_app.logger.debug(f"Dify API Response ({resp.status_code}): {resp.text}")
|
||
|
||
if resp.status_code != 200:
|
||
resp.raise_for_status()
|
||
|
||
data = resp.json()
|
||
return data.get("answer") or data
|
||
|
||
def translate_text(text: str, target_lang: str, user_id: str = "system") -> str:
|
||
"""
|
||
Calls the Dify CHAT API for translation, mimicking a user query.
|
||
"""
|
||
api_key = os.getenv("DIFY_TRANSLATOR_API_KEY")
|
||
# Combine all information into a single query string, as expected by the prompt
|
||
query = f"目標語言:{target_lang}\n需翻譯內容:\n{text}"
|
||
|
||
payload = {
|
||
"inputs": {}, # Chatbot apps generally don't use inputs here
|
||
"response_mode": "blocking",
|
||
"user": user_id,
|
||
"query": query,
|
||
# conversation_id is optional for single-turn interactions
|
||
}
|
||
return _post_request("/chat-messages", api_key, payload)
|
||
|
||
def summarize_text(text: str, user_id: str = "system") -> str:
|
||
api_key = os.getenv("DIFY_SUMMARIZER_API_KEY")
|
||
payload = {
|
||
"inputs": {},
|
||
"response_mode": "blocking",
|
||
"user": user_id,
|
||
"query": text,
|
||
}
|
||
return _post_request("/chat-messages", api_key, payload)
|
||
|
||
def extract_action_items(text: str, user_id: str = "system") -> list[dict]:
|
||
api_key = os.getenv("DIFY_ACTION_EXTRACTOR_API_KEY")
|
||
|
||
query = f"請從以下會議記錄中,提取所有行動項目(action items),並嚴格以JSON格式返回。會議記錄如下:\n\n{text}"
|
||
|
||
payload = {
|
||
"inputs": {},
|
||
"response_mode": "blocking",
|
||
"user": user_id,
|
||
"query": query,
|
||
}
|
||
raw = _post_request("/chat-messages", api_key, payload)
|
||
|
||
# Fault tolerance for JSON parsing
|
||
s = str(raw).strip()
|
||
s = re.sub(r"^```(?:json)?|```$", "", s, flags=re.IGNORECASE|re.MULTILINE).strip()
|
||
if not (s.startswith("[") and s.endswith("]")):
|
||
m = re.search(r"[\s\S]*\[[\s\S]*\][\s\S]*", s)
|
||
if m: s = m.group(0)
|
||
|
||
items = json.loads(s)
|
||
if not isinstance(items, list):
|
||
raise ValueError("Extractor did not return a list")
|
||
|
||
# Normalize keys for database storage
|
||
normalized = []
|
||
for i in items:
|
||
for k in ("item", "action", "owner", "duedate"):
|
||
if k not in i:
|
||
raise ValueError(f"Extractor item is missing required key: {k}")
|
||
normalized.append({
|
||
"item": i["item"],
|
||
"action": i["action"],
|
||
"owner": i["owner"],
|
||
"due_date": i["duedate"],
|
||
})
|
||
return normalized
|