Severian commited on
Commit
a7a635b
·
verified ·
1 Parent(s): a47d087

Update response_formatter.py

Browse files
Files changed (1) hide show
  1. response_formatter.py +114 -145
response_formatter.py CHANGED
@@ -1,158 +1,127 @@
1
- from typing import Dict, Optional, Tuple, List, Any, Set
2
- import re
3
- import xml.etree.ElementTree as ET
4
- from datetime import datetime
5
- import json
6
- import logging
 
 
 
 
7
 
8
- # Setup logger
9
- logger = logging.getLogger(__name__)
10
- logger.setLevel(logging.INFO)
 
11
 
12
- # Create console handler if needed
13
- if not logger.handlers:
14
- ch = logging.StreamHandler()
15
- ch.setLevel(logging.INFO)
16
- formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
17
- ch.setFormatter(formatter)
18
- logger.addHandler(ch)
 
 
 
 
 
19
 
20
- class StreamingFormatter:
21
- def __init__(self):
22
- self.processed_events = set()
23
- self.current_tool_outputs = []
24
- self.current_citations = []
25
- self.current_metadata = {}
26
- self.current_message_id = None
27
- self.current_message_buffer = ""
28
 
29
- def reset(self):
30
- """Reset the formatter state"""
31
- self.processed_events.clear()
32
- self.current_tool_outputs.clear()
33
- self.current_citations.clear()
34
- self.current_metadata.clear()
35
- self.current_message_id = None
36
- self.current_message_buffer = ""
37
 
38
- def append_to_buffer(self, text: str):
39
- """Append text to the current message buffer"""
40
- self.current_message_buffer += text
 
 
 
 
 
 
 
41
 
42
- def get_and_clear_buffer(self) -> str:
43
- """Get the current buffer content and clear it"""
44
- content = self.current_message_buffer
45
- self.current_message_buffer = ""
46
- return content
 
 
 
 
 
 
 
 
 
 
47
 
48
- class ToolType:
49
- DUCKDUCKGO = "duckduckgo_search"
50
- REDDIT_NEWS = "reddit_x_gnews_newswire_crunchbase"
51
- PUBMED = "pubmed_search"
52
- CENSUS = "get_census_data"
53
- HEATMAP = "heatmap_code"
54
- MERMAID = "mermaid_diagram"
55
- WISQARS = "wisqars"
56
- WONDER = "wonder"
57
- NCHS = "nchs"
58
- ONESTEP = "onestep"
59
- DQS = "dqs_nhis_adult_summary_health_statistics"
60
 
61
- class ResponseFormatter:
62
- _instance = None
63
-
64
- def __new__(cls):
65
- if cls._instance is None:
66
- cls._instance = super(ResponseFormatter, cls).__new__(cls)
67
- cls._instance.streaming_state = StreamingFormatter()
68
- cls._instance.logger = logger
69
- return cls._instance
70
 
71
- def format_thought(
72
- self,
73
- thought: str,
74
- observation: str,
75
- tool_outputs: List[Dict] = None,
76
- event_id: str = None,
77
- message_id: str = None
78
- ) -> Optional[Tuple[str, str]]:
79
- """Format thought and tool outputs as XML"""
80
- root = ET.Element("agent_response")
81
-
82
- if thought:
83
- thought_elem = ET.SubElement(root, "thought")
84
- thought_elem.text = thought
85
-
86
- if observation:
87
- obs_elem = ET.SubElement(root, "observation")
88
- obs_elem.text = observation
89
-
90
- if tool_outputs:
91
- tools_elem = ET.SubElement(root, "tool_outputs")
92
- for output in tool_outputs:
93
- tool_elem = ET.SubElement(tools_elem, "tool_output")
94
- tool_elem.attrib["type"] = output.get("type", "")
95
- tool_elem.text = output.get("content", "")
96
-
97
- xml_output = ET.tostring(root, encoding='unicode')
98
- return thought, xml_output
99
 
100
- def format_message(
101
- self,
102
- message: str,
103
- event_id: str = None,
104
- message_id: str = None
105
- ) -> Optional[Tuple[str, str]]:
106
- """Format message as XML for frontend"""
107
- if not message:
108
- return None
109
-
110
- root = ET.Element("agent_response")
111
- msg_elem = ET.SubElement(root, "message")
112
- msg_elem.text = message
113
-
114
- xml_output = ET.tostring(root, encoding='unicode')
115
- return message, xml_output
116
 
117
- def format_error(
118
- self,
119
- error: str,
120
- event_id: str = None,
121
- message_id: str = None
122
- ) -> Optional[Tuple[str, str]]:
123
- """Format error message for both terminal and XML output"""
124
- # Skip if already processed
125
- if event_id and event_id in self.streaming_state.processed_events:
126
- return None
127
-
128
- # Handle message state
129
- if message_id != self.streaming_state.current_message_id:
130
- self.streaming_state.reset()
131
- self.streaming_state.current_message_id = message_id
132
-
133
- # Skip empty errors
134
- if not error:
135
- return None
136
-
137
- # Terminal format
138
- terminal_output = f"Error: {error}"
139
 
140
- # XML format
141
- root = ET.Element("agent_response")
142
- error_elem = ET.SubElement(root, "error")
143
- error_elem.text = error
144
-
145
- xml_output = ET.tostring(root, encoding='unicode')
146
-
147
- # Track processed event
148
- if event_id:
149
- self.streaming_state.processed_events.add(event_id)
150
-
151
- return terminal_output, xml_output
 
 
 
 
 
 
 
 
 
 
 
152
 
153
- @staticmethod
154
- def _clean_markdown(text: str) -> str:
155
- """Clean markdown formatting from text"""
156
- text = re.sub(r'```.*?```', '', text, flags=re.DOTALL)
157
- text = re.sub(r'[*_`#]', '', text)
158
- return re.sub(r'\n{3,}', '\n\n', text.strip())
 
 
 
1
+ export class LogicController {
2
+ constructor() {
3
+ this.storage = {
4
+ xmlBuffer: '',
5
+ isFirstMessage: true,
6
+ toolOutputs: new Map()
7
+ };
8
+
9
+ this.eventDispatcher = new EventTarget();
10
+ }
11
 
12
+ processStreamData(chunk) {
13
+ try {
14
+ const rawData = chunk.data || chunk;
15
+ console.log('Raw chunk received:', rawData);
16
 
17
+ if (typeof rawData === 'string' && rawData.includes('<agent_response>')) {
18
+ this.storage.xmlBuffer += rawData;
19
+
20
+ // Process complete messages
21
+ while (this.storage.xmlBuffer.includes('<agent_response>')) {
22
+ const startTag = '<agent_response>';
23
+ const endTag = '</agent_response>';
24
+
25
+ const startIndex = this.storage.xmlBuffer.indexOf(startTag);
26
+ const endIndex = this.storage.xmlBuffer.indexOf(endTag);
27
+
28
+ if (startIndex === -1 || endIndex === -1) break;
29
 
30
+ // Extract complete message
31
+ const completeMessage = this.storage.xmlBuffer.slice(
32
+ startIndex,
33
+ endIndex + endTag.length
34
+ );
 
 
 
35
 
36
+ // Process message content
37
+ this.processMessage(completeMessage);
 
 
 
 
 
 
38
 
39
+ // Remove processed message
40
+ this.storage.xmlBuffer = this.storage.xmlBuffer.slice(
41
+ endIndex + endTag.length
42
+ );
43
+ }
44
+ }
45
+ } catch (error) {
46
+ console.error('Error processing stream data:', error);
47
+ }
48
+ }
49
 
50
+ processMessage(xmlMessage) {
51
+ try {
52
+ // Parse message content
53
+ const messageMatch = xmlMessage.match(/<message>(.*?)<\/message>/s);
54
+ const thoughtMatch = xmlMessage.match(/<thought>(.*?)<\/thought>/s);
55
+ const toolOutputsMatch = xmlMessage.match(/<tool_outputs>(.*?)<\/tool_outputs>/s);
56
+
57
+ // Handle regular message
58
+ if (messageMatch?.[1]) {
59
+ this.dispatchUpdate('chatwindow', {
60
+ message: messageMatch[1].trim(),
61
+ replace: false,
62
+ format: true
63
+ });
64
+ }
65
 
66
+ // Handle thought content
67
+ if (thoughtMatch?.[1]) {
68
+ this.dispatchUpdate('chatwindow', {
69
+ message: thoughtMatch[1].trim(),
70
+ replace: false,
71
+ format: true,
72
+ isThought: true
73
+ });
74
+ }
 
 
 
75
 
76
+ // Handle tool outputs
77
+ if (toolOutputsMatch?.[1]) {
78
+ this.processToolOutputs(toolOutputsMatch[1]);
79
+ }
 
 
 
 
 
80
 
81
+ } catch (error) {
82
+ console.error('Error processing message:', error);
83
+ }
84
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
 
86
+ processToolOutputs(toolOutputsXml) {
87
+ try {
88
+ const parser = new DOMParser();
89
+ const doc = parser.parseFromString(toolOutputsXml, 'text/xml');
90
+ const outputs = doc.getElementsByTagName('tool_output');
 
 
 
 
 
 
 
 
 
 
 
91
 
92
+ Array.from(outputs).forEach(output => {
93
+ const type = output.getAttribute('type');
94
+ const content = output.textContent;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
 
96
+ // Store tool output
97
+ const outputId = `${type}-${Date.now()}`;
98
+ this.storage.toolOutputs.set(outputId, {
99
+ type,
100
+ content,
101
+ timestamp: Date.now()
102
+ });
103
+
104
+ // Dispatch tool-specific update
105
+ this.dispatchUpdate('maincontent', {
106
+ observationWidget: {
107
+ id: outputId,
108
+ type,
109
+ tool: type,
110
+ observation: content
111
+ }
112
+ });
113
+ });
114
+
115
+ } catch (error) {
116
+ console.error('Error processing tool outputs:', error);
117
+ }
118
+ }
119
 
120
+ dispatchUpdate(component, data) {
121
+ console.log('Dispatching update:', component, data);
122
+ const event = new CustomEvent('componentUpdate', {
123
+ detail: { component, data }
124
+ });
125
+ this.eventDispatcher.dispatchEvent(event);
126
+ }
127
+ }