From 5d0ec6f3c49f17a3e362fc5cf318b434ffc0fc1b Mon Sep 17 00:00:00 2001 From: yumoqing Date: Fri, 24 Jul 2026 11:37:04 +0800 Subject: [PATCH] fix: rewrite tag_assign dspy with explicit return --- wwwroot/tag_assign/index.dspy | 103 +++++++--------------------------- wwwroot/tag_search/index.dspy | 2 +- 2 files changed, 21 insertions(+), 84 deletions(-) diff --git a/wwwroot/tag_assign/index.dspy b/wwwroot/tag_assign/index.dspy index 261f554..c5f2d74 100644 --- a/wwwroot/tag_assign/index.dspy +++ b/wwwroot/tag_assign/index.dspy @@ -11,7 +11,6 @@ import json base_url = entire_url('/tag_assign/index.dspy') async with db.sqlorContext(dbname) as sor: - # Handle assign/unassign actions if action and media_id and tag_id: import uuid try: @@ -27,37 +26,24 @@ async with db.sqlorContext(dbname) as sor: except Exception: pass - # Get knowledge bases kbs = await sor.R('knowledge_bases', {}) - - # Get media items media_items = [] if kb_id: if media_type == 'document': - recs = await sor.sqlExe( - "SELECT id, file_name, file_type FROM documents WHERE kb_id=${kb_id}$ ORDER BY created_at DESC LIMIT 100", - {"kb_id": kb_id}) + recs = await sor.sqlExe("SELECT id, file_name, file_type FROM documents WHERE kb_id=${kb_id}$ ORDER BY created_at DESC LIMIT 100", {"kb_id": kb_id}) media_items = [{"id": r.id, "name": r.file_name, "type": r.file_type} for r in recs] elif media_type == 'face': - recs = await sor.sqlExe( - "SELECT id, name, description FROM entities WHERE kb_id=${kb_id}$ AND entity_type='person' AND face_embedding_id IS NOT NULL ORDER BY created_at DESC LIMIT 100", - {"kb_id": kb_id}) + recs = await sor.sqlExe("SELECT id, name, description FROM entities WHERE kb_id=${kb_id}$ AND entity_type='person' AND face_embedding_id IS NOT NULL ORDER BY created_at DESC LIMIT 100", {"kb_id": kb_id}) media_items = [{"id": r.id, "name": r.name or '人脸#'+r.id[:8], "type": "face"} for r in recs] elif media_type == 'voice': - recs = await sor.sqlExe( - "SELECT id, name, description FROM entities WHERE kb_id=${kb_id}$ AND entity_type='voice' AND voice_embedding_id IS NOT NULL ORDER BY created_at DESC LIMIT 100", - {"kb_id": kb_id}) + recs = await sor.sqlExe("SELECT id, name, description FROM entities WHERE kb_id=${kb_id}$ AND entity_type='voice' AND voice_embedding_id IS NOT NULL ORDER BY created_at DESC LIMIT 100", {"kb_id": kb_id}) media_items = [{"id": r.id, "name": r.name or '声纹#'+r.id[:8], "type": "voice"} for r in recs] - # Get current tags for selected media current_tags = [] if media_id: - recs = await sor.sqlExe( - "SELECT t.id, t.name, t.color FROM media_tags mt JOIN tags t ON mt.tag_id=t.id WHERE mt.media_type=${type}$ AND mt.media_id=${mid}$", - {"type": media_type, "mid": media_id}) + recs = await sor.sqlExe("SELECT t.id, t.name, t.color FROM media_tags mt JOIN tags t ON mt.tag_id=t.id WHERE mt.media_type=${type}$ AND mt.media_id=${mid}$", {"type": media_type, "mid": media_id}) current_tags = [{"id": r.id, "name": r.name, "color": r.color} for r in recs] - # Get all available tags all_tags = [] if kb_id: tag_recs = await sor.R('tags', {'kb_id': kb_id}) @@ -65,93 +51,44 @@ async with db.sqlorContext(dbname) as sor: current_tag_ids = {t['id'] for t in current_tags} - # Build available tag buttons tag_buttons = [] for t in all_tags: is_assigned = t['id'] in current_tag_ids - if is_assigned: - act = 'unassign' - lbl = '✓ ' + t['name'] - bg = t['color'] - clr = '#fff' - else: - act = 'assign' - lbl = '+ ' + t['name'] - bg = '#e5e7eb' - clr = '#666' + act = 'unassign' if is_assigned else 'assign' + lbl = ('✓ ' if is_assigned else '+ ') + t['name'] + bg = t['color'] if is_assigned else '#e5e7eb' + clr = '#fff' if is_assigned else '#666' tag_url = base_url + '?kb_id=' + kb_id + '&media_type=' + media_type + '&media_id=' + media_id + '&action=' + act + '&tag_id=' + t['id'] - tag_buttons.append({ - "widgettype": "Button", - "options": {"label": lbl, "bgcolor": bg, "color": clr, "cfontsize": 12, "padding": "4px 12px", "marginRight": "6px", "marginBottom": "6px"}, - "binds": [{"wid": "self", "event": "click", "actiontype": "urlwidget", "target": "app.rag_main_content", "mode": "replace", "options": {"url": tag_url}}] - }) + tag_buttons.append({"widgettype": "Button", "options": {"label": lbl, "bgcolor": bg, "color": clr, "cfontsize": 12, "padding": "4px 12px", "marginRight": "6px", "marginBottom": "6px"}, "binds": [{"wid": "self", "event": "click", "actiontype": "urlwidget", "target": "app.rag_main_content", "mode": "replace", "options": {"url": tag_url}}]}) - # Build media list buttons media_buttons = [] for m in media_items: murl = base_url + '?kb_id=' + kb_id + '&media_type=' + media_type + '&media_id=' + m['id'] is_current = m['id'] == media_id - media_buttons.append({ - "widgettype": "Button", - "options": { - "label": ('📄' if media_type=='document' else ('👤' if media_type=='face' else '🎤')) + ' ' + m['name'], - "bgcolor": "#dbeafe" if is_current else "#f9fafb", - "color": "#1e40af" if is_current else "#666", - "cfontsize": 12, - "padding": "6px 12px", - "marginBottom": "4px", - "halign": "left" - }, - "binds": [{"wid": "self", "event": "click", "actiontype": "urlwidget", "target": "app.rag_main_content", "mode": "replace", "options": {"url": murl}}] - }) + icon = '📄' if media_type == 'document' else ('👤' if media_type == 'face' else '🎤') + media_buttons.append({"widgettype": "Button", "options": {"label": icon + ' ' + m['name'], "bgcolor": "#dbeafe" if is_current else "#f9fafb", "color": "#1e40af" if is_current else "#666", "cfontsize": 12, "padding": "6px 12px", "marginBottom": "4px", "halign": "left"}, "binds": [{"wid": "self", "event": "click", "actiontype": "urlwidget", "target": "app.rag_main_content", "mode": "replace", "options": {"url": murl}}]}) - # KB buttons kb_buttons = [] for k in kbs: - kb_buttons.append({ - "widgettype": "Button", - "options": {"label": k.name, "bgcolor": "#3b82f6" if k.id==kb_id else "#e5e7eb", "color": "#fff" if k.id==kb_id else "#666", "cfontsize": 12, "padding": "6px 14px", "marginRight": "6px", "marginBottom": "4px"}, - "binds": [{"wid": "self", "event": "click", "actiontype": "urlwidget", "target": "app.rag_main_content", "mode": "replace", - "options": {"url": base_url + '?kb_id=' + k.id + '&media_type=' + media_type}}]} - ) + kb_buttons.append({"widgettype": "Button", "options": {"label": k.name, "bgcolor": "#3b82f6" if k.id == kb_id else "#e5e7eb", "color": "#fff" if k.id == kb_id else "#666", "cfontsize": 12, "padding": "6px 14px", "marginRight": "6px", "marginBottom": "4px"}, "binds": [{"wid": "self", "event": "click", "actiontype": "urlwidget", "target": "app.rag_main_content", "mode": "replace", "options": {"url": base_url + '?kb_id=' + k.id + '&media_type=' + media_type}}]}) - # Type tabs type_tabs = [] for mt, mt_label in [('document', '📄 文档'), ('face', '👤 人脸'), ('voice', '🎤 声纹')]: - type_tabs.append({ - "widgettype": "Button", - "options": {"label": mt_label, "bgcolor": "#3b82f6" if mt==media_type else "#e5e7eb", "color": "#fff" if mt==media_type else "#666", "cfontsize": 12, "padding": "6px 16px", "marginRight": "4px"}, - "binds": [{"wid": "self", "event": "click", "actiontype": "urlwidget", "target": "app.rag_main_content", "mode": "replace", - "options": {"url": base_url + '?kb_id=' + kb_id + '&media_type=' + mt}}]} - ) + type_tabs.append({"widgettype": "Button", "options": {"label": mt_label, "bgcolor": "#3b82f6" if mt == media_type else "#e5e7eb", "color": "#fff" if mt == media_type else "#666", "cfontsize": 12, "padding": "6px 16px", "marginRight": "4px"}, "binds": [{"wid": "self", "event": "click", "actiontype": "urlwidget", "target": "app.rag_main_content", "mode": "replace", "options": {"url": base_url + '?kb_id=' + kb_id + '&media_type=' + mt}}]}) - result = { - "widgettype": "VBox", - "options": {"cheight": 40, "width": "100%", "padding": "16px", "spacing": "12px"}, - "subwidgets": [ + result = json.dumps({"widgettype": "VBox", "options": {"cheight": 40, "width": "100%", "padding": "16px", "spacing": "12px"}, "subwidgets": [ {"widgettype": "Text", "options": {"text": "🏷️ 标签分配", "cfontsize": 20, "fontWeight": "bold", "color": "#333"}}, {"widgettype": "Text", "options": {"text": "为文档、人脸、声纹设置标签", "cfontsize": 13, "color": "#888"}}, - # KB {"widgettype": "Text", "options": {"text": "知识库:", "cfontsize": 13, "color": "#555", "marginTop": "8px"}}, {"widgettype": "HBox", "options": {"spacing": "4px", "wrap": True}, "subwidgets": kb_buttons}, - # Media type {"widgettype": "Text", "options": {"text": "媒体类型:", "cfontsize": 13, "color": "#555", "marginTop": "4px"}}, {"widgettype": "HBox", "options": {"spacing": "4px"}, "subwidgets": type_tabs}, - # Media list - {"widgettype": "Text", "options": {"text": '选择' + ('文档' if media_type=='document' else ('人脸' if media_type=='face' else '声纹')) + ':', "cfontsize": 13, "color": "#555", "marginTop": "8px"}}, + {"widgettype": "Text", "options": {"text": "选择" + ('文档' if media_type == 'document' else ('人脸' if media_type == 'face' else '声纹')) + ":", "cfontsize": 13, "color": "#555", "marginTop": "8px"}}, {"widgettype": "VBox", "options": {"cheight": 12, "bgcolor": "#fafafa", "padding": "8px", "border": "1px solid #e0e0e0", "overflowY": "auto"}, "subwidgets": media_buttons}, - # Current tags {"widgettype": "Text", "options": {"text": "当前标签:", "cfontsize": 13, "color": "#555", "marginTop": "8px"}}, - {"widgettype": "HBox", "options": {"spacing": "4px", "wrap": True}, "subwidgets": [ - {"widgettype": "Button", "options": {"label": t['name'], "bgcolor": t['color'], "color": "#fff", "cfontsize": 11, "padding": "3px 10px", "marginRight": "4px", "marginBottom": "4px"}, - "binds": [{"wid": "self", "event": "click", "actiontype": "urlwidget", "target": "app.rag_main_content", "mode": "replace", - "options": {"url": base_url + '?kb_id=' + kb_id + '&media_type=' + media_type + '&media_id=' + media_id + '&action=unassign&tag_id=' + t['id']}}]} - for t in current_tags] if current_tags else [{"widgettype": "Text", "options": {"text": "(无)", "color": "#aaa", "cfontsize": 12}}]}, - # Available tags + {"widgettype": "HBox", "options": {"spacing": "4px", "wrap": True}, "subwidgets": current_tags and [{"widgettype": "Button", "options": {"label": t['name'], "bgcolor": t['color'], "color": "#fff", "cfontsize": 11, "padding": "3px 10px", "marginRight": "4px", "marginBottom": "4px"}, "binds": [{"wid": "self", "event": "click", "actiontype": "urlwidget", "target": "app.rag_main_content", "mode": "replace", "options": {"url": base_url + '?kb_id=' + kb_id + '&media_type=' + media_type + '&media_id=' + media_id + '&action=unassign&tag_id=' + t['id']}}]} for t in current_tags] or [{"widgettype": "Text", "options": {"text": "(无)", "color": "#aaa", "cfontsize": 12}}]}, {"widgettype": "Text", "options": {"text": "可用标签 (点击切换):", "cfontsize": 13, "color": "#555", "marginTop": "4px"}}, - {"widgettype": "HBox", "options": {"spacing": "4px", "wrap": True}, "subwidgets": tag_buttons} if media_id else [ - {"widgettype": "Text", "options": {"text": "请先选择媒体文件", "color": "#aaa", "cfontsize": 12, "padding": "12px 0"}} - ] - ] -} - result + {"widgettype": "HBox", "options": {"spacing": "4px", "wrap": True}, "subwidgets": tag_buttons} if media_id else [{"widgettype": "Text", "options": {"text": "请先选择媒体文件", "color": "#aaa", "cfontsize": 12, "padding": "12px 0"}}] + ]}) + +return json.loads(result) diff --git a/wwwroot/tag_search/index.dspy b/wwwroot/tag_search/index.dspy index 3f1ca38..1ee474e 100644 --- a/wwwroot/tag_search/index.dspy +++ b/wwwroot/tag_search/index.dspy @@ -99,4 +99,4 @@ async with db.sqlorContext(dbname) as sor: {"widgettype": "VBox", "options": {"spacing": "4px"}, "subwidgets": results_widgets} ] } - result + return result