Spaces:
Running
Running
File size: 7,622 Bytes
143d94c ac50cc2 143d94c 0091076 143d94c 0091076 143d94c 0091076 143d94c 0091076 143d94c 0091076 143d94c ac50cc2 0091076 ac50cc2 143d94c 0091076 143d94c 0091076 143d94c 0091076 143d94c 0091076 143d94c 0091076 143d94c 0091076 143d94c 20d0a16 143d94c 20d0a16 143d94c ac50cc2 cb898e1 143d94c 0091076 143d94c 20d0a16 0091076 143d94c 0091076 20d0a16 0091076 cb898e1 143d94c 20d0a16 cb898e1 20d0a16 0091076 20d0a16 cb898e1 20d0a16 cb898e1 143d94c 0091076 143d94c 0091076 143d94c 0091076 143d94c 0091076 143d94c |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 |
#!/usr/bin/env python3
"""
Покращений модуль для повного логування діагностичних сесій MAI-DX
з детальним захопленням розмов між агентами. (ВЕРСІЯ З ВИПРАВЛЕНИМ ПАРСЕРОМ І ПОВЕРНЕНИМ ВИВОДОМ)
"""
import os
import io
import sys
import json
import time
import uuid
import re
import logging
from datetime import datetime
from typing import List, Dict, Any, Optional, Callable
from dataclasses import dataclass, asdict, field
@dataclass
class AgentMessage:
timestamp: str
agent_name: str
message_type: str
content: str
metadata: Dict[str, Any] = field(default_factory=dict)
@dataclass
class AgentConversation:
round_number: int
start_time: str
end_time: Optional[str] = None
messages: List[AgentMessage] = field(default_factory=list)
decision: Optional[str] = None
cost_incurred: float = 0.0
@dataclass
class DiagnosisSession:
case_id: str
timestamp: str
case_name: str
patient_info: str
mode: str
budget: int = 0
diagnosis: str = "N/A"
confidence: float = 0.0
cost: float = 0.0
iterations: int = 0
duration: float = 0.0
status: str = "In Progress"
reasoning: str = "N/A"
conversations: List[AgentConversation] = field(default_factory=list)
raw_output: str = ""
class EnhancedOutputCapture:
def __init__(self):
self.captured_output = io.StringIO()
self.original_stdout = sys.stdout
def __enter__(self):
sys.stdout = self
return self
def __exit__(self, exc_type, exc_val, exc_tb):
sys.stdout = self.original_stdout
def write(self, text):
"""
FIX: Повертаємо вивід в консоль.
Цей метод тепер і записує вивід у буфер для парсингу,
і дублює його в оригінальну консоль.
"""
self.captured_output.write(text)
self.original_stdout.write(text)
def flush(self):
self.original_stdout.flush()
def get_value(self):
return self._strip_ansi_codes(self.captured_output.getvalue())
@staticmethod
def _strip_ansi_codes(text):
ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
return ansi_escape.sub('', text)
class MAIDxConversationLogger:
def __init__(self, log_dir: str = "mai_dx_logs"):
self.log_dir = log_dir
os.makedirs(self.log_dir, exist_ok=True)
def create_session(self, case_name: str, patient_info: str, mode: str, budget: int) -> DiagnosisSession:
case_id = f"case_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{uuid.uuid4().hex[:6]}"
return DiagnosisSession(
case_id=case_id,
timestamp=datetime.now().isoformat(),
case_name=case_name,
patient_info=patient_info,
mode=mode,
budget=budget
)
def finalize_and_save_session(self, session: DiagnosisSession, result: Any, raw_output: str, duration: float) -> DiagnosisSession:
session.raw_output = raw_output
self._parse_captured_output(session, raw_output)
session.diagnosis = getattr(result, 'final_diagnosis', 'N/A')
session.confidence = getattr(result, 'accuracy_score', 0.0)
session.cost = getattr(result, 'total_cost', 0.0)
session.reasoning = getattr(result, 'accuracy_reasoning', 'N/A')
session.iterations = len(session.conversations)
session.status = "✅ Успішно" if session.confidence >= 3.0 else "⚠️ Потребує перегляду"
session.duration = duration
self._save_session_to_file(session)
self.export_conversation_html(session)
return session
def _parse_captured_output(self, session: DiagnosisSession, captured_text: str):
lines = captured_text.split('\n')
current_round: Optional[AgentConversation] = None
current_agent: Optional[str] = None
buffer: List[str] = []
in_agent_output = False
round_number = 0
for line in lines:
stripped_line = line.strip()
if "Agent Name" in line and ("╭" in line or "┌" in line):
if not current_round or in_agent_output:
round_number += 1
if current_round:
current_round.end_time = datetime.now().isoformat()
current_round = AgentConversation(round_number=round_number, start_time=datetime.now().isoformat())
session.conversations.append(current_round)
agent_match = re.search(r'Agent Name (.*?) (?:\[|\s)', line)
if agent_match:
current_agent = agent_match.group(1).strip()
in_agent_output = True
buffer = []
continue
if in_agent_output and ("╰" in line or "└" in line):
if buffer and current_agent and current_round:
self._add_agent_message(current_round, current_agent, '\n'.join(buffer))
in_agent_output, current_agent, buffer = False, None, []
continue
if in_agent_output:
clean_line = re.sub(r'^[│|]\s*|\s*[│|]\s*$', '', line)
buffer.append(clean_line)
continue
if current_round:
current_round.end_time = datetime.now().isoformat()
def _add_agent_message(self, conversation: AgentConversation, agent_name: str, content: str):
message_type = "output"
formatted_content = content.strip()
if "Structured Output - Attempting Function Call Execution" in formatted_content:
message_type = "function_call"
elif "Score:" in formatted_content and "Justification:" in formatted_content:
message_type = "judgement"
elif "No tests have been proposed" in formatted_content:
message_type = "status_update"
message = AgentMessage(
datetime.now().isoformat(), agent_name, message_type, formatted_content, {'raw_content': content}
)
conversation.messages.append(message)
def _save_session_to_file(self, session: DiagnosisSession):
file_path = os.path.join(self.log_dir, f"{session.case_id}.json")
with open(file_path, 'w', encoding='utf-8') as f:
json.dump(asdict(session), f, ensure_ascii=False, indent=2)
raw_output_path = os.path.join(self.log_dir, f"{session.case_id}_raw.txt")
with open(raw_output_path, 'w', encoding='utf-8') as f:
f.write(session.raw_output)
def export_conversation_html(self, session: DiagnosisSession) -> str:
html_path = os.path.join(self.log_dir, f"{session.case_id}_conversation.html")
html_content = f"<html><head><title>Log - {session.case_id}</title></head><body>"
html_content += f"<h1>Session: {session.case_id}</h1>"
for conv in session.conversations:
html_content += f"<h2>Round {conv.round_number}</h2>"
for msg in conv.messages:
html_content += f"<div><b>{msg.agent_name}</b> [{msg.message_type}]:<pre>{msg.content}</pre></div>"
html_content += "</body></html>"
with open(html_path, 'w', encoding='utf-8') as f:
f.write(html_content)
return html_path |