diff --git a/src/calibre/gui2/llm.py b/src/calibre/gui2/llm.py index b32d17fe16..1d30a9975d 100644 --- a/src/calibre/gui2/llm.py +++ b/src/calibre/gui2/llm.py @@ -32,6 +32,7 @@ from qt.core import ( Qt, QTabWidget, QTextBrowser, + QTimer, QUrl, QVBoxLayout, QWidget, @@ -51,7 +52,7 @@ from calibre.gui2.dialogs.confirm_delete import confirm from calibre.gui2.widgets2 import Dialog from calibre.utils.icu import primary_sort_key from calibre.utils.localization import ui_language_as_english -from calibre.utils.logging import ERROR, WARN +from calibre.utils.logging import ERROR, INFO, WARN from calibre.utils.short_uuid import uuid4 from polyglot.binary import as_hex_unicode @@ -68,6 +69,13 @@ def for_display_to_human(self: ChatMessage, is_initial_query: bool = False, cont return response_to_html(q, content_type=content_type) +def streaming_text_as_html(text: str, emphasize: bool = False) -> str: + style = 'white-space: pre-wrap;' + if emphasize: + style += ' font-style: italic;' + return f'
{_('Details:')}\n{escape(self.current_error_details)}"
+ self.result_display.add_block(err_html)
self.result_display.re_render()
self.scroll_to_bottom()
@@ -336,6 +360,7 @@ class ConverseWidget(QWidget):
self.result_display.scroll_to_bottom()
def start_api_call(self, action_prompt: str, **kwargs: Any) -> None:
+ self.clear_current_error()
if not self.is_ready_for_use:
self.show_error(f'''{_('AI provider not configured.')} {_(
'Configure AI provider')}''', is_critical=False)
@@ -374,13 +399,22 @@ class ConverseWidget(QWidget):
if r is None:
self.conversation_history.finalize_response()
self.update_cost()
+ self.streaming_render_timer.stop()
+ self.update_ui_state()
+ return
elif r.exception is not None:
- self.result_display.show_message(
- f'''{_('Talking to AI failed with error:')} {escape(str(r.exception))}''',
- r.error_details, ERROR, clear_conversation=False)
+ self.conversation_history.current_response_completed = True
+ self.conversation_history.api_call_active = False
+ self.current_error_html = f'''{_('Talking to AI failed with error:')} {escape(str(r.exception))}'''
+ self.current_error_details = r.error_details
+ self.current_error_level = ERROR
+ self.streaming_render_timer.stop()
+ self.update_ui_state()
+ return
else:
self.conversation_history.accumulator.accumulate(r)
- self.update_ui_state()
+ if not self.streaming_render_timer.isActive():
+ self.streaming_render_timer.start()
def show_error(self, html: str, is_critical: bool = False, details: str = '') -> None:
self.clear_current_conversation()
@@ -389,6 +423,12 @@ class ConverseWidget(QWidget):
def clear_current_conversation(self) -> None:
self.conversation_history = ConversationHistory()
+ self.clear_current_error()
+
+ def clear_current_error(self) -> None:
+ self.current_error_html = ''
+ self.current_error_details = ''
+ self.current_error_level = INFO
def update_ui_state(self) -> None:
if self.conversation_history: