Selaa lähdekoodia

Make run_context.json self-contained and fix visualization issues

- Embed search results directly into run_context.json rounds
  - Each round now includes search_results field with full post data
  - Eliminates dependency on external search_results.json
  - Visualization can work with run_context.json alone

- Fix evaluation reason display in visualization
  - Support both camelCase (evaluationReason) and snake_case (evaluation_reason)
  - Add selectedWord field display for add_word strategy nodes
  - Fix field name mapping in transformData function

- Enhance visualization compatibility
  - Prioritize embedded search_results in rounds
  - Maintain backward compatibility with external search_results.json
  - Add informative console messages for data source detection

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
yangxiaohui 1 kuukausi sitten
vanhempi
commit
b03c4d0f9b

+ 21 - 1
sug_v6_1_2_8.py

@@ -602,6 +602,25 @@ async def run_round(
             existing_seed_texts.add(sug.text)
             print(f"  ✓ 新seed: {sug.text} (分数: {sug.score_with_o:.2f} > 来源query: {sug.from_q.score_with_o:.2f})")
 
+    # 序列化搜索结果数据(包含帖子详情)
+    search_results_data = []
+    for search in search_list:
+        search_results_data.append({
+            "text": search.text,
+            "score_with_o": search.score_with_o,
+            "post_list": [
+                {
+                    "note_id": post.note_id,
+                    "note_url": post.note_url,
+                    "title": post.title,
+                    "body_text": post.body_text,
+                    "images": post.images,
+                    "interact_info": post.interact_info
+                }
+                for post in search.post_list
+            ]
+        })
+
     # 记录本轮数据
     round_data.update({
         "sug_count": len(all_sugs),
@@ -614,7 +633,8 @@ async def run_round(
         "output_q_list": [{"text": q.text, "score": q.score_with_o, "reason": q.reason, "from": q.from_source} for q in q_list_next],
         "seed_list_next": [{"text": seed.text, "from": seed.from_type, "score": seed.score_with_o} for seed in seed_list_next],  # 下一轮种子列表
         "sug_details": sug_details,  # 每个Q对应的sug列表
-        "add_word_details": add_word_details  # 每个seed对应的组合词列表
+        "add_word_details": add_word_details,  # 每个seed对应的组合词列表
+        "search_results": search_results_data  # 搜索结果(包含帖子详情)
     })
     context.rounds.append(round_data)
 

+ 5 - 3
visualization/sug_v6_1_2_8/convert_v8_to_graph_v2.js

@@ -235,9 +235,11 @@ function convertV8ToGraphV2(runContext, searchResults) {
       iterations[roundNum * 10].push(searchStepId);
 
       // 只有在有搜索结果时才添加搜索词和帖子
-      if (round.search_count > 0 && searchResults) {
-        if (Array.isArray(searchResults)) {
-          searchResults.forEach((search, searchIndex) => {
+      // 优先使用 round.search_results(新格式),否则使用外部传入的 searchResults(兼容旧版本)
+      const roundSearchResults = round.search_results || searchResults;
+      if (round.search_count > 0 && roundSearchResults) {
+        if (Array.isArray(roundSearchResults)) {
+          roundSearchResults.forEach((search, searchIndex) => {
             const searchWordId = `search_${search.text}_r${roundNum}_${searchIndex}`;
             nodes[searchWordId] = {
               type: 'search_word',

+ 20 - 4
visualization/sug_v6_1_2_8/index.js

@@ -25,12 +25,14 @@ if (inputData.rounds && inputData.o) {
   // v6.1.2.8 格式,需要转换
   console.log('✨ 检测到 v6.1.2.8 格式,正在转换为图结构...');
 
-  // 尝试读取 search_results.json
+  // 尝试读取 search_results.json(兼容旧版本)
   let searchResults = null;
   const searchResultsPath = path.join(path.dirname(inputFile), 'search_results.json');
   if (fs.existsSync(searchResultsPath)) {
-    console.log('📄 读取搜索结果数据...');
+    console.log('📄 读取外部搜索结果数据(兼容模式)...');
     searchResults = JSON.parse(fs.readFileSync(searchResultsPath, 'utf-8'));
+  } else {
+    console.log('✅ 使用 run_context.json 中的内嵌搜索结果');
   }
 
   // 使用新的转换函数(按 Round > 步骤 > 数据 组织)
@@ -316,6 +318,19 @@ function QueryNode({ id, data, sourcePosition, targetPosition }) {
                 <strong>Parent:</strong> {data.parent}
               </div>
             )}
+            {data.selectedWord && (
+              <div style={{
+                marginTop: '6px',
+                paddingTop: '6px',
+                borderTop: '1px solid #f3f4f6',
+                fontSize: '10px',
+                color: '#6b7280',
+                lineHeight: '1.5',
+              }}>
+                <strong style={{ color: '#4b5563' }}>选择词:</strong>
+                <span style={{ marginLeft: '4px', color: '#3b82f6', fontWeight: '500' }}>{data.selectedWord}</span>
+              </div>
+            )}
             {data.evaluationReason && (
               <div style={{
                 marginTop: '6px',
@@ -907,7 +922,7 @@ function transformData(data) {
             isSelected: node.is_selected !== undefined ? node.is_selected : true,
             imageList: node.image_list || [],
             noteUrl: node.note_url || '',
-            evaluationReason: node.evaluation_reason || '',
+            evaluationReason: node.evaluationReason || node.evaluation_reason || '',
             interact_info: node.interact_info || {},
             nodeType: nodeType,
           },
@@ -928,10 +943,11 @@ function transformData(data) {
             strategy: node.strategy || '',
             parent: node.parent_query || '',
             isSelected: node.is_selected !== undefined ? node.is_selected : true,
-            evaluationReason: node.evaluation_reason || '',
+            evaluationReason: node.evaluationReason || node.evaluation_reason || '',
             nodeType: nodeType, // 传递实际节点类型用于样式
             searchCount: node.search_count, // search 节点特有
             totalPosts: node.total_posts, // search 节点特有
+            selectedWord: node.selected_word || '', // 加词节点特有 - 显示选择的词
           },
           position: { x: 0, y: 0 },
         });