chat-console 0.1.991.dev1__py3-none-any.whl → 0.2.0__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.
- app/main.py +175 -103
- {chat_console-0.1.991.dev1.dist-info → chat_console-0.2.0.dist-info}/METADATA +1 -1
- {chat_console-0.1.991.dev1.dist-info → chat_console-0.2.0.dist-info}/RECORD +7 -7
- {chat_console-0.1.991.dev1.dist-info → chat_console-0.2.0.dist-info}/WHEEL +0 -0
- {chat_console-0.1.991.dev1.dist-info → chat_console-0.2.0.dist-info}/entry_points.txt +0 -0
- {chat_console-0.1.991.dev1.dist-info → chat_console-0.2.0.dist-info}/licenses/LICENSE +0 -0
- {chat_console-0.1.991.dev1.dist-info → chat_console-0.2.0.dist-info}/top_level.txt +0 -0
app/main.py
CHANGED
@@ -25,16 +25,18 @@ from app.ui.model_selector import ModelSelector, StyleSelector
|
|
25
25
|
from app.ui.chat_list import ChatList
|
26
26
|
from app.api.base import BaseModelClient
|
27
27
|
from app.utils import generate_streaming_response, save_settings_to_config, generate_conversation_title # Import title function
|
28
|
+
# Import version here to avoid potential circular import issues at top level
|
29
|
+
from app import __version__
|
28
30
|
|
29
31
|
# --- Remove SettingsScreen class entirely ---
|
30
32
|
|
31
33
|
class HistoryScreen(Screen):
|
32
34
|
"""Screen for viewing chat history."""
|
33
|
-
|
35
|
+
|
34
36
|
BINDINGS = [
|
35
37
|
Binding("escape", "pop_screen", "Close"),
|
36
38
|
]
|
37
|
-
|
39
|
+
|
38
40
|
CSS = """
|
39
41
|
#history-container {
|
40
42
|
width: 80; # Keep HistoryScreen CSS
|
@@ -43,29 +45,29 @@ class HistoryScreen(Screen):
|
|
43
45
|
border: round $primary;
|
44
46
|
padding: 1; # Keep HistoryScreen CSS
|
45
47
|
}
|
46
|
-
|
48
|
+
|
47
49
|
#title { # Keep HistoryScreen CSS
|
48
50
|
width: 100%; # Keep HistoryScreen CSS
|
49
51
|
content-align: center middle;
|
50
52
|
text-align: center;
|
51
53
|
padding-bottom: 1;
|
52
54
|
}
|
53
|
-
|
55
|
+
|
54
56
|
ListView { # Keep HistoryScreen CSS
|
55
57
|
width: 100%; # Keep HistoryScreen CSS
|
56
58
|
height: 1fr;
|
57
59
|
border: solid $primary;
|
58
60
|
}
|
59
|
-
|
61
|
+
|
60
62
|
ListItem { # Keep HistoryScreen CSS
|
61
63
|
padding: 1; # Keep HistoryScreen CSS
|
62
64
|
border-bottom: solid $primary-darken-2;
|
63
65
|
}
|
64
|
-
|
66
|
+
|
65
67
|
ListItem:hover { # Keep HistoryScreen CSS
|
66
68
|
background: $primary-darken-1; # Keep HistoryScreen CSS
|
67
69
|
}
|
68
|
-
|
70
|
+
|
69
71
|
#button-row { # Keep HistoryScreen CSS
|
70
72
|
width: 100%; # Keep HistoryScreen CSS
|
71
73
|
height: 3;
|
@@ -114,11 +116,11 @@ class HistoryScreen(Screen):
|
|
114
116
|
|
115
117
|
class SimpleChatApp(App): # Keep SimpleChatApp class definition
|
116
118
|
"""Simplified Chat CLI application.""" # Keep SimpleChatApp docstring
|
117
|
-
|
119
|
+
|
118
120
|
TITLE = "Chat Console"
|
119
121
|
SUB_TITLE = "AI Chat Interface" # Keep SimpleChatApp SUB_TITLE
|
120
122
|
DARK = True # Keep SimpleChatApp DARK
|
121
|
-
|
123
|
+
|
122
124
|
# Ensure the log directory exists in a standard cache location
|
123
125
|
log_dir = os.path.expanduser("~/.cache/chat-cli")
|
124
126
|
os.makedirs(log_dir, exist_ok=True)
|
@@ -131,6 +133,24 @@ class SimpleChatApp(App): # Keep SimpleChatApp class definition
|
|
131
133
|
padding: 0 1;
|
132
134
|
}
|
133
135
|
|
136
|
+
#app-info-bar {
|
137
|
+
width: 100%;
|
138
|
+
height: 1;
|
139
|
+
background: $surface-darken-3;
|
140
|
+
color: $text-muted;
|
141
|
+
padding: 0 1;
|
142
|
+
}
|
143
|
+
|
144
|
+
#version-info {
|
145
|
+
width: auto;
|
146
|
+
text-align: left;
|
147
|
+
}
|
148
|
+
|
149
|
+
#model-info {
|
150
|
+
width: 1fr;
|
151
|
+
text-align: right;
|
152
|
+
}
|
153
|
+
|
134
154
|
#conversation-title { # Keep SimpleChatApp CSS
|
135
155
|
width: 100%; # Keep SimpleChatApp CSS
|
136
156
|
height: 2;
|
@@ -141,6 +161,19 @@ class SimpleChatApp(App): # Keep SimpleChatApp class definition
|
|
141
161
|
border-bottom: solid $primary-darken-2;
|
142
162
|
}
|
143
163
|
|
164
|
+
#action-buttons {
|
165
|
+
width: 100%;
|
166
|
+
height: auto;
|
167
|
+
padding: 0 1; /* Corrected padding: 0 vertical, 1 horizontal */
|
168
|
+
align-horizontal: center;
|
169
|
+
background: $surface-darken-1;
|
170
|
+
}
|
171
|
+
|
172
|
+
#new-chat-button, #change-title-button {
|
173
|
+
margin: 0 1;
|
174
|
+
min-width: 15;
|
175
|
+
}
|
176
|
+
|
144
177
|
#messages-container { # Keep SimpleChatApp CSS
|
145
178
|
width: 100%; # Keep SimpleChatApp CSS
|
146
179
|
height: 1fr;
|
@@ -243,23 +276,22 @@ class SimpleChatApp(App): # Keep SimpleChatApp class definition
|
|
243
276
|
align: center middle;
|
244
277
|
}
|
245
278
|
"""
|
246
|
-
|
279
|
+
|
247
280
|
BINDINGS = [ # Keep SimpleChatApp BINDINGS, ensure Enter is not globally bound for settings
|
248
281
|
Binding("q", "quit", "Quit", show=True, key_display="q"),
|
249
|
-
#
|
250
|
-
Binding("n", "action_new_conversation", "New Chat", show=True, key_display="n"),
|
251
|
-
Binding("c", "action_new_conversation", "New Chat", show=False, key_display="c"), #
|
282
|
+
# Add priority=True to ensure these capture before input
|
283
|
+
Binding("n", "action_new_conversation", "New Chat", show=True, key_display="n", priority=True),
|
284
|
+
Binding("c", "action_new_conversation", "New Chat", show=False, key_display="c", priority=True), # Add priority to alias too
|
252
285
|
Binding("escape", "escape", "Cancel / Stop", show=True, key_display="esc"), # Escape might close settings panel too
|
253
286
|
Binding("ctrl+c", "quit", "Quit", show=False),
|
254
|
-
Binding("h", "view_history", "History", show=True, key_display="h"), #
|
255
|
-
Binding("s", "settings", "Settings", show=True, key_display="s"), #
|
256
|
-
|
257
|
-
Binding("t", "action_update_title", "Update Title", show=True, key_display="t"),
|
287
|
+
Binding("h", "view_history", "History", show=True, key_display="h", priority=True), # Add priority
|
288
|
+
Binding("s", "settings", "Settings", show=True, key_display="s", priority=True), # Add priority
|
289
|
+
Binding("t", "action_update_title", "Update Title", show=True, key_display="t", priority=True), # Add priority
|
258
290
|
] # Keep SimpleChatApp BINDINGS end
|
259
291
|
|
260
292
|
current_conversation = reactive(None) # Keep SimpleChatApp reactive var
|
261
293
|
is_generating = reactive(False) # Keep SimpleChatApp reactive var
|
262
|
-
|
294
|
+
|
263
295
|
def __init__(self, initial_text: Optional[str] = None): # Keep SimpleChatApp __init__
|
264
296
|
super().__init__() # Keep SimpleChatApp __init__
|
265
297
|
self.db = ChatDatabase() # Keep SimpleChatApp __init__
|
@@ -272,24 +304,33 @@ class SimpleChatApp(App): # Keep SimpleChatApp class definition
|
|
272
304
|
def compose(self) -> ComposeResult: # Modify SimpleChatApp compose
|
273
305
|
"""Create the simplified application layout."""
|
274
306
|
yield Header()
|
275
|
-
|
307
|
+
|
276
308
|
with Vertical(id="main-content"):
|
309
|
+
# Add app info bar with version and model info
|
310
|
+
with Horizontal(id="app-info-bar"):
|
311
|
+
yield Static(f"Chat Console v{__version__}", id="version-info") # Use imported version
|
312
|
+
yield Static(f"Model: {self.selected_model}", id="model-info")
|
313
|
+
|
277
314
|
# Conversation title
|
278
315
|
yield Static("New Conversation", id="conversation-title")
|
279
|
-
|
316
|
+
|
317
|
+
# Add action buttons at the top for visibility
|
318
|
+
with Horizontal(id="action-buttons"):
|
319
|
+
yield Button("+ New Chat", id="new-chat-button", variant="success")
|
320
|
+
yield Button("✎ Change Title", id="change-title-button", variant="primary")
|
321
|
+
|
280
322
|
# Messages area
|
281
323
|
with ScrollableContainer(id="messages-container"):
|
282
324
|
# Will be populated with messages
|
283
325
|
pass
|
284
|
-
|
326
|
+
|
285
327
|
# Loading indicator
|
286
328
|
yield Static("Generating response...", id="loading-indicator", classes="hidden")
|
287
|
-
|
329
|
+
|
288
330
|
# Input area
|
289
331
|
with Container(id="input-area"):
|
290
332
|
# Use the custom InputWithFocus widget
|
291
333
|
yield InputWithFocus(placeholder="Type your message here...", id="message-input")
|
292
|
-
# Removed Static widgets previously used for diagnosis
|
293
334
|
|
294
335
|
# --- Add Settings Panel (hidden initially) ---
|
295
336
|
with Container(id="settings-panel"):
|
@@ -299,18 +340,30 @@ class SimpleChatApp(App): # Keep SimpleChatApp class definition
|
|
299
340
|
with Horizontal(id="settings-buttons"):
|
300
341
|
yield Button("Save", id="settings-save-button", variant="success")
|
301
342
|
yield Button("Cancel", id="settings-cancel-button", variant="error")
|
302
|
-
|
343
|
+
|
303
344
|
yield Footer()
|
304
|
-
|
345
|
+
|
305
346
|
async def on_mount(self) -> None: # Keep SimpleChatApp on_mount
|
306
347
|
"""Initialize the application on mount.""" # Keep SimpleChatApp on_mount docstring
|
348
|
+
# Add diagnostic logging for bindings
|
349
|
+
print(f"Registered bindings: {self.__class__.BINDINGS}") # Corrected access to class attribute
|
350
|
+
|
351
|
+
# Update the version display (already imported at top)
|
352
|
+
try:
|
353
|
+
version_info = self.query_one("#version-info", Static)
|
354
|
+
version_info.update(f"Chat Console v{__version__}")
|
355
|
+
except Exception:
|
356
|
+
pass # Silently ignore if widget not found yet
|
357
|
+
|
358
|
+
self.update_app_info() # Update the model info
|
359
|
+
|
307
360
|
# Check API keys and services # Keep SimpleChatApp on_mount
|
308
361
|
api_issues = [] # Keep SimpleChatApp on_mount
|
309
362
|
if not OPENAI_API_KEY: # Keep SimpleChatApp on_mount
|
310
363
|
api_issues.append("- OPENAI_API_KEY is not set") # Keep SimpleChatApp on_mount
|
311
364
|
if not ANTHROPIC_API_KEY: # Keep SimpleChatApp on_mount
|
312
365
|
api_issues.append("- ANTHROPIC_API_KEY is not set") # Keep SimpleChatApp on_mount
|
313
|
-
|
366
|
+
|
314
367
|
# Check Ollama availability and try to start if not running # Keep SimpleChatApp on_mount
|
315
368
|
from app.utils import ensure_ollama_running # Keep SimpleChatApp on_mount
|
316
369
|
if not ensure_ollama_running(): # Keep SimpleChatApp on_mount
|
@@ -325,7 +378,7 @@ class SimpleChatApp(App): # Keep SimpleChatApp class definition
|
|
325
378
|
api_issues.append("- No Ollama models found") # Keep SimpleChatApp on_mount
|
326
379
|
except Exception: # Keep SimpleChatApp on_mount
|
327
380
|
api_issues.append("- Error connecting to Ollama server") # Keep SimpleChatApp on_mount
|
328
|
-
|
381
|
+
|
329
382
|
if api_issues: # Keep SimpleChatApp on_mount
|
330
383
|
self.notify( # Keep SimpleChatApp on_mount
|
331
384
|
"Service issues detected:\n" + "\n".join(api_issues) + # Keep SimpleChatApp on_mount
|
@@ -334,10 +387,10 @@ class SimpleChatApp(App): # Keep SimpleChatApp class definition
|
|
334
387
|
severity="warning", # Keep SimpleChatApp on_mount
|
335
388
|
timeout=10 # Keep SimpleChatApp on_mount
|
336
389
|
) # Keep SimpleChatApp on_mount
|
337
|
-
|
390
|
+
|
338
391
|
# Create a new conversation # Keep SimpleChatApp on_mount
|
339
392
|
await self.create_new_conversation() # Keep SimpleChatApp on_mount
|
340
|
-
|
393
|
+
|
341
394
|
# If initial text was provided, send it # Keep SimpleChatApp on_mount
|
342
395
|
if self.initial_text: # Keep SimpleChatApp on_mount
|
343
396
|
input_widget = self.query_one("#message-input", Input) # Keep SimpleChatApp on_mount
|
@@ -354,39 +407,35 @@ class SimpleChatApp(App): # Keep SimpleChatApp class definition
|
|
354
407
|
# Create new conversation in database using selected model and style # Keep SimpleChatApp create_new_conversation
|
355
408
|
model = self.selected_model # Keep SimpleChatApp create_new_conversation
|
356
409
|
style = self.selected_style # Keep SimpleChatApp create_new_conversation
|
357
|
-
|
410
|
+
|
358
411
|
# Create a title for the new conversation # Keep SimpleChatApp create_new_conversation
|
359
412
|
title = f"New conversation ({datetime.now().strftime('%Y-%m-%d %H:%M')})" # Keep SimpleChatApp create_new_conversation
|
360
|
-
|
413
|
+
|
361
414
|
# Create conversation in database using the correct method # Keep SimpleChatApp create_new_conversation
|
362
415
|
log(f"Creating conversation with title: {title}, model: {model}, style: {style}") # Added log
|
363
416
|
conversation_id = self.db.create_conversation(title, model, style) # Keep SimpleChatApp create_new_conversation
|
364
417
|
log(f"Database returned conversation_id: {conversation_id}") # Added log
|
365
|
-
|
418
|
+
|
366
419
|
# Get the full conversation data # Keep SimpleChatApp create_new_conversation
|
367
420
|
conversation_data = self.db.get_conversation(conversation_id) # Keep SimpleChatApp create_new_conversation
|
368
|
-
|
421
|
+
|
369
422
|
# Set as current conversation # Keep SimpleChatApp create_new_conversation
|
370
423
|
self.current_conversation = Conversation.from_dict(conversation_data) # Keep SimpleChatApp create_new_conversation
|
371
|
-
|
424
|
+
|
372
425
|
# Update UI # Keep SimpleChatApp create_new_conversation
|
373
|
-
|
374
|
-
|
375
|
-
|
426
|
+
title_widget = self.query_one("#conversation-title", Static) # Keep SimpleChatApp create_new_conversation
|
427
|
+
title_widget.update(self.current_conversation.title) # Keep SimpleChatApp create_new_conversation
|
428
|
+
|
376
429
|
# Clear messages and update UI # Keep SimpleChatApp create_new_conversation
|
377
430
|
self.messages = [] # Keep SimpleChatApp create_new_conversation
|
378
431
|
log("Finished updating messages UI in create_new_conversation") # Added log
|
379
432
|
await self.update_messages_ui() # Keep SimpleChatApp create_new_conversation
|
433
|
+
self.update_app_info() # Update model info after potentially loading conversation
|
380
434
|
|
381
435
|
async def action_new_conversation(self) -> None: # Keep SimpleChatApp action_new_conversation
|
382
436
|
"""Handle the new conversation action.""" # Keep SimpleChatApp action_new_conversation docstring
|
383
437
|
log("--- ENTERING action_new_conversation ---") # Add entry log
|
384
|
-
|
385
|
-
# Check if the currently focused widget is the input widget
|
386
|
-
currently_focused = self.focused
|
387
|
-
if currently_focused and currently_focused.id == "message-input":
|
388
|
-
log("action_new_conversation skipped: input has focus")
|
389
|
-
return
|
438
|
+
# Focus check removed - relying on priority=True in binding
|
390
439
|
|
391
440
|
log("action_new_conversation EXECUTING") # Add execution log
|
392
441
|
await self.create_new_conversation() # Keep SimpleChatApp action_new_conversation
|
@@ -413,24 +462,41 @@ class SimpleChatApp(App): # Keep SimpleChatApp class definition
|
|
413
462
|
log("Escape pressed, but settings not visible and not generating.") # Added log
|
414
463
|
# pass # Keep SimpleChatApp action_escape comment
|
415
464
|
|
416
|
-
|
465
|
+
def update_app_info(self) -> None:
|
466
|
+
"""Update the displayed app information."""
|
467
|
+
try:
|
468
|
+
# Update model info
|
469
|
+
model_info = self.query_one("#model-info", Static)
|
470
|
+
model_display = self.selected_model
|
471
|
+
|
472
|
+
# Try to get a more readable name from config if available
|
473
|
+
if self.selected_model in CONFIG["available_models"]:
|
474
|
+
provider = CONFIG["available_models"][self.selected_model]["provider"]
|
475
|
+
display_name = CONFIG["available_models"][self.selected_model]["display_name"]
|
476
|
+
model_display = f"{display_name} ({provider.capitalize()})"
|
477
|
+
|
478
|
+
model_info.update(f"Model: {model_display}")
|
479
|
+
except Exception as e:
|
480
|
+
# Silently handle errors to prevent crashes
|
481
|
+
log.error(f"Error updating app info: {e}") # Log error instead of passing silently
|
482
|
+
pass
|
417
483
|
|
418
484
|
async def update_messages_ui(self) -> None: # Keep SimpleChatApp update_messages_ui
|
419
485
|
"""Update the messages UI.""" # Keep SimpleChatApp update_messages_ui docstring
|
420
486
|
# Clear existing messages # Keep SimpleChatApp update_messages_ui
|
421
487
|
messages_container = self.query_one("#messages-container") # Keep SimpleChatApp update_messages_ui
|
422
488
|
messages_container.remove_children() # Keep SimpleChatApp update_messages_ui
|
423
|
-
|
489
|
+
|
424
490
|
# Add messages with a small delay between each # Keep SimpleChatApp update_messages_ui
|
425
491
|
for message in self.messages: # Keep SimpleChatApp update_messages_ui
|
426
492
|
display = MessageDisplay(message, highlight_code=CONFIG["highlight_code"]) # Keep SimpleChatApp update_messages_ui
|
427
493
|
messages_container.mount(display) # Keep SimpleChatApp update_messages_ui
|
428
494
|
messages_container.scroll_end(animate=False) # Keep SimpleChatApp update_messages_ui
|
429
495
|
await asyncio.sleep(0.01) # Small delay to prevent UI freezing # Keep SimpleChatApp update_messages_ui
|
430
|
-
|
496
|
+
|
431
497
|
# Final scroll to bottom # Keep SimpleChatApp update_messages_ui
|
432
498
|
messages_container.scroll_end(animate=False) # Keep SimpleChatApp update_messages_ui
|
433
|
-
|
499
|
+
|
434
500
|
async def on_input_submitted(self, event: Input.Submitted) -> None: # Keep SimpleChatApp on_input_submitted
|
435
501
|
"""Handle input submission (Enter key in the main input).""" # Keep SimpleChatApp on_input_submitted docstring
|
436
502
|
await self.action_send_message() # Restore direct call # Keep SimpleChatApp on_input_submitted
|
@@ -439,38 +505,38 @@ class SimpleChatApp(App): # Keep SimpleChatApp class definition
|
|
439
505
|
"""Initiate message sending.""" # Keep SimpleChatApp action_send_message docstring
|
440
506
|
input_widget = self.query_one("#message-input", Input) # Keep SimpleChatApp action_send_message
|
441
507
|
content = input_widget.value.strip() # Keep SimpleChatApp action_send_message
|
442
|
-
|
508
|
+
|
443
509
|
if not content or not self.current_conversation: # Keep SimpleChatApp action_send_message
|
444
510
|
return # Keep SimpleChatApp action_send_message
|
445
|
-
|
511
|
+
|
446
512
|
# Clear input # Keep SimpleChatApp action_send_message
|
447
513
|
input_widget.value = "" # Keep SimpleChatApp action_send_message
|
448
|
-
|
514
|
+
|
449
515
|
# Create user message # Keep SimpleChatApp action_send_message
|
450
516
|
user_message = Message(role="user", content=content) # Keep SimpleChatApp action_send_message
|
451
517
|
self.messages.append(user_message) # Keep SimpleChatApp action_send_message
|
452
|
-
|
518
|
+
|
453
519
|
# Save to database # Keep SimpleChatApp action_send_message
|
454
520
|
self.db.add_message( # Keep SimpleChatApp action_send_message
|
455
521
|
self.current_conversation.id, # Keep SimpleChatApp action_send_message
|
456
522
|
"user", # Keep SimpleChatApp action_send_message
|
457
523
|
content # Keep SimpleChatApp action_send_message
|
458
524
|
) # Keep SimpleChatApp action_send_message
|
459
|
-
|
525
|
+
|
460
526
|
# Check if this is the first message in the conversation
|
461
527
|
# Note: We check length *before* adding the potential assistant message
|
462
|
-
is_first_message = len(self.messages) == 1
|
463
|
-
|
528
|
+
is_first_message = len(self.messages) == 1
|
529
|
+
|
464
530
|
# Update UI with user message first
|
465
531
|
await self.update_messages_ui()
|
466
|
-
|
532
|
+
|
467
533
|
# If this is the first message and dynamic titles are enabled, generate one
|
468
534
|
if is_first_message and self.current_conversation and CONFIG.get("generate_dynamic_titles", True):
|
469
535
|
log("First message detected, generating title...")
|
470
536
|
title_generation_in_progress = True # Use a local flag
|
471
537
|
loading = self.query_one("#loading-indicator")
|
472
538
|
loading.remove_class("hidden") # Show loading for title gen
|
473
|
-
|
539
|
+
|
474
540
|
try:
|
475
541
|
# Get appropriate client
|
476
542
|
model = self.selected_model
|
@@ -488,16 +554,16 @@ class SimpleChatApp(App): # Keep SimpleChatApp class definition
|
|
488
554
|
self.current_conversation.id,
|
489
555
|
title=title
|
490
556
|
)
|
491
|
-
|
557
|
+
|
492
558
|
# Update UI title
|
493
559
|
title_widget = self.query_one("#conversation-title", Static)
|
494
560
|
title_widget.update(title)
|
495
|
-
|
561
|
+
|
496
562
|
# Update conversation object
|
497
563
|
self.current_conversation.title = title
|
498
|
-
|
564
|
+
|
499
565
|
self.notify(f"Conversation title set to: {title}", severity="information", timeout=3)
|
500
|
-
|
566
|
+
|
501
567
|
except Exception as e:
|
502
568
|
log.error(f"Failed to generate title: {str(e)}")
|
503
569
|
self.notify(f"Failed to generate title: {str(e)}", severity="warning")
|
@@ -510,25 +576,25 @@ class SimpleChatApp(App): # Keep SimpleChatApp class definition
|
|
510
576
|
|
511
577
|
# Generate AI response (will set self.is_generating and handle loading indicator)
|
512
578
|
await self.generate_response()
|
513
|
-
|
579
|
+
|
514
580
|
# Focus back on input
|
515
581
|
input_widget.focus()
|
516
|
-
|
582
|
+
|
517
583
|
async def generate_response(self) -> None: # Keep SimpleChatApp generate_response
|
518
584
|
"""Generate an AI response.""" # Keep SimpleChatApp generate_response docstring
|
519
585
|
if not self.current_conversation or not self.messages: # Keep SimpleChatApp generate_response
|
520
586
|
return # Keep SimpleChatApp generate_response
|
521
|
-
|
587
|
+
|
522
588
|
self.is_generating = True # Keep SimpleChatApp generate_response
|
523
589
|
log(f"Setting is_generating to True") # Added log
|
524
590
|
loading = self.query_one("#loading-indicator") # Keep SimpleChatApp generate_response
|
525
591
|
loading.remove_class("hidden") # Keep SimpleChatApp generate_response
|
526
|
-
|
592
|
+
|
527
593
|
try: # Keep SimpleChatApp generate_response
|
528
594
|
# Get conversation parameters # Keep SimpleChatApp generate_response
|
529
595
|
model = self.selected_model # Keep SimpleChatApp generate_response
|
530
596
|
style = self.selected_style # Keep SimpleChatApp generate_response
|
531
|
-
|
597
|
+
|
532
598
|
# Convert messages to API format # Keep SimpleChatApp generate_response
|
533
599
|
api_messages = [] # Keep SimpleChatApp generate_response
|
534
600
|
for msg in self.messages: # Keep SimpleChatApp generate_response
|
@@ -536,7 +602,7 @@ class SimpleChatApp(App): # Keep SimpleChatApp class definition
|
|
536
602
|
"role": msg.role, # Keep SimpleChatApp generate_response
|
537
603
|
"content": msg.content # Keep SimpleChatApp generate_response
|
538
604
|
}) # Keep SimpleChatApp generate_response
|
539
|
-
|
605
|
+
|
540
606
|
# Get appropriate client # Keep SimpleChatApp generate_response
|
541
607
|
try: # Keep SimpleChatApp generate_response
|
542
608
|
client = BaseModelClient.get_client_for_model(model) # Keep SimpleChatApp generate_response
|
@@ -545,7 +611,7 @@ class SimpleChatApp(App): # Keep SimpleChatApp class definition
|
|
545
611
|
except Exception as e: # Keep SimpleChatApp generate_response
|
546
612
|
self.notify(f"Failed to initialize model client: {str(e)}", severity="error") # Keep SimpleChatApp generate_response
|
547
613
|
return # Keep SimpleChatApp generate_response
|
548
|
-
|
614
|
+
|
549
615
|
# Start streaming response # Keep SimpleChatApp generate_response
|
550
616
|
assistant_message = Message(role="assistant", content="Thinking...") # Keep SimpleChatApp generate_response
|
551
617
|
self.messages.append(assistant_message) # Keep SimpleChatApp generate_response
|
@@ -553,24 +619,24 @@ class SimpleChatApp(App): # Keep SimpleChatApp class definition
|
|
553
619
|
message_display = MessageDisplay(assistant_message, highlight_code=CONFIG["highlight_code"]) # Keep SimpleChatApp generate_response
|
554
620
|
messages_container.mount(message_display) # Keep SimpleChatApp generate_response
|
555
621
|
messages_container.scroll_end(animate=False) # Keep SimpleChatApp generate_response
|
556
|
-
|
622
|
+
|
557
623
|
# Add small delay to show thinking state # Keep SimpleChatApp generate_response
|
558
624
|
await asyncio.sleep(0.5) # Keep SimpleChatApp generate_response
|
559
|
-
|
625
|
+
|
560
626
|
# Stream chunks to the UI with synchronization # Keep SimpleChatApp generate_response
|
561
627
|
update_lock = asyncio.Lock() # Keep SimpleChatApp generate_response
|
562
|
-
|
628
|
+
|
563
629
|
async def update_ui(content: str): # Keep SimpleChatApp generate_response
|
564
630
|
if not self.is_generating: # Keep SimpleChatApp generate_response
|
565
631
|
log("update_ui called but is_generating is False, returning.") # Added log
|
566
632
|
return # Keep SimpleChatApp generate_response
|
567
|
-
|
633
|
+
|
568
634
|
async with update_lock: # Keep SimpleChatApp generate_response
|
569
635
|
try: # Keep SimpleChatApp generate_response
|
570
636
|
# Clear thinking indicator on first content # Keep SimpleChatApp generate_response
|
571
637
|
if assistant_message.content == "Thinking...": # Keep SimpleChatApp generate_response
|
572
638
|
assistant_message.content = "" # Keep SimpleChatApp generate_response
|
573
|
-
|
639
|
+
|
574
640
|
# Update message with full content so far # Keep SimpleChatApp generate_response
|
575
641
|
assistant_message.content = content # Keep SimpleChatApp generate_response
|
576
642
|
# Update UI with full content # Keep SimpleChatApp generate_response
|
@@ -583,7 +649,7 @@ class SimpleChatApp(App): # Keep SimpleChatApp class definition
|
|
583
649
|
self.refresh(layout=True) # Keep SimpleChatApp generate_response
|
584
650
|
except Exception as e: # Keep SimpleChatApp generate_response
|
585
651
|
log.error(f"Error updating UI: {str(e)}") # Use log instead of logger
|
586
|
-
|
652
|
+
|
587
653
|
# Generate the response with timeout and cleanup # Keep SimpleChatApp generate_response
|
588
654
|
generation_task = None # Keep SimpleChatApp generate_response
|
589
655
|
try: # Keep SimpleChatApp generate_response
|
@@ -598,10 +664,10 @@ class SimpleChatApp(App): # Keep SimpleChatApp class definition
|
|
598
664
|
update_ui # Keep SimpleChatApp generate_response
|
599
665
|
) # Keep SimpleChatApp generate_response
|
600
666
|
) # Keep SimpleChatApp generate_response
|
601
|
-
|
667
|
+
|
602
668
|
# Wait for response with timeout # Keep SimpleChatApp generate_response
|
603
669
|
full_response = await asyncio.wait_for(generation_task, timeout=60) # Longer timeout # Keep SimpleChatApp generate_response
|
604
|
-
|
670
|
+
|
605
671
|
# Save to database only if we got a complete response # Keep SimpleChatApp generate_response
|
606
672
|
if self.is_generating and full_response: # Keep SimpleChatApp generate_response
|
607
673
|
log("Generation finished, saving full response to DB") # Added log
|
@@ -615,19 +681,19 @@ class SimpleChatApp(App): # Keep SimpleChatApp class definition
|
|
615
681
|
await asyncio.sleep(0.1) # Wait for UI to update # Keep SimpleChatApp generate_response
|
616
682
|
elif not full_response:
|
617
683
|
log("Generation finished but full_response is empty/None") # Added log
|
618
|
-
|
684
|
+
|
619
685
|
except asyncio.TimeoutError: # Keep SimpleChatApp generate_response
|
620
686
|
log.error("Response generation timed out") # Use log instead of logger
|
621
687
|
error_msg = "Response generation timed out. The model may be busy or unresponsive. Please try again." # Keep SimpleChatApp generate_response
|
622
688
|
self.notify(error_msg, severity="error") # Keep SimpleChatApp generate_response
|
623
|
-
|
689
|
+
|
624
690
|
# Remove the incomplete message # Keep SimpleChatApp generate_response
|
625
691
|
if self.messages and self.messages[-1].role == "assistant": # Keep SimpleChatApp generate_response
|
626
692
|
self.messages.pop() # Keep SimpleChatApp generate_response
|
627
|
-
|
693
|
+
|
628
694
|
# Update UI to remove the incomplete message # Keep SimpleChatApp generate_response
|
629
695
|
await self.update_messages_ui() # Keep SimpleChatApp generate_response
|
630
|
-
|
696
|
+
|
631
697
|
finally: # Keep SimpleChatApp generate_response
|
632
698
|
# Ensure task is properly cancelled and cleaned up # Keep SimpleChatApp generate_response
|
633
699
|
if generation_task: # Keep SimpleChatApp generate_response
|
@@ -638,10 +704,10 @@ class SimpleChatApp(App): # Keep SimpleChatApp class definition
|
|
638
704
|
await generation_task # Keep SimpleChatApp generate_response
|
639
705
|
except (asyncio.CancelledError, Exception) as e: # Keep SimpleChatApp generate_response
|
640
706
|
log.error(f"Error cleaning up generation task: {str(e)}") # Use log instead of logger
|
641
|
-
|
707
|
+
|
642
708
|
# Force a final UI refresh # Keep SimpleChatApp generate_response
|
643
709
|
self.refresh(layout=True) # Keep SimpleChatApp generate_response
|
644
|
-
|
710
|
+
|
645
711
|
except Exception as e: # Keep SimpleChatApp generate_response
|
646
712
|
log.error(f"Exception during generate_response: {str(e)}") # Added log
|
647
713
|
self.notify(f"Error generating response: {str(e)}", severity="error") # Keep SimpleChatApp generate_response
|
@@ -654,21 +720,31 @@ class SimpleChatApp(App): # Keep SimpleChatApp class definition
|
|
654
720
|
self.is_generating = False # Keep SimpleChatApp generate_response
|
655
721
|
loading = self.query_one("#loading-indicator") # Keep SimpleChatApp generate_response
|
656
722
|
loading.add_class("hidden") # Keep SimpleChatApp generate_response
|
657
|
-
|
723
|
+
|
658
724
|
def on_model_selector_model_selected(self, event: ModelSelector.ModelSelected) -> None: # Keep SimpleChatApp on_model_selector_model_selected
|
659
725
|
"""Handle model selection""" # Keep SimpleChatApp on_model_selector_model_selected docstring
|
660
726
|
self.selected_model = event.model_id # Keep SimpleChatApp on_model_selector_model_selected
|
661
|
-
|
727
|
+
self.update_app_info() # Update the displayed model info
|
728
|
+
|
662
729
|
def on_style_selector_style_selected(self, event: StyleSelector.StyleSelected) -> None: # Keep SimpleChatApp on_style_selector_style_selected
|
663
730
|
"""Handle style selection""" # Keep SimpleChatApp on_style_selector_style_selected docstring
|
664
731
|
self.selected_style = event.style_id # Keep SimpleChatApp on_style_selector_style_selected
|
665
|
-
|
732
|
+
|
666
733
|
async def on_button_pressed(self, event: Button.Pressed) -> None: # Modify SimpleChatApp on_button_pressed
|
667
734
|
"""Handle button presses."""
|
668
735
|
button_id = event.button.id
|
669
|
-
|
736
|
+
|
737
|
+
if button_id == "new-chat-button":
|
738
|
+
# Create a new chat
|
739
|
+
await self.create_new_conversation()
|
740
|
+
# Focus back on input after creating new chat
|
741
|
+
self.query_one("#message-input").focus()
|
742
|
+
elif button_id == "change-title-button":
|
743
|
+
# Change title
|
744
|
+
# Note: action_update_title already checks self.current_conversation
|
745
|
+
await self.action_update_title()
|
670
746
|
# --- Handle Settings Panel Buttons ---
|
671
|
-
|
747
|
+
elif button_id == "settings-cancel-button":
|
672
748
|
settings_panel = self.query_one("#settings-panel")
|
673
749
|
settings_panel.remove_class("visible")
|
674
750
|
self.query_one("#message-input").focus() # Focus input after closing
|
@@ -678,10 +754,10 @@ class SimpleChatApp(App): # Keep SimpleChatApp class definition
|
|
678
754
|
# Get selected values (assuming selectors update self.selected_model/style directly via events)
|
679
755
|
model_to_save = self.selected_model
|
680
756
|
style_to_save = self.selected_style
|
681
|
-
|
757
|
+
|
682
758
|
# Save globally
|
683
759
|
save_settings_to_config(model_to_save, style_to_save)
|
684
|
-
|
760
|
+
|
685
761
|
# Update current conversation if one exists
|
686
762
|
if self.current_conversation:
|
687
763
|
self.db.update_conversation(
|
@@ -699,11 +775,11 @@ class SimpleChatApp(App): # Keep SimpleChatApp class definition
|
|
699
775
|
settings_panel = self.query_one("#settings-panel")
|
700
776
|
settings_panel.remove_class("visible")
|
701
777
|
self.query_one("#message-input").focus() # Focus input after closing
|
702
|
-
|
778
|
+
|
703
779
|
# --- Keep other button logic if needed (currently none) ---
|
704
780
|
# elif button_id == "send-button": # Example if send button existed
|
705
781
|
# await self.action_send_message()
|
706
|
-
|
782
|
+
|
707
783
|
async def view_chat_history(self) -> None: # Keep SimpleChatApp view_chat_history
|
708
784
|
"""Show chat history in a popup.""" # Keep SimpleChatApp view_chat_history docstring
|
709
785
|
# Get recent conversations # Keep SimpleChatApp view_chat_history
|
@@ -711,32 +787,33 @@ class SimpleChatApp(App): # Keep SimpleChatApp class definition
|
|
711
787
|
if not conversations: # Keep SimpleChatApp view_chat_history
|
712
788
|
self.notify("No chat history found", severity="warning") # Keep SimpleChatApp view_chat_history
|
713
789
|
return # Keep SimpleChatApp view_chat_history
|
714
|
-
|
790
|
+
|
715
791
|
async def handle_selection(selected_id: int) -> None: # Keep SimpleChatApp view_chat_history
|
716
792
|
if not selected_id: # Keep SimpleChatApp view_chat_history
|
717
793
|
return # Keep SimpleChatApp view_chat_history
|
718
|
-
|
794
|
+
|
719
795
|
# Get full conversation # Keep SimpleChatApp view_chat_history
|
720
796
|
conversation_data = self.db.get_conversation(selected_id) # Keep SimpleChatApp view_chat_history
|
721
797
|
if not conversation_data: # Keep SimpleChatApp view_chat_history
|
722
798
|
self.notify("Could not load conversation", severity="error") # Keep SimpleChatApp view_chat_history
|
723
799
|
return # Keep SimpleChatApp view_chat_history
|
724
|
-
|
800
|
+
|
725
801
|
# Update current conversation # Keep SimpleChatApp view_chat_history
|
726
802
|
self.current_conversation = Conversation.from_dict(conversation_data) # Keep SimpleChatApp view_chat_history
|
727
|
-
|
803
|
+
|
728
804
|
# Update title # Keep SimpleChatApp view_chat_history
|
729
805
|
title = self.query_one("#conversation-title", Static) # Keep SimpleChatApp view_chat_history
|
730
806
|
title.update(self.current_conversation.title) # Keep SimpleChatApp view_chat_history
|
731
|
-
|
807
|
+
|
732
808
|
# Load messages # Keep SimpleChatApp view_chat_history
|
733
809
|
self.messages = [Message(**msg) for msg in self.current_conversation.messages] # Keep SimpleChatApp view_chat_history
|
734
810
|
await self.update_messages_ui() # Keep SimpleChatApp view_chat_history
|
735
|
-
|
811
|
+
|
736
812
|
# Update model and style selectors # Keep SimpleChatApp view_chat_history
|
737
813
|
self.selected_model = self.current_conversation.model # Keep SimpleChatApp view_chat_history
|
738
814
|
self.selected_style = self.current_conversation.style # Keep SimpleChatApp view_chat_history
|
739
|
-
|
815
|
+
self.update_app_info() # Update info bar after loading history
|
816
|
+
|
740
817
|
self.push_screen(HistoryScreen(conversations, handle_selection)) # Keep SimpleChatApp view_chat_history
|
741
818
|
|
742
819
|
async def action_view_history(self) -> None: # Keep SimpleChatApp action_view_history
|
@@ -766,12 +843,7 @@ class SimpleChatApp(App): # Keep SimpleChatApp class definition
|
|
766
843
|
async def action_update_title(self) -> None:
|
767
844
|
"""Allow users to manually change the conversation title"""
|
768
845
|
log("--- ENTERING action_update_title ---") # Add entry log
|
769
|
-
|
770
|
-
# Check focus using self.focused instead of has_focus
|
771
|
-
currently_focused = self.focused
|
772
|
-
if currently_focused and currently_focused.id == "message-input":
|
773
|
-
log("action_update_title skipped: input has focus")
|
774
|
-
return
|
846
|
+
# Focus check removed - relying on priority=True in binding
|
775
847
|
|
776
848
|
log("action_update_title EXECUTING") # Add execution log
|
777
849
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: chat-console
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.2.0
|
4
4
|
Summary: A command-line interface for chatting with LLMs, storing chats and (future) rag interactions
|
5
5
|
Home-page: https://github.com/wazacraftrfid/chat-console
|
6
6
|
Author: Johnathan Greenaway
|
@@ -1,7 +1,7 @@
|
|
1
1
|
app/__init__.py,sha256=OeqboIrx_Kjea0CY9Be8nLI-No1YWQfqbWIp-4lMOOI,131
|
2
2
|
app/config.py,sha256=sKNp6Za4ZfW-CZBOvEv0TncAS77AnKi86hTM51C4KQ4,5227
|
3
3
|
app/database.py,sha256=nt8CVuDpy6zw8mOYqDcfUmNw611t7Ln7pz22M0b6-MI,9967
|
4
|
-
app/main.py,sha256=
|
4
|
+
app/main.py,sha256=PMx9DcD2MAVyqeYanqu6mqr43RG4oXA9jFIdBwoQFQY,48174
|
5
5
|
app/models.py,sha256=4-y9Lytay2exWPFi0FDlVeRL3K2-I7E-jBqNzTfokqY,2644
|
6
6
|
app/utils.py,sha256=AgmLrmyikt1Y7KturNmZVK2eC6de-RfRadVbp3HmUAg,8434
|
7
7
|
app/api/__init__.py,sha256=A8UL84ldYlv8l7O-yKzraVFcfww86SgWfpl4p7R03-w,62
|
@@ -15,9 +15,9 @@ app/ui/chat_list.py,sha256=WQTYVNSSXlx_gQal3YqILZZKL9UiTjmNMIDX2I9pAMM,11205
|
|
15
15
|
app/ui/model_selector.py,sha256=Aj1irAs9DQMn8wfcPsFZGxWmx0JTzHjSe7pVdDMwqTQ,13182
|
16
16
|
app/ui/search.py,sha256=b-m14kG3ovqW1-i0qDQ8KnAqFJbi5b1FLM9dOnbTyIs,9763
|
17
17
|
app/ui/styles.py,sha256=04AhPuLrOd2yenfRySFRestPeuTPeMLzhmMB67NdGvw,5615
|
18
|
-
chat_console-0.
|
19
|
-
chat_console-0.
|
20
|
-
chat_console-0.
|
21
|
-
chat_console-0.
|
22
|
-
chat_console-0.
|
23
|
-
chat_console-0.
|
18
|
+
chat_console-0.2.0.dist-info/licenses/LICENSE,sha256=srHZ3fvcAuZY1LHxE7P6XWju2njRCHyK6h_ftEbzxSE,1057
|
19
|
+
chat_console-0.2.0.dist-info/METADATA,sha256=DPnX1Q1e7iTfNyyQRhJezaUXuHtq1E9mqb7k7gDe0GI,2921
|
20
|
+
chat_console-0.2.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
21
|
+
chat_console-0.2.0.dist-info/entry_points.txt,sha256=kkVdEc22U9PAi2AeruoKklfkng_a_aHAP6VRVwrAD7c,67
|
22
|
+
chat_console-0.2.0.dist-info/top_level.txt,sha256=io9g7LCbfmTG1SFKgEOGXmCFB9uMP2H5lerm0HiHWQE,4
|
23
|
+
chat_console-0.2.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|