Nischal Subedi
commited on
Commit
·
610dd7d
1
Parent(s):
4d1e834
UI update
Browse files
app.py
CHANGED
@@ -89,6 +89,71 @@ Answer:"""
|
|
89 |
logging.info("No statutes found matching the pattern in the context.")
|
90 |
return "No specific statutes found in the provided context."
|
91 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
92 |
@lru_cache(maxsize=50)
|
93 |
def process_query_cached(self, query: str, state: str, openai_api_key: str, n_results: int = 5) -> Dict[str, any]:
|
94 |
logging.info(f"Processing query (cache key: '{query}'|'{state}'|key_hidden) with n_results={n_results}")
|
@@ -176,6 +241,8 @@ Answer:"""
|
|
176 |
answer_text = "<div class='error-message'><span class='error-icon'>⚠️</span>The AI model returned an empty response. This might be due to the query, context limitations, or temporary issues. Please try rephrasing your question or try again later.</div>"
|
177 |
else:
|
178 |
logging.info("LLM generated answer successfully.")
|
|
|
|
|
179 |
|
180 |
return {"answer": answer_text, "context_used": context}
|
181 |
|
@@ -636,6 +703,21 @@ Answer:"""
|
|
636 |
color: var(--text-primary);
|
637 |
line-height: 1.7;
|
638 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
639 |
|
640 |
/* Error message styles */
|
641 |
.error-message {
|
|
|
89 |
logging.info("No statutes found matching the pattern in the context.")
|
90 |
return "No specific statutes found in the provided context."
|
91 |
|
92 |
+
def format_llm_response_for_html(self, text: str) -> str:
|
93 |
+
"""
|
94 |
+
Formats the LLM's raw text response for display in a Gradio HTML component,
|
95 |
+
converting Markdown-like elements to HTML. Handles bolding, paragraphs, and lists.
|
96 |
+
"""
|
97 |
+
if not text:
|
98 |
+
return ""
|
99 |
+
|
100 |
+
# 1. Convert markdown bolding (**text**) to HTML <b>text</b>
|
101 |
+
formatted_text = re.sub(r'\*\*(.*?)\*\*', r'<b>\1</b>', text)
|
102 |
+
|
103 |
+
# Split the text into blocks based on two or more consecutive newlines
|
104 |
+
# This separates paragraphs and distinct list blocks.
|
105 |
+
blocks = re.split(r'\n\s*\n+', formatted_text.strip())
|
106 |
+
|
107 |
+
html_output_parts = []
|
108 |
+
|
109 |
+
for block in blocks:
|
110 |
+
block = block.strip()
|
111 |
+
if not block:
|
112 |
+
continue
|
113 |
+
|
114 |
+
# Check if the block is a list (starts with a list marker on its first non-empty line)
|
115 |
+
list_lines = block.split('\n')
|
116 |
+
|
117 |
+
is_list = False
|
118 |
+
list_type = None # 'ul' or 'ol'
|
119 |
+
|
120 |
+
for line in list_lines:
|
121 |
+
stripped_line = line.strip()
|
122 |
+
if stripped_line: # Check first non-empty line
|
123 |
+
if re.match(r'^[*-]\s*(.*)', stripped_line):
|
124 |
+
is_list = True
|
125 |
+
list_type = 'ul'
|
126 |
+
elif re.match(r'^\d+\.\s*(.*)', stripped_line):
|
127 |
+
is_list = True
|
128 |
+
list_type = 'ol'
|
129 |
+
break # Only need to check the first actual line
|
130 |
+
|
131 |
+
if is_list:
|
132 |
+
# Process as a list
|
133 |
+
list_html = f'<{list_type}>'
|
134 |
+
for line in list_lines:
|
135 |
+
stripped_line = line.strip()
|
136 |
+
if not stripped_line:
|
137 |
+
continue # Skip empty lines within a list block
|
138 |
+
|
139 |
+
# Extract content after the list marker
|
140 |
+
if list_type == 'ul':
|
141 |
+
item_content = re.sub(r'^[*-]\s*', '', stripped_line)
|
142 |
+
else: # ol
|
143 |
+
item_content = re.sub(r'^\d+\.\s*', '', stripped_line)
|
144 |
+
|
145 |
+
list_html += f'<li>{item_content.strip()}</li>'
|
146 |
+
list_html += f'</{list_type}>'
|
147 |
+
html_output_parts.append(list_html)
|
148 |
+
else:
|
149 |
+
# Process as a paragraph
|
150 |
+
# Replace single newlines within the paragraph block with <br>
|
151 |
+
paragraph_content = block.replace('\n', '<br>')
|
152 |
+
html_output_parts.append(f'<p>{paragraph_content}</p>')
|
153 |
+
|
154 |
+
return ''.join(html_output_parts)
|
155 |
+
|
156 |
+
|
157 |
@lru_cache(maxsize=50)
|
158 |
def process_query_cached(self, query: str, state: str, openai_api_key: str, n_results: int = 5) -> Dict[str, any]:
|
159 |
logging.info(f"Processing query (cache key: '{query}'|'{state}'|key_hidden) with n_results={n_results}")
|
|
|
241 |
answer_text = "<div class='error-message'><span class='error-icon'>⚠️</span>The AI model returned an empty response. This might be due to the query, context limitations, or temporary issues. Please try rephrasing your question or try again later.</div>"
|
242 |
else:
|
243 |
logging.info("LLM generated answer successfully.")
|
244 |
+
# Apply the formatting function here to convert Markdown-like syntax to HTML
|
245 |
+
answer_text = self.format_llm_response_for_html(answer_text)
|
246 |
|
247 |
return {"answer": answer_text, "context_used": context}
|
248 |
|
|
|
703 |
color: var(--text-primary);
|
704 |
line-height: 1.7;
|
705 |
}
|
706 |
+
/* Custom CSS for lists and paragraphs within response-content for better spacing */
|
707 |
+
.response-content p {
|
708 |
+
margin-bottom: 1em; /* Spacing between paragraphs */
|
709 |
+
}
|
710 |
+
.response-content ul, .response-content ol {
|
711 |
+
margin-bottom: 1em; /* Spacing after lists */
|
712 |
+
padding-left: 2em; /* Indent lists */
|
713 |
+
}
|
714 |
+
.response-content li {
|
715 |
+
margin-bottom: 0.5em; /* Spacing between list items */
|
716 |
+
}
|
717 |
+
.response-content li:last-child {
|
718 |
+
margin-bottom: 0;
|
719 |
+
}
|
720 |
+
|
721 |
|
722 |
/* Error message styles */
|
723 |
.error-message {
|