chat-console 0.3.94__py3-none-any.whl → 0.3.991__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/__init__.py CHANGED
@@ -3,4 +3,4 @@ Chat CLI
3
3
  A command-line interface for chatting with various LLM providers like ChatGPT and Claude.
4
4
  """
5
5
 
6
- __version__ = "0.3.94"
6
+ __version__ = "0.3.991"
app/main.py CHANGED
@@ -643,127 +643,94 @@ class SimpleChatApp(App): # Keep SimpleChatApp class definition
643
643
  # Update UI with user message first
644
644
  await self.update_messages_ui()
645
645
 
646
- # If this is the first message and dynamic titles are enabled, generate one
647
- # Only attempt title generation if the message has sufficient content (at least 3 characters)
646
+ # If this is the first message and dynamic titles are enabled, start background title generation
648
647
  if is_first_message and self.current_conversation and CONFIG.get("generate_dynamic_titles", True) and len(content) >= 3:
649
- log("First message detected, generating title...")
650
- print(f"First message detected, generating conversation title for: {content[:30]}...")
651
- debug_log(f"First message detected with length {len(content)}, generating conversation title")
652
-
653
- # Show loading indicator for title generation
654
- loading = self.query_one("#loading-indicator")
655
- loading.remove_class("hidden")
656
- loading.update("🔤 Generating title...")
648
+ log("First message detected, starting background title generation...")
649
+ debug_log(f"First message detected with length {len(content)}, creating background title task")
650
+ asyncio.create_task(self._generate_title_background(content))
657
651
 
658
- try:
659
- # Get appropriate client
660
- model = self.selected_model
661
- print(f"Using model for title generation: {model}")
662
- debug_log(f"Selected model for title generation: '{model}'")
663
-
664
- # Check if model is valid
665
- if not model:
666
- debug_log("Model is empty, falling back to default")
667
- # Fallback to a safe default model - preferring OpenAI if key exists
668
- if OPENAI_API_KEY:
669
- model = "gpt-3.5-turbo"
670
- debug_log("Falling back to OpenAI gpt-3.5-turbo for title generation")
671
- elif ANTHROPIC_API_KEY:
672
- model = "claude-3-haiku-20240307" # Updated to newer Claude model
673
- debug_log("Falling back to Anthropic Claude 3 Haiku for title generation")
674
- else:
675
- # Last resort - use a common Ollama model
676
- model = "llama3" # Common default
677
- debug_log("Falling back to Ollama model: llama3")
652
+ # Start main response generation immediately
653
+ debug_log(f"About to call generate_response with model: '{self.selected_model}'")
654
+ await self.generate_response()
678
655
 
679
- debug_log(f"Getting client for model: {model}")
680
- client = await BaseModelClient.get_client_for_model(model)
681
-
682
- if client is None:
683
- debug_log(f"No client available for model: {model}, trying to initialize")
684
- # Try to determine client type and initialize manually
685
- client_type = BaseModelClient.get_client_type_for_model(model)
686
- if client_type:
687
- debug_log(f"Found client type {client_type.__name__} for {model}, initializing")
688
- try:
689
- client = await client_type.create()
690
- debug_log("Client initialized successfully")
691
- except Exception as init_err:
692
- debug_log(f"Error initializing client: {str(init_err)}")
693
-
694
- if client is None:
695
- debug_log("Could not initialize client, falling back to safer model")
696
- # Try a different model as last resort
697
- if OPENAI_API_KEY:
698
- from app.api.openai import OpenAIClient
699
- client = await OpenAIClient.create()
700
- model = "gpt-3.5-turbo"
701
- debug_log("Falling back to OpenAI for title generation")
702
- elif ANTHROPIC_API_KEY:
703
- from app.api.anthropic import AnthropicClient
704
- client = await AnthropicClient.create()
705
- model = "claude-3-haiku-20240307" # Updated to newer Claude model
706
- debug_log("Falling back to Anthropic for title generation")
707
- else:
708
- raise Exception("No valid API clients available for title generation")
656
+ # Focus back on input
657
+ input_widget.focus()
709
658
 
710
- # Generate title - make sure we're using the right client for the model
711
- print(f"Calling generate_conversation_title with model: {model}")
712
- log(f"Calling generate_conversation_title with model: {model}")
713
- debug_log(f"Calling generate_conversation_title with model: {model}, client type: {type(client).__name__}")
714
-
715
- # Double-check that we're using the right client for this model
716
- expected_client_type = BaseModelClient.get_client_type_for_model(model)
717
- if expected_client_type and not isinstance(client, expected_client_type):
718
- debug_log(f"Warning: Client type mismatch. Expected {expected_client_type.__name__}, got {type(client).__name__}")
719
- debug_log("Creating new client with correct type")
720
- client = await BaseModelClient.get_client_for_model(model)
721
-
722
- title = await generate_conversation_title(content, model, client)
723
- debug_log(f"Generated title: {title}")
724
- log(f"Generated title: {title}")
725
- print(f"Generated title: {title}")
659
+ async def _generate_title_background(self, content: str) -> None:
660
+ """Generates the conversation title in the background."""
661
+ if not self.current_conversation or not CONFIG.get("generate_dynamic_titles", True):
662
+ return
726
663
 
664
+ log("Starting background title generation...")
665
+ debug_log(f"Background title generation started for content: {content[:30]}...")
666
+
667
+ try:
668
+ # Use the logic from generate_conversation_title in utils.py
669
+ # It already prioritizes faster models (OpenAI/Anthropic)
670
+ # We need a client instance here. Let's get one based on priority.
671
+ title_client = None
672
+ title_model = None
673
+ from app.config import OPENAI_API_KEY, ANTHROPIC_API_KEY
674
+ from app.api.base import BaseModelClient
675
+
676
+ # Determine title client and model based on available keys
677
+ if OPENAI_API_KEY:
678
+ from app.api.openai import OpenAIClient
679
+ title_client = await OpenAIClient.create()
680
+ title_model = "gpt-3.5-turbo"
681
+ debug_log("Using OpenAI for background title generation")
682
+ elif ANTHROPIC_API_KEY:
683
+ from app.api.anthropic import AnthropicClient
684
+ title_client = await AnthropicClient.create()
685
+ title_model = "claude-3-haiku-20240307"
686
+ debug_log("Using Anthropic for background title generation")
687
+ else:
688
+ # Fallback to the currently selected model's client if no API keys
689
+ selected_model_resolved = resolve_model_id(self.selected_model)
690
+ title_client = await BaseModelClient.get_client_for_model(selected_model_resolved)
691
+ title_model = selected_model_resolved
692
+ debug_log(f"Using selected model's client ({type(title_client).__name__}) for background title generation")
693
+
694
+ if not title_client or not title_model:
695
+ raise Exception("Could not determine a client/model for title generation.")
696
+
697
+ # Call the utility function
698
+ from app.utils import generate_conversation_title # Import locally if needed
699
+ new_title = await generate_conversation_title(content, title_model, title_client)
700
+ debug_log(f"Background generated title: {new_title}")
701
+
702
+ # Check if title generation returned the default or a real title
703
+ if new_title and not new_title.startswith("Conversation ("):
727
704
  # Update conversation title in database
728
705
  self.db.update_conversation(
729
706
  self.current_conversation.id,
730
- title=title
707
+ title=new_title
731
708
  )
732
709
 
733
- # Update UI title
734
- title_widget = self.query_one("#conversation-title", Static)
735
- title_widget.update(title)
736
-
737
- # Update conversation object
738
- self.current_conversation.title = title
739
-
740
- # DO NOT update the selected model here - keep the user's original selection
741
- # This was causing issues with model mixing
742
- debug_log(f"Keeping original selected model: '{self.selected_model}'")
743
-
744
- self.notify(f"Conversation title set to: {title}", severity="information", timeout=3)
745
-
746
- except Exception as e:
747
- debug_log(f"Failed to generate title: {str(e)}")
748
- log.error(f"Failed to generate title: {str(e)}")
749
- print(f"Failed to generate title: {str(e)}")
750
- self.notify(f"Failed to generate title: {str(e)}", severity="warning")
751
- finally:
752
- # Hide loading indicator *only if* AI response generation isn't about to start
753
- if not self.is_generating:
754
- loading.add_class("hidden")
755
-
756
- # Small delay to ensure state is updated
757
- await asyncio.sleep(0.1)
758
-
759
- # Log just before generate_response call
760
- debug_log(f"About to call generate_response with model: '{self.selected_model}'")
761
-
762
- # Generate AI response (will set self.is_generating and handle loading indicator)
763
- await self.generate_response()
710
+ # Update UI title (if conversation hasn't changed)
711
+ # Check if the current conversation ID still matches
712
+ # Need to fetch the conversation again to be sure, or check against self.current_conversation.id
713
+ current_conv_id = self.current_conversation.id if self.current_conversation else None
714
+ if current_conv_id and self.db.get_conversation(current_conv_id): # Check if conversation still exists
715
+ # Check if the app's current conversation is still the same one
716
+ if self.current_conversation and self.current_conversation.id == current_conv_id:
717
+ title_widget = self.query_one("#conversation-title", Static)
718
+ title_widget.update(new_title)
719
+ self.current_conversation.title = new_title # Update local object too
720
+ log(f"Background title update successful: {new_title}")
721
+ # Maybe a subtle notification? Optional.
722
+ # self.notify(f"Title set: {new_title}", severity="information", timeout=2)
723
+ else:
724
+ log("Conversation changed before background title update could apply.")
725
+ else:
726
+ log(f"Conversation with ID {current_conv_id} no longer exists. Skipping title update.")
727
+ else:
728
+ log(f"Background title generation resulted in default or empty title: '{new_title}'. Not updating.")
764
729
 
765
- # Focus back on input
766
- input_widget.focus()
730
+ except Exception as e:
731
+ debug_log(f"Background title generation failed: {str(e)}")
732
+ log.error(f"Background title generation failed: {str(e)}")
733
+ # Do not notify the user, just log the error.
767
734
 
768
735
  async def generate_response(self) -> None:
769
736
  """Generate an AI response using a non-blocking worker with fallback."""
app/ui/chat_interface.py CHANGED
@@ -136,7 +136,7 @@ class MessageDisplay(Static): # Inherit from Static instead of RichLog
136
136
  # Force a complete replacement
137
137
  self.message.content = content
138
138
  formatted_content = self._format_content(content)
139
- self.update(formatted_content, refresh=True)
139
+ self.update(formatted_content)
140
140
 
141
141
  # Force app-level refresh
142
142
  try:
@@ -153,9 +153,10 @@ class MessageDisplay(Static): # Inherit from Static instead of RichLog
153
153
  # For all other updates - ALWAYS update
154
154
  self.message.content = content
155
155
  formatted_content = self._format_content(content)
156
- self.update(formatted_content, refresh=True)
156
+ # Ensure the update call doesn't have refresh=True
157
+ self.update(formatted_content)
157
158
 
158
- # Force refresh
159
+ # Force refresh using app.refresh() instead of passing to update()
159
160
  try:
160
161
  if self.app:
161
162
  self.app.refresh(layout=True)
app/utils.py CHANGED
@@ -33,15 +33,15 @@ async def generate_conversation_title(message: str, model: str, client: Any) ->
33
33
  # Try-except the entire function to ensure we always return a title
34
34
  try:
35
35
  # Pick a reliable title generation model - prefer OpenAI if available
36
- from ..config import OPENAI_API_KEY, ANTHROPIC_API_KEY
36
+ from app.config import OPENAI_API_KEY, ANTHROPIC_API_KEY
37
37
 
38
38
  if OPENAI_API_KEY:
39
- from ..api.openai import OpenAIClient
39
+ from app.api.openai import OpenAIClient
40
40
  title_client = await OpenAIClient.create()
41
41
  title_model = "gpt-3.5-turbo"
42
42
  debug_log("Using OpenAI for title generation")
43
43
  elif ANTHROPIC_API_KEY:
44
- from ..api.anthropic import AnthropicClient
44
+ from app.api.anthropic import AnthropicClient
45
45
  title_client = await AnthropicClient.create()
46
46
  title_model = "claude-3-haiku-20240307"
47
47
  debug_log("Using Anthropic for title generation")
@@ -753,7 +753,9 @@ def resolve_model_id(model_id_or_name: str) -> str:
753
753
  "04-turbo": "gpt-4-turbo",
754
754
  "035": "gpt-3.5-turbo",
755
755
  "35-turbo": "gpt-3.5-turbo",
756
- "35": "gpt-3.5-turbo"
756
+ "35": "gpt-3.5-turbo",
757
+ "4.1-mini": "gpt-4.1-mini", # Add support for gpt-4.1-mini
758
+ "4.1": "gpt-4.1" # Add support for gpt-4.1
757
759
  }
758
760
 
759
761
  if input_lower in openai_model_aliases:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chat-console
3
- Version: 0.3.94
3
+ Version: 0.3.991
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,24 +1,24 @@
1
- app/__init__.py,sha256=TW6khejFFoiUQaPE4GrBSNhjPphu3d8_DnTATL6tl9c,131
1
+ app/__init__.py,sha256=ZWMbaTFHYgXqy4qglFzcSE1yk3aIh1w759fHYF8d-K4,132
2
2
  app/config.py,sha256=xeRGXcKbNvAdQGkaJJBipM4yHZJTM1y4ZFoW764APOU,7661
3
3
  app/database.py,sha256=nt8CVuDpy6zw8mOYqDcfUmNw611t7Ln7pz22M0b6-MI,9967
4
- app/main.py,sha256=RmQtbIKtgo92Y5Ue0NbozNP_9BsswLee1rw2la9yEH8,78953
4
+ app/main.py,sha256=dqQ2kxJL_94jcQFCT6tF5PLIH336tBTVUGkZhK4V8i4,77258
5
5
  app/models.py,sha256=4-y9Lytay2exWPFi0FDlVeRL3K2-I7E-jBqNzTfokqY,2644
6
- app/utils.py,sha256=9bi7mYkt4fp7w8m5CgXJrecozugfqdPD4vYkJZvCQ-c,38703
6
+ app/utils.py,sha256=dDT7iiH-BXPiFhJCK095X05ofb65z97p-YXH-OuOK3k,38830
7
7
  app/api/__init__.py,sha256=A8UL84ldYlv8l7O-yKzraVFcfww86SgWfpl4p7R03-w,62
8
8
  app/api/anthropic.py,sha256=uInwNvGLJ_iPUs4BjdwaqXTU6NfmK1SzX7498Pt44fI,10667
9
9
  app/api/base.py,sha256=zvlHHfIcaObefkJ3w4er9ZSX7YGZ_MM0H-wrzD8CGAM,7629
10
10
  app/api/ollama.py,sha256=eFG24nI2MlF57z9EHiA97v02NgFJ0kxaPUX26xAXFsg,66154
11
11
  app/api/openai.py,sha256=hLPr955tUx_2vwRuLP8Zrl3vu7kQZgUETi4cJuaYnFE,10810
12
12
  app/ui/__init__.py,sha256=RndfbQ1Tv47qdSiuQzvWP96lPS547SDaGE-BgOtiP_w,55
13
- app/ui/chat_interface.py,sha256=e_BJMIRtH9YX85-bwNOixz3St9fRn4C_eHhZtvz35D0,18180
13
+ app/ui/chat_interface.py,sha256=oSDZi0Jgj_L8WnBh1RuJpIeIcN-RQ38CNejwsXiWTVg,18267
14
14
  app/ui/chat_list.py,sha256=WQTYVNSSXlx_gQal3YqILZZKL9UiTjmNMIDX2I9pAMM,11205
15
15
  app/ui/model_browser.py,sha256=pdblLVkdyVF0_Bo02bqbErGAtieyH-y6IfhMOPEqIso,71124
16
16
  app/ui/model_selector.py,sha256=9fIPpAiqb568idt9pdROAYaxpoqY9czMF-bGdOl4nYk,18861
17
17
  app/ui/search.py,sha256=b-m14kG3ovqW1-i0qDQ8KnAqFJbi5b1FLM9dOnbTyIs,9763
18
18
  app/ui/styles.py,sha256=04AhPuLrOd2yenfRySFRestPeuTPeMLzhmMB67NdGvw,5615
19
- chat_console-0.3.94.dist-info/licenses/LICENSE,sha256=srHZ3fvcAuZY1LHxE7P6XWju2njRCHyK6h_ftEbzxSE,1057
20
- chat_console-0.3.94.dist-info/METADATA,sha256=9KzoIz0DiVXSzjkis3eRQtcCJRhyCLJe2L9Kx3olp8A,2922
21
- chat_console-0.3.94.dist-info/WHEEL,sha256=SmOxYU7pzNKBqASvQJ7DjX3XGUF92lrGhMb3R6_iiqI,91
22
- chat_console-0.3.94.dist-info/entry_points.txt,sha256=kkVdEc22U9PAi2AeruoKklfkng_a_aHAP6VRVwrAD7c,67
23
- chat_console-0.3.94.dist-info/top_level.txt,sha256=io9g7LCbfmTG1SFKgEOGXmCFB9uMP2H5lerm0HiHWQE,4
24
- chat_console-0.3.94.dist-info/RECORD,,
19
+ chat_console-0.3.991.dist-info/licenses/LICENSE,sha256=srHZ3fvcAuZY1LHxE7P6XWju2njRCHyK6h_ftEbzxSE,1057
20
+ chat_console-0.3.991.dist-info/METADATA,sha256=LrgZQlvm_Pslv_T4RhVul8rPCfHvL0riJw5p4AsZ-iM,2923
21
+ chat_console-0.3.991.dist-info/WHEEL,sha256=ck4Vq1_RXyvS4Jt6SI0Vz6fyVs4GWg7AINwpsaGEgPE,91
22
+ chat_console-0.3.991.dist-info/entry_points.txt,sha256=kkVdEc22U9PAi2AeruoKklfkng_a_aHAP6VRVwrAD7c,67
23
+ chat_console-0.3.991.dist-info/top_level.txt,sha256=io9g7LCbfmTG1SFKgEOGXmCFB9uMP2H5lerm0HiHWQE,4
24
+ chat_console-0.3.991.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (79.0.1)
2
+ Generator: setuptools (80.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5