abstractassistant 0.3.3__py3-none-any.whl → 0.3.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- abstractassistant/ui/history_dialog.py +575 -51
- abstractassistant/ui/provider_manager.py +2 -2
- abstractassistant/ui/qt_bubble.py +454 -29
- {abstractassistant-0.3.3.dist-info → abstractassistant-0.3.5.dist-info}/METADATA +3 -3
- {abstractassistant-0.3.3.dist-info → abstractassistant-0.3.5.dist-info}/RECORD +9 -9
- {abstractassistant-0.3.3.dist-info → abstractassistant-0.3.5.dist-info}/WHEEL +0 -0
- {abstractassistant-0.3.3.dist-info → abstractassistant-0.3.5.dist-info}/entry_points.txt +0 -0
- {abstractassistant-0.3.3.dist-info → abstractassistant-0.3.5.dist-info}/licenses/LICENSE +0 -0
- {abstractassistant-0.3.3.dist-info → abstractassistant-0.3.5.dist-info}/top_level.txt +0 -0
|
@@ -186,8 +186,8 @@ class ProviderManager:
|
|
|
186
186
|
Returns:
|
|
187
187
|
Formatted display name
|
|
188
188
|
"""
|
|
189
|
-
#
|
|
190
|
-
display_name = model
|
|
189
|
+
# Use the full model name (preserving provider prefix)
|
|
190
|
+
display_name = model
|
|
191
191
|
|
|
192
192
|
# Truncate if too long
|
|
193
193
|
if len(display_name) > max_length:
|
|
@@ -405,6 +405,7 @@ class QtChatBubble(QWidget):
|
|
|
405
405
|
("Clear", self.clear_session),
|
|
406
406
|
("Load", self.load_session),
|
|
407
407
|
("Save", self.save_session),
|
|
408
|
+
# ("Compact", self.compact_session), # Hidden for now - functionality preserved
|
|
408
409
|
("History", self.show_history)
|
|
409
410
|
]
|
|
410
411
|
|
|
@@ -614,6 +615,7 @@ class QtChatBubble(QWidget):
|
|
|
614
615
|
self.model_combo.currentTextChanged.connect(self.on_model_changed)
|
|
615
616
|
self.model_combo.setFixedHeight(28)
|
|
616
617
|
self.model_combo.setMinimumWidth(140)
|
|
618
|
+
self.model_combo.view().setMinimumWidth(380) # Wider dropdown to show full model names
|
|
617
619
|
self.model_combo.setStyleSheet("""
|
|
618
620
|
QComboBox {
|
|
619
621
|
background: rgba(255, 255, 255, 0.08);
|
|
@@ -769,39 +771,38 @@ class QtChatBubble(QWidget):
|
|
|
769
771
|
}
|
|
770
772
|
|
|
771
773
|
QComboBox QAbstractItemView {
|
|
772
|
-
background: #
|
|
773
|
-
border: 1px solid #
|
|
774
|
+
background: #252525;
|
|
775
|
+
border: 1px solid #404040;
|
|
774
776
|
border-radius: 8px;
|
|
775
|
-
selection-background-color: #
|
|
776
|
-
color: #
|
|
777
|
-
padding:
|
|
778
|
-
font-family: "
|
|
777
|
+
selection-background-color: #404040;
|
|
778
|
+
color: #ffffff;
|
|
779
|
+
padding: 6px;
|
|
780
|
+
font-family: "SF Mono", "Monaco", "Menlo", "Consolas", monospace;
|
|
779
781
|
}
|
|
780
|
-
|
|
782
|
+
|
|
781
783
|
QComboBox QAbstractItemView::item {
|
|
782
|
-
height:
|
|
783
|
-
padding:
|
|
784
|
+
height: 44px;
|
|
785
|
+
padding: 10px 16px;
|
|
784
786
|
border: none;
|
|
785
|
-
font-size:
|
|
786
|
-
font-weight: 400;
|
|
787
|
-
color: #
|
|
788
|
-
border-radius:
|
|
789
|
-
margin:
|
|
787
|
+
font-size: 13px;
|
|
788
|
+
font-weight: 400;
|
|
789
|
+
color: #e8e8e8;
|
|
790
|
+
border-radius: 6px;
|
|
791
|
+
margin: 3px;
|
|
792
|
+
letter-spacing: 0.02em;
|
|
790
793
|
}
|
|
791
|
-
|
|
794
|
+
|
|
792
795
|
QComboBox QAbstractItemView::item:selected {
|
|
793
|
-
background: #
|
|
796
|
+
background: #404040;
|
|
794
797
|
color: #ffffff;
|
|
798
|
+
font-weight: 500;
|
|
799
|
+
border: 1px solid #0066cc;
|
|
795
800
|
}
|
|
796
|
-
|
|
801
|
+
|
|
797
802
|
QComboBox QAbstractItemView::item:hover {
|
|
798
|
-
background: #
|
|
803
|
+
background: #333333;
|
|
799
804
|
}
|
|
800
805
|
|
|
801
|
-
QComboBox QAbstractItemView::item:selected {
|
|
802
|
-
background: #4299e1;
|
|
803
|
-
}
|
|
804
|
-
|
|
805
806
|
/* Labels - Clean Typography */
|
|
806
807
|
QLabel {
|
|
807
808
|
color: rgba(255, 255, 255, 0.8);
|
|
@@ -985,7 +986,7 @@ class QtChatBubble(QWidget):
|
|
|
985
986
|
|
|
986
987
|
# Add models to dropdown with display names
|
|
987
988
|
for model in models:
|
|
988
|
-
display_name = self.provider_manager.create_model_display_name(model, max_length=
|
|
989
|
+
display_name = self.provider_manager.create_model_display_name(model, max_length=55)
|
|
989
990
|
self.model_combo.addItem(display_name, model)
|
|
990
991
|
|
|
991
992
|
# Set preferred model
|
|
@@ -1013,9 +1014,10 @@ class QtChatBubble(QWidget):
|
|
|
1013
1014
|
models = get_available_models_for_provider(self.current_provider)
|
|
1014
1015
|
|
|
1015
1016
|
for model in models:
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1017
|
+
# Use full model name (preserving provider prefix)
|
|
1018
|
+
display_name = model
|
|
1019
|
+
if len(display_name) > 55:
|
|
1020
|
+
display_name = display_name[:52] + "..."
|
|
1019
1021
|
self.model_combo.addItem(display_name, model)
|
|
1020
1022
|
|
|
1021
1023
|
if self.model_combo.count() > 0:
|
|
@@ -1971,6 +1973,268 @@ class QtChatBubble(QWidget):
|
|
|
1971
1973
|
if self.debug:
|
|
1972
1974
|
print("🧹 Session cleared (including attached files and file tracking)")
|
|
1973
1975
|
|
|
1976
|
+
def compact_session(self):
|
|
1977
|
+
"""Compact the current session using AbstractCore's summarizer functionality."""
|
|
1978
|
+
if not self.message_history:
|
|
1979
|
+
QMessageBox.information(
|
|
1980
|
+
self,
|
|
1981
|
+
"No Session",
|
|
1982
|
+
"No conversation history to compact. Start a conversation first."
|
|
1983
|
+
)
|
|
1984
|
+
return
|
|
1985
|
+
|
|
1986
|
+
# Check if session is too short to compact
|
|
1987
|
+
if len(self.message_history) < 4: # Need at least 2 exchanges to be worth compacting
|
|
1988
|
+
QMessageBox.information(
|
|
1989
|
+
self,
|
|
1990
|
+
"Session Too Short",
|
|
1991
|
+
"Session is too short to compact. Need at least 2 exchanges (4 messages)."
|
|
1992
|
+
)
|
|
1993
|
+
return
|
|
1994
|
+
|
|
1995
|
+
reply = QMessageBox.question(
|
|
1996
|
+
self,
|
|
1997
|
+
"Compact Session",
|
|
1998
|
+
"This will summarize the conversation history into a concise system message, "
|
|
1999
|
+
"keeping only the most recent 2 exchanges for context.\n\n"
|
|
2000
|
+
"This action cannot be undone. Continue?",
|
|
2001
|
+
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
|
|
2002
|
+
QMessageBox.StandardButton.No
|
|
2003
|
+
)
|
|
2004
|
+
|
|
2005
|
+
if reply == QMessageBox.StandardButton.Yes:
|
|
2006
|
+
try:
|
|
2007
|
+
# Show progress
|
|
2008
|
+
self.status_label.setText("compacting")
|
|
2009
|
+
self.status_label.setStyleSheet("""
|
|
2010
|
+
QLabel {
|
|
2011
|
+
background: rgba(250, 179, 135, 0.2);
|
|
2012
|
+
border: 1px solid rgba(250, 179, 135, 0.3);
|
|
2013
|
+
border-radius: 12px;
|
|
2014
|
+
padding: 4px 12px;
|
|
2015
|
+
font-size: 11px;
|
|
2016
|
+
font-weight: 600;
|
|
2017
|
+
text-transform: uppercase;
|
|
2018
|
+
letter-spacing: 0.5px;
|
|
2019
|
+
color: #fab387;
|
|
2020
|
+
}
|
|
2021
|
+
""")
|
|
2022
|
+
|
|
2023
|
+
# Notify main app about status change
|
|
2024
|
+
if self.status_callback:
|
|
2025
|
+
self.status_callback("compacting")
|
|
2026
|
+
|
|
2027
|
+
# Create conversation text for summarization
|
|
2028
|
+
conversation_text = self._format_conversation_for_summarization()
|
|
2029
|
+
|
|
2030
|
+
# Use AbstractCore's summarizer functionality through LLMManager
|
|
2031
|
+
summary = self._generate_conversation_summary(conversation_text)
|
|
2032
|
+
|
|
2033
|
+
if summary:
|
|
2034
|
+
# Keep the last 2 exchanges (4 messages) for context
|
|
2035
|
+
recent_messages = self.message_history[-4:] if len(self.message_history) >= 4 else self.message_history[-2:]
|
|
2036
|
+
|
|
2037
|
+
# Create new session with summary as system context
|
|
2038
|
+
self._create_compacted_session(summary, recent_messages)
|
|
2039
|
+
|
|
2040
|
+
# Update UI
|
|
2041
|
+
self.token_count = 0 # Reset token count
|
|
2042
|
+
self.update_token_display()
|
|
2043
|
+
|
|
2044
|
+
# Show success message
|
|
2045
|
+
QMessageBox.information(
|
|
2046
|
+
self,
|
|
2047
|
+
"Session Compacted",
|
|
2048
|
+
f"Session successfully compacted!\n\n"
|
|
2049
|
+
f"Original: {len(self.message_history)} messages\n"
|
|
2050
|
+
f"Compacted: Summary + {len(recent_messages)} recent messages"
|
|
2051
|
+
)
|
|
2052
|
+
|
|
2053
|
+
if self.debug:
|
|
2054
|
+
print(f"🗜️ Session compacted: {len(self.message_history)} -> summary + {len(recent_messages)} recent")
|
|
2055
|
+
else:
|
|
2056
|
+
raise Exception("Failed to generate summary")
|
|
2057
|
+
|
|
2058
|
+
except Exception as e:
|
|
2059
|
+
QMessageBox.critical(
|
|
2060
|
+
self,
|
|
2061
|
+
"Compaction Error",
|
|
2062
|
+
f"Failed to compact session:\n{str(e)}"
|
|
2063
|
+
)
|
|
2064
|
+
if self.debug:
|
|
2065
|
+
print(f"❌ Failed to compact session: {e}")
|
|
2066
|
+
import traceback
|
|
2067
|
+
traceback.print_exc()
|
|
2068
|
+
finally:
|
|
2069
|
+
# Reset status
|
|
2070
|
+
self.status_label.setText("ready")
|
|
2071
|
+
self.status_label.setStyleSheet("""
|
|
2072
|
+
QLabel {
|
|
2073
|
+
background: rgba(166, 227, 161, 0.2);
|
|
2074
|
+
border: 1px solid rgba(166, 227, 161, 0.3);
|
|
2075
|
+
border-radius: 12px;
|
|
2076
|
+
padding: 4px 12px;
|
|
2077
|
+
font-size: 11px;
|
|
2078
|
+
font-weight: 600;
|
|
2079
|
+
text-transform: uppercase;
|
|
2080
|
+
letter-spacing: 0.5px;
|
|
2081
|
+
color: #a6e3a1;
|
|
2082
|
+
}
|
|
2083
|
+
""")
|
|
2084
|
+
if self.status_callback:
|
|
2085
|
+
self.status_callback("ready")
|
|
2086
|
+
|
|
2087
|
+
def _format_conversation_for_summarization(self) -> str:
|
|
2088
|
+
"""Format the conversation history for summarization."""
|
|
2089
|
+
lines = []
|
|
2090
|
+
lines.append("=== CONVERSATION HISTORY ===\n")
|
|
2091
|
+
|
|
2092
|
+
for i, msg in enumerate(self.message_history):
|
|
2093
|
+
role = "USER" if msg.get('type') == 'user' else "ASSISTANT"
|
|
2094
|
+
content = msg.get('content', '')
|
|
2095
|
+
timestamp = msg.get('timestamp', '')
|
|
2096
|
+
|
|
2097
|
+
# Add timestamp if available
|
|
2098
|
+
if timestamp:
|
|
2099
|
+
try:
|
|
2100
|
+
from datetime import datetime
|
|
2101
|
+
if isinstance(timestamp, str):
|
|
2102
|
+
dt = datetime.fromisoformat(timestamp)
|
|
2103
|
+
time_str = dt.strftime("%Y-%m-%d %H:%M")
|
|
2104
|
+
lines.append(f"[{time_str}] {role}:")
|
|
2105
|
+
else:
|
|
2106
|
+
lines.append(f"{role}:")
|
|
2107
|
+
except:
|
|
2108
|
+
lines.append(f"{role}:")
|
|
2109
|
+
else:
|
|
2110
|
+
lines.append(f"{role}:")
|
|
2111
|
+
|
|
2112
|
+
lines.append(content)
|
|
2113
|
+
lines.append("") # Empty line between messages
|
|
2114
|
+
|
|
2115
|
+
return "\n".join(lines)
|
|
2116
|
+
|
|
2117
|
+
def _generate_conversation_summary(self, conversation_text: str) -> str:
|
|
2118
|
+
"""Generate a conversation summary using AbstractCore's summarizer functionality."""
|
|
2119
|
+
try:
|
|
2120
|
+
# Use the current LLM to generate a summary
|
|
2121
|
+
# This mimics what the AbstractCore summarizer CLI does
|
|
2122
|
+
summary_prompt = f"""Please provide a comprehensive but concise summary of the following conversation.
|
|
2123
|
+
Focus on:
|
|
2124
|
+
- Key topics discussed
|
|
2125
|
+
- Important decisions or conclusions reached
|
|
2126
|
+
- Relevant context that should be preserved
|
|
2127
|
+
- Any ongoing tasks or questions
|
|
2128
|
+
|
|
2129
|
+
The summary should be detailed enough to provide context for continuing the conversation, but concise enough to save tokens.
|
|
2130
|
+
|
|
2131
|
+
Conversation to summarize:
|
|
2132
|
+
{conversation_text}
|
|
2133
|
+
|
|
2134
|
+
Please provide the summary in a clear, structured format:"""
|
|
2135
|
+
|
|
2136
|
+
if self.llm_manager and self.llm_manager.llm:
|
|
2137
|
+
# Generate summary using current LLM
|
|
2138
|
+
response = self.llm_manager.llm.generate(summary_prompt)
|
|
2139
|
+
|
|
2140
|
+
if hasattr(response, 'content'):
|
|
2141
|
+
return response.content
|
|
2142
|
+
else:
|
|
2143
|
+
return str(response)
|
|
2144
|
+
else:
|
|
2145
|
+
raise Exception("No LLM available for summarization")
|
|
2146
|
+
|
|
2147
|
+
except Exception as e:
|
|
2148
|
+
if self.debug:
|
|
2149
|
+
print(f"❌ Error generating summary: {e}")
|
|
2150
|
+
raise
|
|
2151
|
+
|
|
2152
|
+
def _create_compacted_session(self, summary: str, recent_messages: list):
|
|
2153
|
+
"""Create a new session with the summary and recent messages."""
|
|
2154
|
+
try:
|
|
2155
|
+
# Create new session with summary embedded in the system prompt.
|
|
2156
|
+
final_system_prompt = f"""You are a helpful AI assistant who has access to tools to help the user.
|
|
2157
|
+
Always be a critical and creative thinker who leverage constructive skepticism to progress and evolve its reasoning and answers.
|
|
2158
|
+
Always answer in nicely formatted markdown.
|
|
2159
|
+
|
|
2160
|
+
=== CONVERSATION CONTEXT ===
|
|
2161
|
+
The following is a summary of our previous conversation:
|
|
2162
|
+
|
|
2163
|
+
{summary}
|
|
2164
|
+
|
|
2165
|
+
=== END CONTEXT ===
|
|
2166
|
+
|
|
2167
|
+
Continue the conversation naturally, referring to the context above when relevant."""
|
|
2168
|
+
|
|
2169
|
+
# Create new session with the composed system prompt
|
|
2170
|
+
if self.llm_manager:
|
|
2171
|
+
# Create new session with custom system prompt
|
|
2172
|
+
from abstractcore import BasicSession
|
|
2173
|
+
|
|
2174
|
+
# Prepare tools list (same as in LLMManager)
|
|
2175
|
+
tools = []
|
|
2176
|
+
try:
|
|
2177
|
+
from abstractcore.tools.common_tools import (
|
|
2178
|
+
list_files, search_files, read_file, edit_file,
|
|
2179
|
+
write_file, execute_command, web_search
|
|
2180
|
+
)
|
|
2181
|
+
tools = [
|
|
2182
|
+
list_files, search_files, read_file, edit_file,
|
|
2183
|
+
write_file, execute_command, web_search
|
|
2184
|
+
]
|
|
2185
|
+
except ImportError:
|
|
2186
|
+
pass
|
|
2187
|
+
|
|
2188
|
+
# Create new session with summary in system prompt
|
|
2189
|
+
new_session = BasicSession(
|
|
2190
|
+
self.llm_manager.llm,
|
|
2191
|
+
system_prompt=final_system_prompt,
|
|
2192
|
+
tools=tools
|
|
2193
|
+
)
|
|
2194
|
+
|
|
2195
|
+
# Add recent messages to the new session
|
|
2196
|
+
for msg in recent_messages:
|
|
2197
|
+
if msg.get('type') == 'user':
|
|
2198
|
+
# Add user message without generating response
|
|
2199
|
+
from abstractcore.messages import UserMessage
|
|
2200
|
+
user_msg = UserMessage(content=msg.get('content', ''))
|
|
2201
|
+
new_session.messages.append(user_msg)
|
|
2202
|
+
elif msg.get('type') == 'assistant':
|
|
2203
|
+
# Add assistant message
|
|
2204
|
+
from abstractcore.messages import AssistantMessage
|
|
2205
|
+
assistant_msg = AssistantMessage(content=msg.get('content', ''))
|
|
2206
|
+
new_session.messages.append(assistant_msg)
|
|
2207
|
+
|
|
2208
|
+
# Replace current session
|
|
2209
|
+
self.llm_manager.current_session = new_session
|
|
2210
|
+
|
|
2211
|
+
# Update local message history to reflect the compacted state
|
|
2212
|
+
# Create a special "system" message to represent the summary
|
|
2213
|
+
compacted_history = [
|
|
2214
|
+
{
|
|
2215
|
+
'timestamp': datetime.now().isoformat(),
|
|
2216
|
+
'type': 'system',
|
|
2217
|
+
'content': f"📋 **Session Compacted**\n\n{summary}",
|
|
2218
|
+
'provider': self.current_provider,
|
|
2219
|
+
'model': self.current_model,
|
|
2220
|
+
'attached_files': []
|
|
2221
|
+
}
|
|
2222
|
+
]
|
|
2223
|
+
|
|
2224
|
+
# Add recent messages
|
|
2225
|
+
compacted_history.extend(recent_messages)
|
|
2226
|
+
|
|
2227
|
+
# Update message history
|
|
2228
|
+
self.message_history = compacted_history
|
|
2229
|
+
|
|
2230
|
+
if self.debug:
|
|
2231
|
+
print(f"✅ Created compacted session with enhanced system prompt")
|
|
2232
|
+
|
|
2233
|
+
except Exception as e:
|
|
2234
|
+
if self.debug:
|
|
2235
|
+
print(f"❌ Error creating compacted session: {e}")
|
|
2236
|
+
raise
|
|
2237
|
+
|
|
1974
2238
|
def load_session(self):
|
|
1975
2239
|
"""Load a session using AbstractCore via LLMManager."""
|
|
1976
2240
|
file_path, _ = QFileDialog.getOpenFileName(
|
|
@@ -2192,8 +2456,12 @@ class QtChatBubble(QWidget):
|
|
|
2192
2456
|
# Toggle behavior: create dialog if doesn't exist, toggle visibility if it does
|
|
2193
2457
|
if iPhoneMessagesDialog:
|
|
2194
2458
|
if self.history_dialog is None:
|
|
2195
|
-
# Create dialog first time
|
|
2196
|
-
self.history_dialog = iPhoneMessagesDialog.create_dialog(
|
|
2459
|
+
# Create dialog first time with deletion support
|
|
2460
|
+
self.history_dialog = iPhoneMessagesDialog.create_dialog(
|
|
2461
|
+
self.message_history,
|
|
2462
|
+
self,
|
|
2463
|
+
delete_callback=self._handle_message_deletion
|
|
2464
|
+
)
|
|
2197
2465
|
# Set callback to update button when dialog is hidden via Back button
|
|
2198
2466
|
self.history_dialog.set_hide_callback(lambda: self._update_history_button_appearance(False))
|
|
2199
2467
|
self.history_dialog.show()
|
|
@@ -2205,7 +2473,11 @@ class QtChatBubble(QWidget):
|
|
|
2205
2473
|
self._update_history_button_appearance(False)
|
|
2206
2474
|
else:
|
|
2207
2475
|
# Update dialog with latest messages before showing
|
|
2208
|
-
self.history_dialog = iPhoneMessagesDialog.create_dialog(
|
|
2476
|
+
self.history_dialog = iPhoneMessagesDialog.create_dialog(
|
|
2477
|
+
self.message_history,
|
|
2478
|
+
self,
|
|
2479
|
+
delete_callback=self._handle_message_deletion
|
|
2480
|
+
)
|
|
2209
2481
|
# Set callback to update button when dialog is hidden via Back button
|
|
2210
2482
|
self.history_dialog.set_hide_callback(lambda: self._update_history_button_appearance(False))
|
|
2211
2483
|
self.history_dialog.show()
|
|
@@ -2256,6 +2528,159 @@ class QtChatBubble(QWidget):
|
|
|
2256
2528
|
}
|
|
2257
2529
|
""")
|
|
2258
2530
|
|
|
2531
|
+
def _handle_message_deletion(self, indices_to_delete: List[int]):
|
|
2532
|
+
"""Handle deletion of messages from the history dialog."""
|
|
2533
|
+
try:
|
|
2534
|
+
if not indices_to_delete:
|
|
2535
|
+
return
|
|
2536
|
+
|
|
2537
|
+
# Validate indices
|
|
2538
|
+
for index in indices_to_delete:
|
|
2539
|
+
if not (0 <= index < len(self.message_history)):
|
|
2540
|
+
QMessageBox.critical(
|
|
2541
|
+
self,
|
|
2542
|
+
"Invalid Selection",
|
|
2543
|
+
f"Invalid message index {index}. Please refresh and try again."
|
|
2544
|
+
)
|
|
2545
|
+
return
|
|
2546
|
+
|
|
2547
|
+
# Delete messages from local history (indices are sorted in reverse order)
|
|
2548
|
+
original_count = len(self.message_history)
|
|
2549
|
+
|
|
2550
|
+
for index in indices_to_delete:
|
|
2551
|
+
if 0 <= index < len(self.message_history):
|
|
2552
|
+
del self.message_history[index]
|
|
2553
|
+
|
|
2554
|
+
# Update AbstractCore session to reflect deletions
|
|
2555
|
+
self._update_abstractcore_session_after_deletion()
|
|
2556
|
+
|
|
2557
|
+
# Update token count
|
|
2558
|
+
self._update_token_count_from_session()
|
|
2559
|
+
|
|
2560
|
+
# Update history dialog if it's open (keep it open!)
|
|
2561
|
+
if self.history_dialog and self.history_dialog.isVisible():
|
|
2562
|
+
try:
|
|
2563
|
+
# Update the dialog content without closing it
|
|
2564
|
+
self.history_dialog.update_message_history(self.message_history)
|
|
2565
|
+
except Exception as dialog_error:
|
|
2566
|
+
import traceback
|
|
2567
|
+
traceback.print_exc()
|
|
2568
|
+
# Fallback: recreate dialog if update fails
|
|
2569
|
+
try:
|
|
2570
|
+
if len(self.message_history) == 0:
|
|
2571
|
+
self.history_dialog.hide()
|
|
2572
|
+
self._update_history_button_appearance(False)
|
|
2573
|
+
else:
|
|
2574
|
+
new_dialog = iPhoneMessagesDialog.create_dialog(
|
|
2575
|
+
self.message_history,
|
|
2576
|
+
self,
|
|
2577
|
+
delete_callback=self._handle_message_deletion
|
|
2578
|
+
)
|
|
2579
|
+
if new_dialog:
|
|
2580
|
+
old_pos = self.history_dialog.pos()
|
|
2581
|
+
self.history_dialog.hide()
|
|
2582
|
+
self.history_dialog = new_dialog
|
|
2583
|
+
self.history_dialog.move(old_pos) # Keep same position
|
|
2584
|
+
self.history_dialog.set_hide_callback(lambda: self._update_history_button_appearance(False))
|
|
2585
|
+
self.history_dialog.show()
|
|
2586
|
+
except:
|
|
2587
|
+
try:
|
|
2588
|
+
self.history_dialog.hide()
|
|
2589
|
+
self._update_history_button_appearance(False)
|
|
2590
|
+
except:
|
|
2591
|
+
pass
|
|
2592
|
+
|
|
2593
|
+
# Log success (no popup)
|
|
2594
|
+
deleted_count = original_count - len(self.message_history)
|
|
2595
|
+
|
|
2596
|
+
if self.debug:
|
|
2597
|
+
print(f"🗑️ Deleted {deleted_count} messages from history")
|
|
2598
|
+
|
|
2599
|
+
except Exception as e:
|
|
2600
|
+
print(f"❌ Critical error in _handle_message_deletion: {e}")
|
|
2601
|
+
import traceback
|
|
2602
|
+
traceback.print_exc()
|
|
2603
|
+
|
|
2604
|
+
try:
|
|
2605
|
+
QMessageBox.critical(
|
|
2606
|
+
self,
|
|
2607
|
+
"Deletion Error",
|
|
2608
|
+
f"Failed to delete messages:\n{str(e)}\n\nCheck console for details."
|
|
2609
|
+
)
|
|
2610
|
+
except:
|
|
2611
|
+
print("❌ Could not show error dialog")
|
|
2612
|
+
|
|
2613
|
+
if self.debug:
|
|
2614
|
+
print(f"❌ Failed to delete messages: {e}")
|
|
2615
|
+
import traceback
|
|
2616
|
+
traceback.print_exc()
|
|
2617
|
+
|
|
2618
|
+
def _update_abstractcore_session_after_deletion(self):
|
|
2619
|
+
"""Update AbstractCore session to reflect message deletions."""
|
|
2620
|
+
try:
|
|
2621
|
+
if not self.llm_manager or not self.llm_manager.current_session:
|
|
2622
|
+
return
|
|
2623
|
+
|
|
2624
|
+
# Get current system prompt
|
|
2625
|
+
current_session = self.llm_manager.current_session
|
|
2626
|
+
system_prompt = getattr(current_session, 'system_prompt', None) or """
|
|
2627
|
+
You are a helpful AI assistant who has access to tools to help the user.
|
|
2628
|
+
Always be a critical and creative thinker who leverage constructive skepticism to progress and evolve its reasoning and answers.
|
|
2629
|
+
Always answer in nicely formatted markdown.
|
|
2630
|
+
"""
|
|
2631
|
+
|
|
2632
|
+
# Prepare tools list (same as in LLMManager)
|
|
2633
|
+
tools = []
|
|
2634
|
+
try:
|
|
2635
|
+
from abstractcore.tools.common_tools import (
|
|
2636
|
+
list_files, search_files, read_file, edit_file,
|
|
2637
|
+
write_file, execute_command, web_search
|
|
2638
|
+
)
|
|
2639
|
+
tools = [
|
|
2640
|
+
list_files, search_files, read_file, edit_file,
|
|
2641
|
+
write_file, execute_command, web_search
|
|
2642
|
+
]
|
|
2643
|
+
except ImportError as import_error:
|
|
2644
|
+
pass
|
|
2645
|
+
pass
|
|
2646
|
+
|
|
2647
|
+
# Create new session with updated message history
|
|
2648
|
+
from abstractcore import BasicSession
|
|
2649
|
+
new_session = BasicSession(
|
|
2650
|
+
self.llm_manager.llm,
|
|
2651
|
+
system_prompt=system_prompt,
|
|
2652
|
+
tools=tools
|
|
2653
|
+
)
|
|
2654
|
+
|
|
2655
|
+
# Add remaining messages to the new session
|
|
2656
|
+
for i, msg in enumerate(self.message_history):
|
|
2657
|
+
try:
|
|
2658
|
+
if msg.get('type') == 'user':
|
|
2659
|
+
from abstractcore.messages import UserMessage
|
|
2660
|
+
user_msg = UserMessage(content=msg.get('content', ''))
|
|
2661
|
+
new_session.messages.append(user_msg)
|
|
2662
|
+
elif msg.get('type') == 'assistant':
|
|
2663
|
+
from abstractcore.messages import AssistantMessage
|
|
2664
|
+
assistant_msg = AssistantMessage(content=msg.get('content', ''))
|
|
2665
|
+
new_session.messages.append(assistant_msg)
|
|
2666
|
+
elif msg.get('type') == 'system':
|
|
2667
|
+
# Skip system messages (handled by system_prompt)
|
|
2668
|
+
pass
|
|
2669
|
+
else:
|
|
2670
|
+
# Unknown message type
|
|
2671
|
+
pass
|
|
2672
|
+
except Exception as msg_error:
|
|
2673
|
+
# Continue with other messages
|
|
2674
|
+
pass
|
|
2675
|
+
|
|
2676
|
+
# Replace current session
|
|
2677
|
+
self.llm_manager.current_session = new_session
|
|
2678
|
+
|
|
2679
|
+
except Exception as e:
|
|
2680
|
+
import traceback
|
|
2681
|
+
traceback.print_exc()
|
|
2682
|
+
# Don't raise - this is not critical for the UI operation
|
|
2683
|
+
|
|
2259
2684
|
def close_app(self):
|
|
2260
2685
|
"""Close the entire application completely."""
|
|
2261
2686
|
if self.debug:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: abstractassistant
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.5
|
|
4
4
|
Summary: A sleek (macOS) system tray application providing instant access to LLMs
|
|
5
5
|
Author-email: Laurent-Philippe Albou <contact@abstractcore.ai>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -21,14 +21,14 @@ Classifier: Topic :: Desktop Environment
|
|
|
21
21
|
Requires-Python: >=3.9
|
|
22
22
|
Description-Content-Type: text/markdown
|
|
23
23
|
License-File: LICENSE
|
|
24
|
-
Requires-Dist: abstractcore[
|
|
24
|
+
Requires-Dist: abstractcore[anthropic,lmstudio,ollama,openai,tools]>=2.9.1
|
|
25
25
|
Requires-Dist: pystray>=0.19.4
|
|
26
26
|
Requires-Dist: Pillow>=10.0.0
|
|
27
27
|
Requires-Dist: PyQt5>=5.15.0
|
|
28
28
|
Requires-Dist: markdown>=3.5.0
|
|
29
29
|
Requires-Dist: pygments>=2.16.0
|
|
30
30
|
Requires-Dist: pymdown-extensions>=10.0
|
|
31
|
-
Requires-Dist: abstractvoice>=0.5.
|
|
31
|
+
Requires-Dist: abstractvoice>=0.5.2
|
|
32
32
|
Requires-Dist: pyperclip>=1.8.2
|
|
33
33
|
Requires-Dist: plyer>=2.1.0
|
|
34
34
|
Requires-Dist: tomli>=2.0.0; python_version < "3.11"
|
|
@@ -9,9 +9,9 @@ abstractassistant/core/llm_manager.py,sha256=hJun-nDfRv9zxv_3tfrHAmVYSYT96E-0zDJ
|
|
|
9
9
|
abstractassistant/core/tts_manager.py,sha256=Cxh302EgIycwkWxe7XntmLW-j_WusbJOYRCs3Jms3CU,9892
|
|
10
10
|
abstractassistant/ui/__init__.py,sha256=aRNE2pS50nFAX6y--rSGMNYwhz905g14gRd6g4BolYU,13
|
|
11
11
|
abstractassistant/ui/chat_bubble.py,sha256=bY48b4IeQzOrRN2_sJ5OazhZcJ8IMaBM6R3EexvU30Q,11885
|
|
12
|
-
abstractassistant/ui/history_dialog.py,sha256=
|
|
13
|
-
abstractassistant/ui/provider_manager.py,sha256=
|
|
14
|
-
abstractassistant/ui/qt_bubble.py,sha256=
|
|
12
|
+
abstractassistant/ui/history_dialog.py,sha256=lVyNrZVu73CZo593DvnuWU1iCpmZybTCFTjlu4RrqBM,39701
|
|
13
|
+
abstractassistant/ui/provider_manager.py,sha256=v61bM3yNt5jEbU5yS6J2q32kl6qw8zG-I6vKtx3l0cA,8364
|
|
14
|
+
abstractassistant/ui/qt_bubble.py,sha256=sPeL54bIVVDUsE51U2b_XgtzDVIb-qbkI1IHz26dLlw,115631
|
|
15
15
|
abstractassistant/ui/toast_manager.py,sha256=1aU4DPo-J45bC61gTEctHq98ZrHIFxRfZa_9Q8KF588,13721
|
|
16
16
|
abstractassistant/ui/toast_window.py,sha256=BRSwEBlaND5LLipn1HOX0ISWxVH-zOHsYplFkiPaj_g,21727
|
|
17
17
|
abstractassistant/ui/tts_state_manager.py,sha256=UF_zrfl9wf0hNHBGxevcoKxW5Dh7zXibUSVoSSjGP4o,10565
|
|
@@ -19,9 +19,9 @@ abstractassistant/ui/ui_styles.py,sha256=FvE2CVUbHmHu1PKVTBBGyhbt781qh4WjLMrHvil
|
|
|
19
19
|
abstractassistant/utils/__init__.py,sha256=7Q3BxyXETkt3tm5trhuLTyL8PoECOK0QiK-0KUVAR2Q,16
|
|
20
20
|
abstractassistant/utils/icon_generator.py,sha256=SWPgi1V6_8544Zbc2vAfFXAy15H35neyUGCYt2eKoic,16475
|
|
21
21
|
abstractassistant/utils/markdown_renderer.py,sha256=u5tVIhulSwRYADiqJcZNoHhU8e6pJVgzrwZRd61Bov0,12585
|
|
22
|
-
abstractassistant-0.3.
|
|
23
|
-
abstractassistant-0.3.
|
|
24
|
-
abstractassistant-0.3.
|
|
25
|
-
abstractassistant-0.3.
|
|
26
|
-
abstractassistant-0.3.
|
|
27
|
-
abstractassistant-0.3.
|
|
22
|
+
abstractassistant-0.3.5.dist-info/licenses/LICENSE,sha256=QUjFNAE-0yOkW9-Rle2axkpkt9H7xiZ2VbN-VeONhxc,1106
|
|
23
|
+
abstractassistant-0.3.5.dist-info/METADATA,sha256=T58ZbA3OYjIjS9qm1EPaLOMPqcwFrezy4o1-HZYTrUg,11599
|
|
24
|
+
abstractassistant-0.3.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
25
|
+
abstractassistant-0.3.5.dist-info/entry_points.txt,sha256=MIzeCh0XG6MbhIzBHtkdEjmjxYBsQrGFevq8Y1L8Jkc,118
|
|
26
|
+
abstractassistant-0.3.5.dist-info/top_level.txt,sha256=qZc_LQH3CBxLq2P4B1aHayzkj8hn0euR31edkXQVzDA,18
|
|
27
|
+
abstractassistant-0.3.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|