ss900371tw commited on
Commit
fff69cd
·
verified ·
1 Parent(s): c692541

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +81 -128
src/streamlit_app.py CHANGED
@@ -517,140 +517,93 @@ elif 'json_data_for_batch' in st.session_state:
517
  if st.session_state.execute_batch_analysis and 'json_data_for_batch' in st.session_state and st.session_state.json_data_for_batch is not None:
518
  st.session_state.execute_batch_analysis = False
519
  start_time = time.time()
520
-
521
- # 執行前確保清空結果
522
- st.session_state.batch_results = []
523
 
524
- if inference_client is None:
525
- st.error("Client 未連線,無法執行。請檢查 HF_TOKEN 和模型設定。")
526
- else:
527
- logs_list = st.session_state.json_data_for_batch
 
 
 
 
 
528
 
529
- if logs_list:
530
- vs = st.session_state.get("vector_store", None)
531
-
532
- # 將 Log 條目轉換為 JSON 字串,用於 LLM 輸入
533
- formatted_logs = [json.dumps(log, indent=2, ensure_ascii=False) for log in logs_list]
534
-
535
- analysis_sequences = []
536
-
537
- # --- 核心修改:基於 IP 關聯的 Log Sequence 建構 ---
538
- for i in range(len(formatted_logs)):
539
- current_log_entry = logs_list[i]
540
- current_log_str = formatted_logs[i]
541
-
542
- # 嘗試從當前 Log 條目中提取 IP 地址 (優先 W3C 格式,然後是一般日誌格式)
543
- # 這裡需要根據您的日誌格式調整 key,常見的有 c_ip, remote_addr, source_ip
544
- # 我們使用 W3C 和一般日誌中常見的 key
545
- target_ip = (current_log_entry.get('c_ip') or
546
- current_log_entry.get('c_ip') or
547
- current_log_entry.get('remote_addr') or
548
- current_log_entry.get('source_ip') or
549
- current_log_entry.get('client_ip'))
550
-
551
- sequence_text = []
552
- correlated_logs = []
553
-
554
- # 檢查 IP 是否有效
555
- if target_ip and target_ip != "-":
556
-
557
- # 篩選過去的 Log,最多 WINDOW_SIZE - 1 個,且 IP 必須匹配
558
- # 從 i-1 倒序檢查到 0
559
- for j in range(i - 1, -1, -1):
560
- prior_log_entry = logs_list[j]
561
- prior_ip = (prior_log_entry.get('c_ip') or
562
- prior_log_entry.get('c_ip') or
563
- prior_log_entry.get('remote_addr') or
564
- prior_log_entry.get('source_ip') or
565
- prior_log_entry.get('client_ip'))
566
-
567
- # 檢查 IP 是否匹配
568
- if prior_ip == target_ip:
569
- # 插入到最前面,保持時間順序
570
- correlated_logs.insert(0, formatted_logs[j])
571
-
572
- # 限制累積的 Log 數量(不包含當前 Log)
573
- if len(correlated_logs) >= WINDOW_SIZE - 1:
574
- break
575
-
576
- # 1. 加入相關聯的 Log (時間較早的)
577
- for log_str in correlated_logs:
578
- sequence_text.append(f"--- Correlated Log (IP:{target_ip}) ---\n{log_str}")
579
-
580
- else:
581
- # 如果沒有找到 IP,只分析當前 Log (確保 sequence_text 不是空的)
582
- pass # sequence_text 最終只會包含 TARGET LOG
583
-
584
- # 2. 加入當前的目標 Log
585
- sequence_text.append(f"--- TARGET LOG TO ANALYZE (Index {i+1}) ---\n{current_log_str}")
586
-
587
- analysis_sequences.append({
588
- "sequence_text": "\n\n".join(sequence_text),
589
- "target_log_id": i + 1,
590
- "original_log_entry": logs_list[i]
591
- })
592
-
593
- # --- LLM 執行迴圈 ---
594
- total_sequences = len(analysis_sequences)
595
- st.header(f"⚡ 批量分析執行中 (IP 關聯視窗 $N={WINDOW_SIZE}$)...")
596
- progress_bar = st.progress(0, text=f"準備處理 {total_sequences} 個序列...")
597
-
598
- # 使用一個佔位符來顯示即時進度或警告,而不是結果
599
- status_placeholder = st.empty()
600
-
601
- for i, seq_data in enumerate(analysis_sequences):
602
- log_id = seq_data["target_log_id"]
603
 
604
- # 顯示進度
605
- progress_bar.progress((i + 1) / total_sequences, text=f"Processing {i + 1}/{total_sequences} (Log #{log_id})...")
606
- status_placeholder.text(f"正在分析 Log #{log_id} (IP 序列長度: {seq_data['sequence_text'].count('---')})...")
 
 
 
 
 
 
607
 
608
- try:
609
- response, retrieved_ctx = generate_rag_response_hf_for_log(
610
- client=inference_client,
611
- model_id=MODEL_ID,
612
- log_sequence_text=seq_data["sequence_text"],
613
- user_prompt=analysis_prompt,
614
- sys_prompt=system_prompt,
615
- vector_store=vs,
616
- threshold=similarity_threshold,
617
- max_output_tokens=max_output_tokens,
618
- temperature=temperature,
619
- top_p=top_p
620
- )
621
-
622
- item = {
623
- "log_id": log_id,
624
- "log_content": seq_data["original_log_entry"],
625
- "sequence_analyzed": seq_data["sequence_text"],
626
- "analysis_result": response,
627
- "context": retrieved_ctx
628
- }
629
 
630
- st.session_state.batch_results.append(item)
631
-
632
- except Exception as e:
633
- st.session_state.batch_results.append({
634
- "log_id": log_id,
635
- "log_content": seq_data["original_log_entry"],
636
- "sequence_analyzed": seq_data["sequence_text"],
637
- "analysis_result": f"Model Execution Error: {e}",
638
- "context": ""
639
- })
640
- status_placeholder.error(f"Error Log {log_id}: {e}")
641
-
642
- end_time = time.time()
643
- progress_bar.empty()
644
- status_placeholder.empty()
645
- st.success(f"完成!耗時 {end_time - start_time:.2f} 秒。")
646
-
647
- # 由於結果已在 session state 中,觸發一次重新運行以顯示歷史結果
648
- # 這是必要的,因為批量分析在一個 if 區塊內執行,需要重新執行腳本來執行後續的顯示邏輯。
649
- st.rerun()
650
-
651
- else:
652
- st.error("無法提取有效 Log,請檢查檔案格式。")
 
 
 
 
 
653
 
 
 
 
 
 
 
 
654
  # === 顯示結果 (歷史紀錄) - 已修改為持久顯示結果,而非僅在執行時顯示 ===
655
  if st.session_state.get("batch_results") and isinstance(st.session_state.batch_results, list) and st.session_state.batch_results:
656
  st.header("⚡ 歷史分析結果")
 
517
  if st.session_state.execute_batch_analysis and 'json_data_for_batch' in st.session_state and st.session_state.json_data_for_batch is not None:
518
  st.session_state.execute_batch_analysis = False
519
  start_time = time.time()
 
 
 
520
 
521
+ # ... (前面的初始化和序列建構邏輯保持不變) ...
522
+
523
+ if logs_list:
524
+ # ... (序列建構邏輯保持不變) ...
525
+
526
+ # --- LLM 執行迴圈 ---
527
+ total_sequences = len(analysis_sequences)
528
+ st.header(f"⚡ 批量分析執行中 (IP 關聯視窗 $N={WINDOW_SIZE}$)...")
529
+ progress_bar = st.progress(0, text=f"準備處理 {total_sequences} 個序列...")
530
 
531
+ # 【修改點 1】創建一個佔位符來顯示即時結果
532
+ st.subheader("即時���析結果")
533
+ results_placeholder = st.empty() # 使用 st.empty() 或 st.container()
534
+
535
+ # 【修改點 2】使用一個清單來累積即時顯示的內容 (Markdown 格式)
536
+ # 這樣可以避免每次都重畫整個容器,但 Streamlit 的限制是 'empty' 每次都會覆蓋
537
+ # 最好的方式是使用 st.container() 並在其內部 append 內容
538
+
539
+ real_time_container = st.container() # 創建一個新的容器用於即時輸出
540
+
541
+ for i, seq_data in enumerate(analysis_sequences):
542
+ log_id = seq_data["target_log_id"]
543
+
544
+ # 顯示進度
545
+ progress_bar.progress((i + 1) / total_sequences, text=f"Processing {i + 1}/{total_sequences} (Log #{log_id})...")
546
+ # results_placeholder.text(f"正在分析 Log #{log_id} (IP 序列長度: {seq_data['sequence_text'].count('---')})...")
547
+ # 移除 text 狀態顯示,改用容器顯示結果
548
+
549
+ try:
550
+ # ... (模型呼叫邏輯保持不變) ...
551
+ response, retrieved_ctx = generate_rag_response_hf_for_log(
552
+ # ... (參數保持不變) ...
553
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
554
 
555
+ item = {
556
+ "log_id": log_id,
557
+ "log_content": seq_data["original_log_entry"],
558
+ "sequence_analyzed": seq_data["sequence_text"],
559
+ "analysis_result": response,
560
+ "context": retrieved_ctx
561
+ }
562
+
563
+ st.session_state.batch_results.append(item)
564
 
565
+ # 【修改點 3】即時渲染當前 Log 的分析結果到 real_time_container 內
566
+ with real_time_container:
567
+ response_lower = response.lower()
568
+ is_high = 'high-risk detected!' in response_lower
569
+ is_medium = 'medium-risk detected!' in response_lower
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
570
 
571
+ if is_high:
572
+ header_text = f"Log/Alert #{log_id} (HIGH RISK DETECTED) 🔴"
573
+ st.subheader(header_text)
574
+ st.error(response)
575
+ elif is_medium:
576
+ header_text = f"Log/Alert #{log_id} (MEDIUM RISK DETECTED) 🟠"
577
+ st.subheader(header_text)
578
+ st.warning(response)
579
+ else:
580
+ header_text = f"Log/Alert #{log_id} (Low/No Risk Detected) ⚪"
581
+ st.subheader(header_text)
582
+ st.info(response) # 使用 info ��顯示不具備高/中風險的結果
583
+
584
+ # 為了視覺區隔
585
+ st.markdown("---")
586
+
587
+ except Exception as e:
588
+ # ... (錯誤處理邏輯保持不變) ...
589
+ with real_time_container:
590
+ st.subheader(f"Log/Alert #{log_id} (Execution Error) ❌")
591
+ st.exception(e)
592
+ st.markdown("---")
593
+
594
+ # ... (迴圈結束後的清理邏輯) ...
595
+ end_time = time.time()
596
+ progress_bar.empty()
597
+ # status_placeholder.empty() # 由於我們移除了 status_placeholder,這裡也移除
598
+ st.success(f"完成!耗時 {end_time - start_time:.2f} 秒。")
599
 
600
+ # 【修改點 4】移除 st.rerun()。因為結果已經即時顯示,不需要額外的重新運行。
601
+ # st.rerun() # <--- 移除此行
602
+
603
+ else:
604
+ st.error("無法提取有效 Log,請檢查檔案格式。")
605
+
606
+
607
  # === 顯示結果 (歷史紀錄) - 已修改為持久顯示結果,而非僅在執行時顯示 ===
608
  if st.session_state.get("batch_results") and isinstance(st.session_state.batch_results, list) and st.session_state.batch_results:
609
  st.header("⚡ 歷史分析結果")