cryogenic22 commited on
Commit
24b44d0
·
verified ·
1 Parent(s): 010e6dd

Create utils/response_formatter.py

Browse files
Files changed (1) hide show
  1. utils/response_formatter.py +146 -0
utils/response_formatter.py ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # utils/response_formatter.py
2
+
3
+ import re
4
+ from typing import Dict, Optional
5
+ import streamlit as st
6
+ from datetime import datetime
7
+
8
+ class ResponseFormatter:
9
+ def __init__(self):
10
+ self.section_keywords = [
11
+ "Summary",
12
+ "Overview",
13
+ "Background",
14
+ "Solution",
15
+ "Approach",
16
+ "Benefits",
17
+ "Experience",
18
+ "Methodology",
19
+ "Implementation",
20
+ "Timeline",
21
+ "Pricing",
22
+ "Why Us",
23
+ "Next Steps"
24
+ ]
25
+
26
+ def format_response(self, content: str, metadata: Optional[Dict] = None) -> str:
27
+ """
28
+ Format the AI response into a clean, structured format.
29
+
30
+ Args:
31
+ content (str): Raw response content
32
+ metadata (Optional[Dict]): Additional metadata like sources
33
+
34
+ Returns:
35
+ str: Formatted response
36
+ """
37
+ # Clean up the content
38
+ formatted = self._clean_content(content)
39
+
40
+ # Add structural formatting
41
+ formatted = self._add_structure(formatted)
42
+
43
+ # Format lists and bullet points
44
+ formatted = self._format_lists(formatted)
45
+
46
+ # Add source references if available
47
+ if metadata and 'sources' in metadata:
48
+ formatted = self._add_sources(formatted, metadata['sources'])
49
+
50
+ return formatted
51
+
52
+ def _clean_content(self, content: str) -> str:
53
+ """Clean and normalize the content."""
54
+ # Remove multiple newlines
55
+ cleaned = re.sub(r'\n{3,}', '\n\n', content)
56
+
57
+ # Ensure consistent heading formatting
58
+ for keyword in self.section_keywords:
59
+ pattern = rf'(?i)({keyword}:?)\s*\n'
60
+ cleaned = re.sub(pattern, f'### {keyword}\n\n', cleaned)
61
+
62
+ return cleaned
63
+
64
+ def _add_structure(self, content: str) -> str:
65
+ """Add structural elements to the content."""
66
+ # Add section breaks
67
+ sections = re.split(r'(?m)^###\s+', content)
68
+
69
+ if len(sections) == 1: # No sections found
70
+ # Add a default section if none exists
71
+ content = "### Response\n\n" + content
72
+
73
+ # Add copy button for each section
74
+ structured = ""
75
+ for section in sections:
76
+ if section.strip():
77
+ section_title = section.split('\n')[0].strip()
78
+ section_content = '\n'.join(section.split('\n')[1:]).strip()
79
+
80
+ structured += f"### {section_title}\n\n"
81
+ structured += f"{section_content}\n\n"
82
+
83
+ return structured
84
+
85
+ def _format_lists(self, content: str) -> str:
86
+ """Format lists and bullet points consistently."""
87
+ # Format bullet points
88
+ content = re.sub(r'(?m)^[\-\*]\s+', '• ', content)
89
+
90
+ # Format numbered lists
91
+ content = re.sub(r'(?m)^\d+\.\s+', lambda m: f"{int(m.group().split('.')[0])}. ", content)
92
+
93
+ return content
94
+
95
+ def _add_sources(self, content: str, sources: list) -> str:
96
+ """Add source references to the content."""
97
+ if sources:
98
+ content += "\n\n---\n\n### Sources\n\n"
99
+ for source in sources:
100
+ content += f"• {source}\n"
101
+
102
+ return content
103
+
104
+ def display_formatted_response(response_content: str, metadata: Optional[Dict] = None):
105
+ """
106
+ Display a formatted response in the Streamlit interface.
107
+
108
+ Args:
109
+ response_content (str): Raw response content
110
+ metadata (Optional[Dict]): Additional metadata like sources
111
+ """
112
+ formatter = ResponseFormatter()
113
+ formatted_content = formatter.format_response(response_content, metadata)
114
+
115
+ # Display formatted content
116
+ st.markdown(formatted_content)
117
+
118
+ # Add copy buttons for sections
119
+ sections = re.split(r'(?m)^###\s+', formatted_content)
120
+ for section in sections:
121
+ if section.strip():
122
+ section_title = section.split('\n')[0].strip()
123
+ section_content = '\n'.join(section.split('\n')[1:]).strip()
124
+
125
+ with st.expander(f"Copy {section_title}", expanded=False):
126
+ st.text_area(
127
+ "Copy this content:",
128
+ value=section_content,
129
+ height=200,
130
+ key=f"copy_{section_title}_{datetime.now().timestamp()}"
131
+ )
132
+ if st.button(f"Copy {section_title}", key=f"btn_{section_title}_{datetime.now().timestamp()}"):
133
+ st.write("Content copied to clipboard!")
134
+
135
+ # Example usage in your chat interface:
136
+ """
137
+ def display_chat_message(message, is_user=False):
138
+ if is_user:
139
+ st.chat_message("user").write(message)
140
+ else:
141
+ with st.chat_message("assistant"):
142
+ display_formatted_response(
143
+ message.content,
144
+ metadata=getattr(message, 'metadata', None)
145
+ )
146
+ """