This commit is contained in:
beabigegg
2025-08-17 18:34:38 +08:00
parent 9a263b6afb
commit 788e2409df
7 changed files with 35 additions and 30 deletions

Binary file not shown.

View File

@@ -213,8 +213,11 @@ def handle_meeting_detail(meeting_id):
return jsonify(meeting.to_dict())
if request.method == 'DELETE':
if get_jwt().get('role') != 'admin':
return jsonify({"msg": "Administration rights required"}), 403
current_user_id = get_jwt_identity()
is_admin = get_jwt().get('role') == 'admin'
if not is_admin and str(meeting.created_by_id) != str(current_user_id):
return jsonify({"msg": "Only the meeting creator or an admin can delete this meeting."}), 403
db.session.delete(meeting)
db.session.commit()
return jsonify({"msg": "Meeting and associated action items deleted"}), 200
@@ -235,9 +238,9 @@ def summarize_meeting(meeting_id):
@jwt_required()
def preview_actions(meeting_id):
meeting = Meeting.query.get_or_404(meeting_id)
text_content = meeting.summary or meeting.transcript
text_content = meeting.transcript # Always use the full transcript
if not text_content:
return jsonify({'error': 'Meeting has no summary or transcript to analyze.'}), 400
return jsonify({'error': 'Meeting has no transcript to analyze.'}), 400
task = preview_action_items_task.delay(text_content)
return jsonify({'task_id': task.id, 'status_url': f'/status/{task.id}'}), 202

View File

@@ -40,6 +40,7 @@ const MeetingDetailPage = () => {
const [isAddActionItemOpen, setIsAddActionItemOpen] = useState(false);
const [newActionItem, setNewActionItem] = useState({ action: '', owner_id: '', due_date: '', item: '' });
const [previewedItems, setPreviewedItems] = useState([]);
const [previewMessage, setPreviewMessage] = useState(''); // State for the preview result message
const fetchMeetingData = useCallback(async () => {
try {
@@ -94,7 +95,11 @@ const MeetingDetailPage = () => {
} else if (previewTask) { // Handle preview success
setPreviewTask(null);
if (updatedTask.state === 'SUCCESS' && updatedTask.info.items) {
setPreviewedItems(updatedTask.info.items);
if (updatedTask.info.items.length > 0) {
setPreviewedItems(updatedTask.info.items);
} else {
setPreviewMessage('文本中未找到可提取的行動項目。');
}
}
}
}
@@ -141,6 +146,8 @@ const MeetingDetailPage = () => {
};
const handlePreviewActionItems = async () => {
setPreviewedItems([]); // Clear previous results
setPreviewMessage(''); // Clear previous messages
setPreviewTask({ state: 'PENDING', info: 'Initializing preview task...' });
try {
// This now calls the async task endpoint
@@ -244,9 +251,10 @@ const MeetingDetailPage = () => {
)}
{canManageMeeting && (<Box sx={{ mt: 3 }}>
<Button variant="outlined" startIcon={<PreviewIcon />} onClick={handlePreviewActionItems} disabled={previewTask || summaryTask || isEditingSummary || (!meeting.summary && !meeting.transcript)}>
<Button variant="outlined" startIcon={<PreviewIcon />} onClick={handlePreviewActionItems} disabled={previewTask || summaryTask || isEditingSummary || !meeting.transcript}>
{previewTask ? <CircularProgress size={24} /> : "Preview Action Items"}
</Button>
{previewMessage && <Alert severity="info" sx={{ mt: 2 }}>{previewMessage}</Alert>}
{previewedItems.length > 0 && (<Box>
<TableContainer component={Paper} sx={{ mt: 2 }}><Table size="small">
<TableHead><TableRow><TableCell>Context/Item</TableCell><TableCell>Action</TableCell><TableCell>Owner</TableCell><TableCell>Due Date</TableCell></TableRow></TableHead>

View File

@@ -1,6 +1,7 @@
# services/dify_client.py
import os, json, re, requests
from dotenv import load_dotenv
from flask import current_app
load_dotenv()
@@ -19,12 +20,13 @@ def _post_request(endpoint: str, api_key: str, payload: dict):
}
# For debugging the exact payload sent to the API
print(f"Sending Dify request to {url}: {json.dumps(payload, indent=2, ensure_ascii=False)}")
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:
print(f"Dify API Error Response: {resp.text}")
resp.raise_for_status()
data = resp.json()
@@ -59,11 +61,14 @@ def summarize_text(text: str, user_id: str = "system") -> str:
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": text,
"query": query,
}
raw = _post_request("/chat-messages", api_key, payload)
@@ -90,4 +95,4 @@ def extract_action_items(text: str, user_id: str = "system") -> list[dict]:
"owner": i["owner"],
"due_date": i["duedate"],
})
return normalized
return normalized

View File

@@ -362,37 +362,26 @@ def summarize_text_task(self, meeting_id):
def preview_action_items_task(self, text_content):
from app import app
with app.app_context():
from services.dify_client import extract_action_items
try:
self.update_progress(10, 100, "Requesting Dify for action items...")
api_key = app.config.get("DIFY_ACTION_EXTRACTOR_API_KEY")
# We can reuse the logic from the summarizer to strip timestamps
plain_text = re.sub(r'^(\s*\[.*?\])\s*', '', text_content, flags=re.MULTILINE)
if not plain_text.strip():
self.update_progress(100, 100, "Preview skipped, content is empty.")
return {'status': 'Success', 'items': []}
response = ask_dify(api_key, plain_text)
answer_text = response.get("answer", "")
self.update_progress(80, 100, "Parsing response...")
# Use the robust client function
parsed_items = extract_action_items(plain_text)
parsed_items = []
try:
# Find the JSON array within the response string
match = re.search(r'\[.*\]', answer_text, re.DOTALL)
if match:
json_str = match.group(0)
parsed_items = json.loads(json_str)
# Ensure it's a list, otherwise reset to empty
if not isinstance(parsed_items, list):
parsed_items = []
except (json.JSONDecodeError, TypeError):
# If parsing fails, leave it as an empty list
parsed_items = []
self.update_progress(100, 100, "Action item preview generated.")
return {'status': 'Success', 'items': parsed_items}
except Exception as e:
# Log the exception for better debugging
import logging
logging.error(f"Error in preview_action_items_task: {e}", exc_info=True)
self.update_state(
state='FAILURE',
meta={'exc_type': type(e).__name__, 'exc_message': str(e)}