diff --git a/README.md b/README.md index 98ffa21..c2b9a58 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,14 @@ export PATH="$HOME/.local/bin:$PATH" oai ``` +### 5. Alternative Installation (for *nix systems) + +If you have issues with the above method you can add an alias in your `.bashrc`, `.zshrc` etc. + +```bash +alias oai='python3 ' +``` + On first run, you will be prompted to enter your OpenRouter API key. ## Usage @@ -124,11 +132,16 @@ You> Analyze this image @/path/to/image.png ``` **View Session Stats:** + ``` /stats /credits ``` +**Prevous commands input:** + +Use the up/down arrows to see earlier `/`commands and earlier input to model and `` to execute the same command or resend the same input. + ## Command Reference Use `/help` within the application for a complete command reference organized by category: diff --git a/oai.py b/oai.py index f943643..605be12 100644 --- a/oai.py +++ b/oai.py @@ -123,6 +123,26 @@ def load_conversation(name: str) -> Optional[List[Dict[str, str]]]: return json.loads(result[0]) return None +def list_conversations() -> List[Dict[str, Any]]: + """List all saved conversations from DB with metadata.""" + with sqlite3.connect(DB_FILE) as conn: + cursor = conn.execute(''' + SELECT name, MAX(timestamp) as last_saved, data + FROM conversation_sessions + GROUP BY name + ORDER BY last_saved DESC + ''') + conversations = [] + for row in cursor.fetchall(): + name, timestamp, data_json = row + data = json.loads(data_json) + conversations.append({ + 'name': name, + 'timestamp': timestamp, + 'message_count': len(data) + }) + return conversations + def estimate_cost(input_tokens: int, output_tokens: int) -> float: """Estimate cost in USD based on token counts.""" return (input_tokens * MODEL_PRICING['input'] / 1_000_000) + (output_tokens * MODEL_PRICING['output'] / 1_000_000) @@ -314,6 +334,7 @@ def chat(): total_cost = 0.0 message_count = 0 middle_out_enabled = False # Session-level middle-out transform flag + saved_conversations_cache = [] # Cache for /list results to use with /load by number app_logger.info("Starting new chat session") # Log session start @@ -431,12 +452,28 @@ def chat(): elif user_input.lower().startswith("/load"): args = user_input[6:].strip() if not args: - console.print("[bold red]Usage: /load [/]") + console.print("[bold red]Usage: /load [/]") + console.print("[bold yellow]Tip: Use /list to see numbered conversations[/]") continue - loaded_data = load_conversation(args) + + # Check if input is a number + conversation_name = None + if args.isdigit(): + conv_number = int(args) + if saved_conversations_cache and 1 <= conv_number <= len(saved_conversations_cache): + conversation_name = saved_conversations_cache[conv_number - 1]['name'] + console.print(f"[bold cyan]Loading conversation #{conv_number}: '{conversation_name}'[/]") + else: + console.print(f"[bold red]Invalid conversation number: {conv_number}[/]") + console.print(f"[bold yellow]Use /list to see available conversations (1-{len(saved_conversations_cache) if saved_conversations_cache else 0})[/]") + continue + else: + conversation_name = args + + loaded_data = load_conversation(conversation_name) if not loaded_data: - console.print(f"[bold red]Conversation '{args}' not found.[/]") - app_logger.warning(f"Load failed for '{args}' - not found") + console.print(f"[bold red]Conversation '{conversation_name}' not found.[/]") + app_logger.warning(f"Load failed for '{conversation_name}' - not found") continue session_history = loaded_data current_index = len(session_history) - 1 @@ -444,8 +481,38 @@ def chat(): total_output_tokens = 0 total_cost = 0.0 message_count = 0 - console.print(f"[bold green]Conversation '{args}' loaded with {len(session_history)} messages.[/]") - app_logger.info(f"Conversation '{args}' loaded with {len(session_history)} messages") + console.print(f"[bold green]Conversation '{conversation_name}' loaded with {len(session_history)} messages.[/]") + app_logger.info(f"Conversation '{conversation_name}' loaded with {len(session_history)} messages") + continue + elif user_input.lower() == "/list": + conversations = list_conversations() + if not conversations: + console.print("[bold yellow]No saved conversations found.[/]") + app_logger.info("User viewed conversation list - empty") + saved_conversations_cache = [] + continue + + # Update cache for /load command + saved_conversations_cache = conversations + + table = Table("No.", "Name", "Messages", "Last Saved", show_header=True, header_style="bold magenta") + for idx, conv in enumerate(conversations, 1): + # Parse ISO timestamp and format it nicely + try: + dt = datetime.datetime.fromisoformat(conv['timestamp']) + formatted_time = dt.strftime('%Y-%m-%d %H:%M:%S') + except: + formatted_time = conv['timestamp'] + + table.add_row( + str(idx), + conv['name'], + str(conv['message_count']), + formatted_time + ) + + console.print(Panel(table, title=f"[bold green]Saved Conversations ({len(conversations)} total)[/]", title_align="left", subtitle="[dim]Use /load or /load to load a conversation[/]", subtitle_align="right")) + app_logger.info(f"User viewed conversation list - {len(conversations)} conversations") continue elif user_input.lower() == "/prev": if not session_history or current_index <= 0: @@ -775,7 +842,7 @@ def chat(): ) help_table.add_row( "/clear", - "Clear the terminal screen for a clean interface.", + "Clear the terminal screen for a clean interface. You can also use the keycombo [bold]ctrl+l[/]", "/clear" ) help_table.add_row( @@ -897,9 +964,14 @@ def chat(): "/export md notes.md\n/export html report.html" ) help_table.add_row( - "/load ", - "Load a saved conversation from database. Resets session metrics.", - "/load my_chat" + "/list", + "List all saved conversations with numbers, message counts, and timestamps.", + "/list" + ) + help_table.add_row( + "/load ", + "Load a saved conversation by name or number (from /list). Resets session metrics.", + "/load my_chat\n/load 3" ) help_table.add_row( "/save ",