# 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