From 90fcba28bca6813eb6877df5e85fae8d8f5fdbff Mon Sep 17 00:00:00 2001 From: Paul Bailey Date: Mon, 23 Feb 2026 00:59:20 +0000 Subject: [PATCH] Enhancement Suite: Anti-loop protection, Kokoro TTS integration, ComfyUI status monitoring, and auto-open image functionality --- build/lib/ollama_cli/__init__.py | 0 build/lib/ollama_cli/agents/__init__.py | 0 build/lib/ollama_cli/core/__init__.py | 0 build/lib/ollama_cli/core/config.py | 57 +++ build/lib/ollama_cli/core/ollama.py | 87 ++++ build/lib/ollama_cli/core/sessions.py | 53 +++ build/lib/ollama_cli/integrations/__init__.py | 0 build/lib/ollama_cli/integrations/lsp.py | 75 +++ build/lib/ollama_cli/integrations/mcp.py | 103 +++++ build/lib/ollama_cli/integrations/notify.py | 64 +++ build/lib/ollama_cli/main.py | 433 ++++++++++++++++++ build/lib/ollama_cli/tools/__init__.py | 0 build/lib/ollama_cli/tools/base.py | 55 +++ build/lib/ollama_cli/tools/code.py | 96 ++++ build/lib/ollama_cli/tools/execution.py | 36 ++ build/lib/ollama_cli/tools/filesystem.py | 83 ++++ build/lib/ollama_cli/tools/git.py | 52 +++ build/lib/ollama_cli/tools/knowledge.py | 41 ++ build/lib/ollama_cli/tools/media.py | 183 ++++++++ build/lib/ollama_cli/tools/memory.py | 59 +++ build/lib/ollama_cli/tools/system.py | 46 ++ build/lib/ollama_cli/tools/web.py | 67 +++ build/lib/ollama_cli/ui/__init__.py | 0 build/lib/ollama_cli/ui/formatter.py | 40 ++ build/lib/ollama_cli/ui/repl.py | 25 + install.sh | 41 +- output.wav | Bin 0 -> 145964 bytes src/ollama_cli.egg-info/SOURCES.txt | 3 + .../__pycache__/main.cpython-312.pyc | Bin 15856 -> 22039 bytes .../core/__pycache__/config.cpython-312.pyc | Bin 2118 -> 2352 bytes src/ollama_cli/core/config.py | 6 + src/ollama_cli/main.py | 138 +++++- .../tools/__pycache__/code.cpython-312.pyc | Bin 4451 -> 5722 bytes .../__pycache__/execution.cpython-312.pyc | Bin 0 -> 1827 bytes .../__pycache__/filesystem.cpython-312.pyc | Bin 3608 -> 4503 bytes .../tools/__pycache__/media.cpython-312.pyc | Bin 0 -> 10578 bytes .../tools/__pycache__/memory.cpython-312.pyc | Bin 0 -> 3121 bytes .../tools/__pycache__/system.cpython-312.pyc | Bin 1726 -> 2521 bytes .../tools/__pycache__/web.cpython-312.pyc | Bin 1718 -> 3770 bytes src/ollama_cli/tools/media.py | 175 ++++++- src/ollama_cli/tools/memory.py | 4 +- 41 files changed, 1982 insertions(+), 40 deletions(-) create mode 100644 build/lib/ollama_cli/__init__.py create mode 100644 build/lib/ollama_cli/agents/__init__.py create mode 100644 build/lib/ollama_cli/core/__init__.py create mode 100644 build/lib/ollama_cli/core/config.py create mode 100644 build/lib/ollama_cli/core/ollama.py create mode 100644 build/lib/ollama_cli/core/sessions.py create mode 100644 build/lib/ollama_cli/integrations/__init__.py create mode 100644 build/lib/ollama_cli/integrations/lsp.py create mode 100644 build/lib/ollama_cli/integrations/mcp.py create mode 100644 build/lib/ollama_cli/integrations/notify.py create mode 100644 build/lib/ollama_cli/main.py create mode 100644 build/lib/ollama_cli/tools/__init__.py create mode 100644 build/lib/ollama_cli/tools/base.py create mode 100644 build/lib/ollama_cli/tools/code.py create mode 100644 build/lib/ollama_cli/tools/execution.py create mode 100644 build/lib/ollama_cli/tools/filesystem.py create mode 100644 build/lib/ollama_cli/tools/git.py create mode 100644 build/lib/ollama_cli/tools/knowledge.py create mode 100644 build/lib/ollama_cli/tools/media.py create mode 100644 build/lib/ollama_cli/tools/memory.py create mode 100644 build/lib/ollama_cli/tools/system.py create mode 100644 build/lib/ollama_cli/tools/web.py create mode 100644 build/lib/ollama_cli/ui/__init__.py create mode 100644 build/lib/ollama_cli/ui/formatter.py create mode 100644 build/lib/ollama_cli/ui/repl.py create mode 100644 output.wav create mode 100644 src/ollama_cli/tools/__pycache__/execution.cpython-312.pyc create mode 100644 src/ollama_cli/tools/__pycache__/media.cpython-312.pyc create mode 100644 src/ollama_cli/tools/__pycache__/memory.cpython-312.pyc diff --git a/build/lib/ollama_cli/__init__.py b/build/lib/ollama_cli/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/build/lib/ollama_cli/agents/__init__.py b/build/lib/ollama_cli/agents/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/build/lib/ollama_cli/core/__init__.py b/build/lib/ollama_cli/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/build/lib/ollama_cli/core/config.py b/build/lib/ollama_cli/core/config.py new file mode 100644 index 0000000..0c8987a --- /dev/null +++ b/build/lib/ollama_cli/core/config.py @@ -0,0 +1,57 @@ +import os +import json + +CONFIG_FILE = os.path.expanduser("~/.ollama-cli-config.json") + +DEFAULT_CONFIG = { + "excluded_models": [], + "git": { + "remote_url": "", + "username": "", + "password": "" + }, + "gitea": { + "url": "", + "token": "" + }, + "auto_save_sessions": True, + "mcp_servers": {}, + "ollama_url": "http://localhost:11434", + "comfy_url": "http://localhost:9000", + "piper": { + "path": "~/.ollama-cli/piper/piper", + "model": "~/.ollama-cli/piper/voice.onnx" + }, + "ntfy": { + "enabled": False, + "topic": "", + "server": "https://ntfy.sh" + }, + "default_model": "llama3.2" +} + +def load_config(): + """Load config file""" + if os.path.exists(CONFIG_FILE): + try: + with open(CONFIG_FILE, 'r') as f: + config = json.load(f) + # Merge with defaults to ensure all keys exist + for k, v in DEFAULT_CONFIG.items(): + if k not in config: + config[k] = v + return config + except Exception: + pass + return DEFAULT_CONFIG.copy() + +def save_config(config): + """Save config file""" + with open(CONFIG_FILE, 'w') as f: + json.dump(config, f, indent=2) + +def update_config(key, value): + """Update a specific config key""" + config = load_config() + config[key] = value + save_config(config) diff --git a/build/lib/ollama_cli/core/ollama.py b/build/lib/ollama_cli/core/ollama.py new file mode 100644 index 0000000..d415281 --- /dev/null +++ b/build/lib/ollama_cli/core/ollama.py @@ -0,0 +1,87 @@ +import json +import requests +import subprocess +import sys +from typing import List, Dict, Optional, Generator + +class OllamaClient: + def __init__(self, base_url: str = "http://localhost:11434"): + # Strip trailing slashes and common API paths if user provided them in config + self.base_url = base_url.rstrip("/") + if self.base_url.endswith("/api/chat"): + self.base_url = self.base_url[:-9] + elif self.base_url.endswith("/api"): + self.base_url = self.base_url[:-4] + + self.chat_url = f"{self.base_url}/api/chat" + self.list_url = f"{self.base_url}/api/tags" + + def get_available_models(self) -> List[str]: + """Get list of models available in Ollama""" + try: + # Try API first + response = requests.get(self.list_url, timeout=5) + if response.status_code == 200: + data = response.json() + return [m['name'] for m in data.get('models', [])] + + # Fallback to CLI + result = subprocess.run("ollama list", shell=True, capture_output=True, text=True, timeout=5) + models = [] + for line in result.stdout.split('\n')[1:]: # Skip header + if line.strip(): + models.append(line.split()[0]) + return models + except Exception: + return [] + + def chat(self, messages: List[Dict], model: str, stream: bool = True) -> Generator[str, None, str]: + """Call Ollama API with streaming""" + try: + response = requests.post(self.chat_url, json={ + "model": model, + "messages": messages, + "stream": stream + }, stream=stream, timeout=60) + response.raise_for_status() + + full_response = "" + if stream: + for line in response.iter_lines(): + if line: + chunk = json.loads(line) + if "message" in chunk and "content" in chunk["message"]: + content = chunk["message"]["content"] + full_response += content + yield content + return full_response + else: + data = response.json() + return data.get("message", {}).get("content", "") + + except requests.exceptions.ConnectionError: + raise ConnectionError(f"Cannot connect to Ollama at {self.base_url}. Make sure it's running.") + except Exception as e: + raise Exception(f"Error calling Ollama: {str(e)}") + + def select_best_model(self, user_input: str, available_models: List[str], default_model: str) -> str: + """Select best model based on task type""" + if not available_models: + return default_model + + # Model preferences + code_models = ["qwen2.5-coder", "mistral-nemo", "codellama", "deepseek-coder"] + general_models = ["llama3.2", "llama3.1", "mistral"] + + # Simple heuristic + code_keywords = ['code', 'script', 'program', 'function', 'debug', 'python', 'javascript'] + is_code_task = any(k in user_input.lower() for k in code_keywords) + + target_list = code_models if is_code_task else general_models + + for target in target_list: + for available in available_models: + if target in available.lower(): + return available + + return available_models[0] if available_models else default_model diff --git a/build/lib/ollama_cli/core/sessions.py b/build/lib/ollama_cli/core/sessions.py new file mode 100644 index 0000000..bc136c7 --- /dev/null +++ b/build/lib/ollama_cli/core/sessions.py @@ -0,0 +1,53 @@ +import os +import json +from datetime import datetime +from typing import List, Dict, Optional + +SESSION_DIR = os.path.expanduser("~/.ollama-cli-sessions") + +def save_session(messages: List[Dict], session_name: Optional[str] = None) -> str: + """Save conversation session""" + os.makedirs(SESSION_DIR, exist_ok=True) + if not session_name: + session_name = datetime.now().strftime("%Y%m%d_%H%M%S") + + # Ensure filename is safe + safe_name = "".join([c for c in session_name if c.isalnum() or c in (' ', '.', '_', '-')]).strip() + session_file = os.path.join(SESSION_DIR, f"{safe_name}.json") + + with open(session_file, 'w') as f: + json.dump({ + "messages": messages, + "timestamp": datetime.now().isoformat(), + "name": session_name + }, f, indent=2) + return session_file + +def load_session(session_name: str) -> Optional[List[Dict]]: + """Load conversation session""" + session_file = os.path.join(SESSION_DIR, f"{session_name}.json") + if not os.path.exists(session_file): + # Try without extension if name doesn't have it + if not session_name.endswith('.json'): + session_file = os.path.join(SESSION_DIR, f"{session_name}.json") + + if os.path.exists(session_file): + with open(session_file, 'r') as f: + data = json.load(f) + return data.get("messages", []) + return None + +def list_sessions() -> List[str]: + """List available sessions""" + if not os.path.exists(SESSION_DIR): + return [] + sessions = [f.replace('.json', '') for f in os.listdir(SESSION_DIR) if f.endswith('.json')] + return sorted(sessions, reverse=True) + +def delete_session(session_name: str) -> bool: + """Delete a session""" + session_file = os.path.join(SESSION_DIR, f"{session_name}.json") + if os.path.exists(session_file): + os.remove(session_file) + return True + return False diff --git a/build/lib/ollama_cli/integrations/__init__.py b/build/lib/ollama_cli/integrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/build/lib/ollama_cli/integrations/lsp.py b/build/lib/ollama_cli/integrations/lsp.py new file mode 100644 index 0000000..58153b3 --- /dev/null +++ b/build/lib/ollama_cli/integrations/lsp.py @@ -0,0 +1,75 @@ +import subprocess +import os +import json +from typing import Dict, Any, Optional + +LSP_SERVERS = {} + +def start_lsp_server(language: str, root_path: str = ".") -> str: + """Start a language server""" + lsp_commands = { + "python": ["pyright-langserver", "--stdio"], + "javascript": ["typescript-language-server", "--stdio"], + "typescript": ["typescript-language-server", "--stdio"], + "rust": ["rust-analyzer"], + "go": ["gopls"], + } + + if language not in lsp_commands: + return f"No LSP server configured for {language}" + + try: + process = subprocess.Popen( + lsp_commands[language], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + cwd=os.path.expanduser(root_path) + ) + + # Initialize + init_request = { + "jsonrpc": "2.0", + "id": 1, + "method": "initialize", + "params": { + "processId": os.getpid(), + "rootUri": f"file://{os.path.abspath(os.path.expanduser(root_path))}", + "capabilities": {} + } + } + + msg = json.dumps(init_request) + process.stdin.write(f"Content-Length: {len(msg)}\r\n\r\n{msg}") + process.stdin.flush() + + LSP_SERVERS[language] = process + return f"Started LSP server for {language}" + except Exception as e: + return f"Error starting LSP: {str(e)}" + +def lsp_goto_definition(language: str, file_path: str, line: int, character: int) -> str: + """Get definition location""" + if language not in LSP_SERVERS: + return "LSP server not started" + + try: + process = LSP_SERVERS[language] + request = { + "jsonrpc": "2.0", + "id": 2, + "method": "textDocument/definition", + "params": { + "textDocument": {"uri": f"file://{os.path.abspath(file_path)}"}, + "position": {"line": line, "character": character} + } + } + + msg = json.dumps(request) + process.stdin.write(f"Content-Length: {len(msg)}\r\n\r\n{msg}") + process.stdin.flush() + + return "Definition lookup sent (response parsing not implemented)" + except Exception as e: + return f"LSP error: {str(e)}" diff --git a/build/lib/ollama_cli/integrations/mcp.py b/build/lib/ollama_cli/integrations/mcp.py new file mode 100644 index 0000000..f2fa29f --- /dev/null +++ b/build/lib/ollama_cli/integrations/mcp.py @@ -0,0 +1,103 @@ +import subprocess +import json +import os +from typing import Dict, Any, Optional, List + +MCP_SERVERS = {} +MCP_TOOLS = {} + +def connect_mcp_server(name: str, command: str, args: Optional[List[str]] = None) -> str: + """Connect to an MCP server via stdio""" + try: + cmd = [command] + (args or []) + process = subprocess.Popen( + cmd, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + bufsize=1 + ) + + # Send initialize request + init_request = { + "jsonrpc": "2.0", + "id": 1, + "method": "initialize", + "params": { + "protocolVersion": "2024-11-05", + "capabilities": {}, + "clientInfo": {"name": "ollama-cli", "version": "3.0"} + } + } + + process.stdin.write(json.dumps(init_request) + "\n") + process.stdin.flush() + + # Read response + response = process.stdout.readline() + if response: + data = json.loads(response) + if "result" in data: + MCP_SERVERS[name] = process + + # List tools + tools_request = { + "jsonrpc": "2.0", + "id": 2, + "method": "tools/list" + } + process.stdin.write(json.dumps(tools_request) + "\n") + process.stdin.flush() + + tools_response = process.stdout.readline() + if tools_response: + tools_data = json.loads(tools_response) + if "result" in tools_data and "tools" in tools_data["result"]: + for tool in tools_data["result"]["tools"]: + tool_name = f"mcp_{name}_{tool['name']}" + MCP_TOOLS[tool_name] = { + "server": name, + "name": tool["name"], + "description": tool.get("description", ""), + "schema": tool.get("inputSchema", {}) + } + + return f"Connected to MCP server '{name}' with {len([t for t in MCP_TOOLS if t.startswith(f'mcp_{name}_')])} tools" + + return f"Failed to initialize MCP server '{name}'" + except Exception as e: + return f"Error connecting to MCP server: {str(e)}" + +def call_mcp_tool(server_name: str, tool_name: str, arguments: Dict[str, Any]) -> str: + """Call a tool on an MCP server""" + try: + if server_name not in MCP_SERVERS: + return f"Error: MCP server '{server_name}' not connected" + + process = MCP_SERVERS[server_name] + + request = { + "jsonrpc": "2.0", + "id": 3, + "method": "tools/call", + "params": { + "name": tool_name, + "arguments": arguments + } + } + + process.stdin.write(json.dumps(request) + "\n") + process.stdin.flush() + + response = process.stdout.readline() + if response: + data = json.loads(response) + if "result" in data: + return json.dumps(data["result"], indent=2) + elif "error" in data: + return f"MCP Error: {data['error']}" + + return "No response from MCP server" + except Exception as e: + return f"Error calling MCP tool: {str(e)}" diff --git a/build/lib/ollama_cli/integrations/notify.py b/build/lib/ollama_cli/integrations/notify.py new file mode 100644 index 0000000..c7a2da5 --- /dev/null +++ b/build/lib/ollama_cli/integrations/notify.py @@ -0,0 +1,64 @@ +import requests +import json +import threading +from typing import Callable, List, Dict + +def send_notification(config: Dict, title: str, message: str, priority: str = "default"): + """Send notification via ntfy.sh""" + ntfy_config = config.get("ntfy", {}) + + if not ntfy_config.get("enabled") or not ntfy_config.get("topic"): + return + + try: + server = ntfy_config.get("server", "https://ntfy.sh") + topic = ntfy_config["topic"] + url = f"{server}/{topic}" + + requests.post(url, + data=message.encode('utf-8'), + headers={ + "Title": title, + "Priority": priority + }, + timeout=5 + ) + except: + pass + +def ntfy_listener(config: Dict, on_message: Callable[[str], str]): + """Listen for ntfy notifications and process them""" + ntfy_config = config.get("ntfy", {}) + + if not ntfy_config.get("enabled") or not ntfy_config.get("topic"): + return + + server = ntfy_config.get("server", "https://ntfy.sh") + topic = ntfy_config["topic"] + url = f"{server}/{topic}/json" + + try: + response = requests.get(url, stream=True, timeout=None) + for line in response.iter_lines(): + if line: + data = json.loads(line) + if data.get("event") == "message": + title = data.get("title", "") + message = data.get("message", "") + + # Skip messages sent by this script + if title in ["Response", "Ollama CLI", "File Created", "Command Complete"]: + continue + + # Process message + response_text = on_message(message) + + # Send response back + send_notification(config, "Response", response_text[:1000]) + except: + pass + +def start_notify_thread(config: Dict, on_message: Callable[[str], str]): + thread = threading.Thread(target=ntfy_listener, args=(config, on_message), daemon=True) + thread.start() + return thread diff --git a/build/lib/ollama_cli/main.py b/build/lib/ollama_cli/main.py new file mode 100644 index 0000000..91bc83c --- /dev/null +++ b/build/lib/ollama_cli/main.py @@ -0,0 +1,433 @@ +""" +Ollama CLI v3.0 +License: MIT +Author: Gemini CLI +""" + +import sys +import os +import json +import re +import logging +from typing import List, Dict, Optional, Tuple + +# Setup logging +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', + handlers=[ + logging.FileHandler(os.path.expanduser("~/.ollama-cli.log")), + ] +) +logger = logging.getLogger("ollama-cli") + +# Add current directory to path +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from ollama_cli.core.config import load_config, save_config, update_config +from ollama_cli.core.ollama import OllamaClient +from ollama_cli.core.sessions import save_session, load_session, list_sessions +from ollama_cli.ui.formatter import console, print_markdown, print_error, print_status, StreamingDisplay +from ollama_cli.ui.repl import REPL +from ollama_cli.tools.base import registry +import ollama_cli.tools.filesystem +import ollama_cli.tools.system +import ollama_cli.tools.web +import ollama_cli.tools.git +import ollama_cli.tools.code +import ollama_cli.tools.knowledge +import ollama_cli.tools.memory +import ollama_cli.tools.execution +import ollama_cli.tools.media +from ollama_cli.integrations.mcp import connect_mcp_server, call_mcp_tool, MCP_TOOLS +from ollama_cli.integrations.lsp import start_lsp_server, lsp_goto_definition +from ollama_cli.integrations.notify import start_notify_thread, send_notification + +VERSION = "3.0.0" + +class OllamaCLI: + def __init__(self): + self.config = load_config() + self.client = OllamaClient(self.config.get("ollama_url")) + self.repl = REPL() + self.messages = [] + self.available_models = [] + self.current_model = self.config.get("default_model", "llama3.2") + self.system_prompt = self._build_system_prompt() + self.max_tool_iterations = 5 + + def _build_system_prompt(self) -> str: + base_prompt = """You are a highly capable AI assistant powered by Ollama. +You have access to a variety of tools to help you complete tasks. + +CRITICAL RULES: +1. When you need to use a tool, you MUST use the EXACT format below. +2. Output ONLY the tool call. DO NOT explain what you are doing. +3. DO NOT repeat a tool call if you have already received a result for it. +4. If you have the answer, just give it. Do not use tools unless necessary. + + +name_of_tool +{"param": "value"} + + +You can call multiple tools in one response by repeating the block above.""" + + tool_desc = registry.generate_system_prompt_snippet() + + # Add MCP tools if any + if MCP_TOOLS: + tool_desc += "\nMCP Tools:\n" + for name, info in MCP_TOOLS.items(): + tool_desc += f"- {name}: {info['description']}\n" + + return f"{base_prompt}\n\n{tool_desc}" + + def reset_messages(self): + self.messages = [{"role": "system", "content": self.system_prompt}] + + def handle_command(self, user_input: str) -> bool: + """Handle slash commands.""" + if not user_input.startswith('/'): + return False + + parts = user_input.split() + cmd = parts[0].lower() + + try: + if cmd == '/help': + help_text = """ +[bold cyan]Ollama CLI v3.0 Help[/bold cyan] + +[bold]General Commands:[/bold] + /help - Show this help message + /quit - Exit the CLI + /clear - Clear conversation history + +[bold]Model & Sessions:[/bold] + /models - List available models + /model - Switch current model + /sessions - List saved sessions + /save [name] - Save current session + /load - Load a saved session + +[bold]Core Agent Tools:[/bold] + [blue]Filesystem:[/blue] read_file, write_file, list_directory, grep_search, get_tree + [blue]Code & Python:[/blue] run_python, replace_text, code_analyze_file, run_linter + [blue]Web:[/blue] web_search, read_url + [blue]Media:[/blue] generate_image, speak_text + [blue]Knowledge:[/blue] kb_add, kb_search, remember_fact, recall_facts, clear_memory + +[bold]Integrations:[/bold] + /mcp connect [args] - Connect MCP server + /lsp start [path] - Start LSP server + /notify setup - Setup ntfy.sh remote control + +[bold]Configuration:[/bold] + /config ollama - Set Ollama server URL (default: http://localhost:11434) + /config comfy - Set ComfyUI server URL (default: http://127.0.0.1:8188) + /config piper_path - Set path to Piper binary + /config piper_model - Set path to Piper voice model +""" + console.print(help_text) + elif cmd == '/config': + if len(parts) == 1: + cfg = load_config() + config_display = f""" +[bold cyan]Current Configuration:[/bold cyan] + [blue]Ollama URL:[/blue] {cfg.get('ollama_url')} + [blue]ComfyUI URL:[/blue] {cfg.get('comfy_url')} + [blue]Image Model:[/blue] {cfg.get('image_model')} + [blue]Piper Path:[/blue] {cfg.get('piper', {}).get('path')} + [blue]Piper Model:[/blue] {cfg.get('piper', {}).get('model')} + [blue]Default Model:[/blue] {cfg.get('default_model')} +""" + console.print(config_display) + elif len(parts) > 2: + subcmd = parts[1].lower() + val = parts[2] + if subcmd == 'ollama': + update_config("ollama_url", val) + print_status(f"Ollama URL updated to: {val}") + self.client = OllamaClient(val) + elif subcmd == 'comfy': + update_config("comfy_url", val) + print_status(f"ComfyUI URL updated to: {val}") + elif subcmd == 'piper_path': + cfg = load_config() + cfg["piper"]["path"] = val + save_config(cfg) + print_status(f"Piper binary path updated to: {val}") + elif subcmd == 'piper_model': + cfg = load_config() + cfg["piper"]["model"] = val + save_config(cfg) + print_status(f"Piper model path updated to: {val}") + else: + print_error("Usage: /config ") + else: + print_error("Usage: /config ") + elif cmd == '/quit': + print_status("Goodbye!") + sys.exit(0) + elif cmd == '/clear': + self.reset_messages() + print_status("History cleared.") + elif cmd == '/models': + self.available_models = self.client.get_available_models() + print_status(f"Available models: {', '.join(self.available_models)}") + elif cmd == '/model': + if len(parts) > 1: + self.current_model = parts[1] + update_config("default_model", self.current_model) + print_status(f"Switched to model: {self.current_model}") + else: + print_status(f"Current model: {self.current_model}") + elif cmd == '/sessions': + sessions = list_sessions() + print_status(f"Recent sessions: {', '.join(sessions[:10])}") + elif cmd == '/save': + name = parts[1] if len(parts) > 1 else None + path = save_session(self.messages, name) + print_status(f"Session saved to {path}") + elif cmd == '/load': + if len(parts) > 1: + messages = load_session(parts[1]) + if messages: + self.messages = messages + print_status(f"Session '{parts[1]}' loaded.") + else: + print_error(f"Session '{parts[1]}' not found.") + else: + print_error("Usage: /load ") + elif cmd == '/mcp': + if len(parts) > 3 and parts[1] == 'connect': + name = parts[2] + command = parts[3] + args = parts[4:] if len(parts) > 4 else None + result = connect_mcp_server(name, command, args) + print_status(result) + # Refresh system prompt with new tools + self.system_prompt = self._build_system_prompt() + self.reset_messages() + else: + print_error("Usage: /mcp connect [args...]") + elif cmd == '/lsp': + if len(parts) > 2 and parts[1] == 'start': + lang = parts[2] + path = parts[3] if len(parts) > 3 else "." + result = start_lsp_server(lang, path) + print_status(result) + else: + print_error("Usage: /lsp start [path]") + elif cmd == '/notify': + if len(parts) > 2 and parts[1] == 'setup': + topic = parts[2] + self.config["ntfy"]["enabled"] = True + self.config["ntfy"]["topic"] = topic + save_config(self.config) + start_notify_thread(self.config, self.process_remote_message) + print_status(f"Notifications enabled for topic: {topic}") + else: + print_error("Usage: /notify setup ") + else: + print_error(f"Unknown command: {cmd}") + except Exception as e: + print_error(f"Command error: {e}") + + return True + + def process_remote_message(self, message: str) -> str: + """Handle messages coming from ntfy.sh""" + temp_messages = self.messages + [{"role": "user", "content": message}] + try: + response = "" + for chunk in self.client.chat(temp_messages, self.current_model, stream=False): + response += chunk + return response + except Exception as e: + return f"Error: {e}" + + def run(self): + console.print(f"[bold cyan]Ollama CLI v{VERSION}[/bold cyan]") + + # Initial model detection + try: + self.available_models = self.client.get_available_models() + if not self.available_models: + print_error("No models found. Make sure Ollama is running.") + else: + if self.current_model not in self.available_models: + self.current_model = self.available_models[0] + print_status(f"Using model: [bold green]{self.current_model}[/bold green]") + except Exception as e: + print_error(f"Could not connect to Ollama: {e}") + + self.reset_messages() + + # Start notification listener if enabled + if self.config.get("ntfy", {}).get("enabled"): + start_notify_thread(self.config, self.process_remote_message) + print_status(f"Notification listener active on topic: {self.config['ntfy']['topic']}") + + while True: + user_input = self.repl.get_input() + + if not user_input: + continue + + if self.handle_command(user_input): + continue + + self.messages.append({"role": "user", "content": user_input}) + self.process_chat_cycle() + + def process_chat_cycle(self): + """The main loop for chat and tool execution.""" + executed_tool_calls = set() + for iteration in range(self.max_tool_iterations): + full_response = "" + try: + print_status(f"Thinking ([dim]{self.current_model}[/dim])...") + with StreamingDisplay() as display: + for chunk in self.client.chat(self.messages, self.current_model): + display.update(chunk) + full_response += chunk + + self.messages.append({"role": "assistant", "content": full_response}) + + # Check for tool calls + tool_calls = self.parse_tool_calls(full_response) + if not tool_calls: + break + + # Execute tool calls + results = [] + for name, params in tool_calls: + # Prevent identical tool call loops across the entire cycle + call_key = f"{name}:{json.dumps(params, sort_keys=True)}" + if call_key in executed_tool_calls: + print_status(f"Skipping duplicate tool call: [dim]{name}[/dim]") + continue + executed_tool_calls.add(call_key) + + result = self.execute_tool(name, params) + # Show result to user + print_status(f"Result: [dim]{result}[/dim]") + results.append(f"Tool Result ({name}):\n{result}") + + if not results: + # If we had tool calls but they were all duplicates, we must stop + break + + # Add all results to conversation + self.messages.append({"role": "user", "content": "\n\n".join(results) + "\n\nTool execution complete. If the task is finished, provide a final response to the user. Do not repeat the tool call."}) + + # If we made it here, loop again to let the model process results + + except Exception as e: + print_error(str(e)) + logger.exception("Chat cycle error") + break + + def parse_tool_calls(self, text: str) -> List[tuple]: + calls = [] + + # 1. Try to find blocks (handles unclosed tags) + tc_blocks = re.findall(r'(.*?)(?:|$)', text, re.DOTALL) + if tc_blocks: + for block in tc_blocks: + if not block.strip(): continue + # Try to find tool_name tag (handles unclosed) + name_match = re.search(r'(.*?)(?:|$)', block, re.DOTALL) + name = name_match.group(1).strip() if name_match else None + + # Try to find parameters tag (handles unclosed) + params_match = re.search(r'(.*?)(?:|$)', block, re.DOTALL) + if name and params_match: + try: + params_str = params_match.group(1).strip() + # Auto-fix unclosed JSON if needed + if params_str.startswith('{') and not params_str.endswith('}'): + params_str += '}' + calls.append((name, json.loads(params_str))) + except: + # Last ditch: search for any JSON-like object in the parameters string + json_match = re.search(r'(\{.*\})', params_str, re.DOTALL) + if json_match: + try: calls.append((name, json.loads(json_match.group(1)))) + except: pass + + # 2. If no blocks, try searching for tool-specific tags + if not calls: + for tool_name in registry.tools.keys(): + # Handle or + attr_pattern = fr'<{tool_name}\s+(.*?)[\s/]*>' + attr_matches = re.findall(attr_pattern, text, re.DOTALL) + for attr_str in attr_matches: + attrs = {} + kv_pairs = re.findall(r'(\w+)\s*=\s*["\'](.*?)["\']', attr_str) + for k, v in kv_pairs: + attrs[k] = v + if attrs: + calls.append((tool_name, attrs)) + + # Handle {json} + block_pattern = fr'<{tool_name}>(.*?)(?:|$)' + block_matches = re.findall(block_pattern, text, re.DOTALL) + for b in block_matches: + json_match = re.search(r'(\{.*\})', b, re.DOTALL) + if json_match: + try: calls.append((tool_name, json.loads(json_match.group(1)))) + except: pass + + # 3. Final fallback: look for tool_name followed by JSON { ... } + if not calls: + for tool_name in registry.tools.keys(): + json_pattern = fr'{tool_name}\s*({{.*?}})' + json_matches = re.findall(json_pattern, text, re.DOTALL) + for json_str in json_matches: + try: + calls.append((tool_name, json.loads(json_str.strip()))) + except: + pass + + return calls + + def execute_tool(self, name: str, params: Dict) -> str: + """Execute a tool (standard or MCP).""" + # Try MCP tools first + if name in MCP_TOOLS: + mcp_tool = MCP_TOOLS[name] + print_status(f"Executing MCP tool [bold blue]{mcp_tool['name']}[/bold blue]...") + return call_mcp_tool(mcp_tool["server"], mcp_tool["name"], params) + + # Try standard tools + tool = registry.get_tool(name) + if tool: + print_status(f"Executing [bold blue]{name}[/bold blue]...") + try: + result = tool.execute(**params) + # Notification for successful file/system ops + if name == "write_file" and "Successfully" in str(result): + send_notification(self.config, "File Created", f"Path: {params.get('path')}") + return result + except Exception as e: + return f"Error executing tool: {e}" + + return f"Unknown tool: {name}" + +def main(): + try: + cli = OllamaCLI() + cli.run() + except KeyboardInterrupt: + print_status("Interrupted by user. Exiting.") + sys.exit(0) + except Exception as e: + print_error(f"Fatal error: {e}") + logger.exception("Fatal error") + sys.exit(1) + +if __name__ == "__main__": + main() diff --git a/build/lib/ollama_cli/tools/__init__.py b/build/lib/ollama_cli/tools/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/build/lib/ollama_cli/tools/base.py b/build/lib/ollama_cli/tools/base.py new file mode 100644 index 0000000..207594d --- /dev/null +++ b/build/lib/ollama_cli/tools/base.py @@ -0,0 +1,55 @@ +from typing import Dict, Any, Callable, List, Optional +import json +import inspect + +class BaseTool: + def __init__(self, name: str, description: str, parameters: Dict[str, Any]): + self.name = name + self.description = description + self.parameters = parameters + + def execute(self, **kwargs) -> Any: + raise NotImplementedError + +class ToolRegistry: + def __init__(self): + self.tools: Dict[str, BaseTool] = {} + + def register(self, tool: BaseTool): + self.tools[tool.name] = tool + + def get_tool(self, name: str) -> Optional[BaseTool]: + return self.tools.get(name) + + def list_tools(self) -> List[Dict[str, Any]]: + return [ + { + "name": t.name, + "description": t.description, + "parameters": t.parameters + } + for t in self.tools.values() + ] + + def generate_system_prompt_snippet(self) -> str: + snippet = "Available tools:\n" + for t in self.tools.values(): + snippet += f"- {t.name}: {t.description}\n" + snippet += f" Parameters: {json.dumps(t.parameters)}\n" + return snippet + +# Singleton registry +registry = ToolRegistry() + +def tool(name: str, description: str, parameters: Dict[str, Any]): + """Decorator to register a function as a tool""" + def decorator(func: Callable): + class DecoratedTool(BaseTool): + def __init__(self): + super().__init__(name, description, parameters) + def execute(self, **kwargs): + return func(**kwargs) + + registry.register(DecoratedTool()) + return func + return decorator diff --git a/build/lib/ollama_cli/tools/code.py b/build/lib/ollama_cli/tools/code.py new file mode 100644 index 0000000..e8a8798 --- /dev/null +++ b/build/lib/ollama_cli/tools/code.py @@ -0,0 +1,96 @@ +import os +import subprocess +from .base import tool + +@tool( + name="code_analyze_file", + description="Analyze code structure and suggest improvements", + parameters={ + "file_path": {"type": "string", "description": "Path to the file to analyze"} + } +) +def code_analyze_file(file_path: str) -> str: + try: + with open(os.path.expanduser(file_path), 'r') as f: + content = f.read() + + lines = content.split('\n') + functions = [l for l in lines if 'def ' in l] + classes = [l for l in lines if 'class ' in l] + + issues = [] + if len(lines) > 500: + issues.append(f"Large file ({len(lines)} lines) - consider splitting") + if len(functions) > 30: + issues.append(f"Many functions ({len(functions)}) - consider refactoring") + + return f"""File: {file_path} +Lines: {len(lines)} | Functions: {len(functions)} | Classes: {len(classes)} + +Issues: +{chr(10).join(f'- {i}' for i in issues) if issues else '- None detected'}""" + except Exception as e: + return f"Error: {str(e)}" + +@tool( + name="generate_readme", + description="Generate a README.md for the current project", + parameters={ + "path": {"type": "string", "description": "Project root path", "default": "."} + } +) +def generate_readme(path: str = ".") -> str: + try: + project_name = os.path.basename(os.path.abspath(path)) + readme_content = f"# {project_name}\n\nGenerated by Ollama CLI." + readme_path = os.path.join(path, "README.md") + with open(readme_path, 'w') as f: + f.write(readme_content) + return f"Generated README.md at {readme_path}" + except Exception as e: + return f"Error: {str(e)}" + +@tool( + name="replace_text", + description="Surgically replace text in a file", + parameters={ + "path": {"type": "string", "description": "Path to the file"}, + "old_text": {"type": "string", "description": "The exact text to find"}, + "new_text": {"type": "string", "description": "The text to replace it with"} + } +) +def replace_text(path: str, old_text: str, new_text: str) -> str: + try: + expanded_path = os.path.expanduser(path) + with open(expanded_path, 'r') as f: + content = f.read() + + if old_text not in content: + return f"Error: The text to replace was not found in {path}" + + new_content = content.replace(old_text, new_text, 1) # Replace first occurrence + with open(expanded_path, 'w') as f: + f.write(new_content) + return f"Successfully updated {path}" + except Exception as e: + return f"Error: {str(e)}" + +@tool( + name="run_linter", + description="Run a linter on a file", + parameters={ + "file_path": {"type": "string", "description": "Path to the file"}, + "linter": {"type": "string", "description": "Linter to use (pylint|eslint|auto)", "default": "auto"} + } +) +def run_linter(file_path: str, linter: str = "auto") -> str: + try: + file_path = os.path.expanduser(file_path) + if linter == "auto": + ext = os.path.splitext(file_path)[1] + linter = "pylint" if ext == ".py" else "eslint" + + result = subprocess.run(f"{linter} {file_path}", shell=True, capture_output=True, text=True) + return result.stdout + result.stderr + except Exception as e: + return f"Error: {str(e)}" diff --git a/build/lib/ollama_cli/tools/execution.py b/build/lib/ollama_cli/tools/execution.py new file mode 100644 index 0000000..d7f65e6 --- /dev/null +++ b/build/lib/ollama_cli/tools/execution.py @@ -0,0 +1,36 @@ +import subprocess +import os +import sys +from .base import tool + +@tool( + name="run_python", + description="Execute a Python script or snippet", + parameters={ + "code": {"type": "string", "description": "Python code to execute"}, + "args": {"type": "string", "description": "Optional arguments", "default": ""} + } +) +def run_python(code: str, args: str = "") -> str: + try: + # Create a temp file + temp_file = ".ollama-cli-exec.py" + with open(temp_file, "w") as f: + f.write(code) + + cmd = f"{sys.executable} {temp_file} {args}" + result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=30) + + output = result.stdout + result.stderr + + # Cleanup + if os.path.exists(temp_file): + os.remove(temp_file) + + if result.returncode == 0: + return f"Success:\n{output}" if output else "Success (no output)" + else: + return f"Error (Exit {result.returncode}):\n{output}" + + except Exception as e: + return f"Execution Error: {str(e)}" \ No newline at end of file diff --git a/build/lib/ollama_cli/tools/filesystem.py b/build/lib/ollama_cli/tools/filesystem.py new file mode 100644 index 0000000..ebb02a9 --- /dev/null +++ b/build/lib/ollama_cli/tools/filesystem.py @@ -0,0 +1,83 @@ +import os +import subprocess +from .base import tool + +@tool( + name="read_file", + description="Read the contents of a file", + parameters={ + "path": {"type": "string", "description": "Path to the file"} + } +) +def read_file(path: str) -> str: + try: + with open(os.path.expanduser(path), 'r') as f: + return f.read() + except Exception as e: + return f"Error reading file: {str(e)}" + +@tool( + name="write_file", + description="Write content to a file", + parameters={ + "path": {"type": "string", "description": "Path to the file"}, + "content": {"type": "string", "description": "Content to write"} + } +) +def write_file(path: str, content: str) -> str: + try: + expanded_path = os.path.expanduser(path) + os.makedirs(os.path.dirname(expanded_path), exist_ok=True) + with open(expanded_path, 'w') as f: + f.write(content) + return f"Successfully written to {path}" + except Exception as e: + return f"Error writing file: {str(e)}" + +@tool( + name="list_directory", + description="List contents of a directory", + parameters={ + "path": {"type": "string", "description": "Path to the directory", "default": "."} + } +) +def list_directory(path: str = ".") -> str: + try: + expanded = os.path.expanduser(path) + files = os.listdir(expanded) + return "\n".join(files) if files else "Directory is empty" + except Exception as e: + return f"Error listing directory: {str(e)}" + +@tool( + name="grep_search", + description="Search for a regular expression pattern in file contents", + parameters={ + "pattern": {"type": "string", "description": "The regex pattern to search for"}, + "path": {"type": "string", "description": "Directory to search in", "default": "."}, + "include": {"type": "string", "description": "Glob pattern for files to include", "default": "*"} + } +) +def grep_search(pattern: str, path: str = ".", include: str = "*") -> str: + try: + cmd = f'grep -rE --include="{include}" "{pattern}" {path}' + result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=20) + return result.stdout if result.stdout else "No matches found." + except Exception as e: + return f"Error: {str(e)}" + +@tool( + name="get_tree", + description="Show the directory structure as a tree", + parameters={ + "path": {"type": "string", "description": "Root path", "default": "."}, + "depth": {"type": "integer", "description": "Max depth", "default": 2} + } +) +def get_tree(path: str = ".", depth: int = 2) -> str: + try: + cmd = f"find {path} -maxdepth {depth} -not -path '*/.*'" + result = subprocess.run(cmd, shell=True, capture_output=True, text=True) + return result.stdout + except Exception as e: + return f"Error: {str(e)}" diff --git a/build/lib/ollama_cli/tools/git.py b/build/lib/ollama_cli/tools/git.py new file mode 100644 index 0000000..6b2a081 --- /dev/null +++ b/build/lib/ollama_cli/tools/git.py @@ -0,0 +1,52 @@ +import subprocess +import requests +from typing import Optional +from .base import tool +from ..core.config import load_config + +@tool( + name="git_operation", + description="Perform git operations (status, diff, commit, push, pull, list)", + parameters={ + "operation": {"type": "string", "description": "Operation to perform (status|diff|commit|push|pull|list)"}, + "message": {"type": "string", "description": "Commit message", "default": "Auto commit"} + } +) +def git_operation(operation: str, message: str = "Auto commit") -> str: + config = load_config() + git_config = config.get("git", {}) + gitea_config = config.get("gitea", {}) + + try: + if operation == "status": + result = subprocess.run("git status", shell=True, capture_output=True, text=True) + elif operation == "diff": + result = subprocess.run("git diff", shell=True, capture_output=True, text=True) + elif operation == "commit": + result = subprocess.run(f'git add -A && git commit -m "{message}"', + shell=True, capture_output=True, text=True) + elif operation == "push": + result = subprocess.run("git push", shell=True, capture_output=True, text=True) + elif operation == "pull": + result = subprocess.run("git pull", shell=True, capture_output=True, text=True) + elif operation == "list": + if gitea_config.get("url") and gitea_config.get("token"): + gitea_url = gitea_config["url"].rstrip("/") + token = gitea_config["token"] + response = requests.get( + f"{gitea_url}/api/v1/user/repos", + headers={"Authorization": f"token {token}"}, + timeout=10 + ) + if response.status_code == 200: + repos = response.json() + return "\n".join([f"- {r['full_name']} ({r['clone_url']})" for r in repos]) + return f"Gitea API error: {response.status_code}" + result = subprocess.run("find . -maxdepth 2 -name '.git' -type d | sed 's|/.git||'", + shell=True, capture_output=True, text=True) + else: + return f"Unknown git operation: {operation}" + + return result.stdout + result.stderr + except Exception as e: + return f"Git error: {str(e)}" diff --git a/build/lib/ollama_cli/tools/knowledge.py b/build/lib/ollama_cli/tools/knowledge.py new file mode 100644 index 0000000..9dc28af --- /dev/null +++ b/build/lib/ollama_cli/tools/knowledge.py @@ -0,0 +1,41 @@ +import json +import os +from .base import tool + +KB_FILE = os.path.expanduser("~/.ollama-cli-kb.json") + +def load_kb(): + if os.path.exists(KB_FILE): + with open(KB_FILE, 'r') as f: + return json.load(f) + return {} + +def save_kb(kb): + with open(KB_FILE, 'w') as f: + json.dump(kb, f, indent=2) + +@tool( + name="kb_add", + description="Add content to the knowledge base", + parameters={ + "key": {"type": "string", "description": "Search key"}, + "content": {"type": "string", "description": "Information content"} + } +) +def kb_add(key: str, content: str) -> str: + kb = load_kb() + kb[key] = content + save_kb(kb) + return f"Added '{key}' to knowledge base" + +@tool( + name="kb_search", + description="Search the knowledge base", + parameters={ + "query": {"type": "string", "description": "Search query"} + } +) +def kb_search(query: str) -> str: + kb = load_kb() + results = [f"{k}: {v[:100]}..." for k, v in kb.items() if query.lower() in k.lower() or query.lower() in v.lower()] + return "\n".join(results) if results else "No matches found" diff --git a/build/lib/ollama_cli/tools/media.py b/build/lib/ollama_cli/tools/media.py new file mode 100644 index 0000000..2611f11 --- /dev/null +++ b/build/lib/ollama_cli/tools/media.py @@ -0,0 +1,183 @@ +import subprocess +import os +import sys +import requests +import json +import random +from .base import tool +from ..core.config import load_config + +@tool( + name="generate_image", + description="Generate an image using the local Stable Diffusion workflow (ComfyUI)", + parameters={ + "prompt": {"type": "string", "description": "The image description"} + } +) +def generate_image(prompt: str) -> str: + """Invokes the ComfyUI API directly with a predefined workflow""" + config = load_config() + comfy_url = config.get("comfy_url", "http://127.0.0.1:8188").rstrip("/") + image_model = config.get("image_model", "dreamshaper_8_pruned.safetensors") + + # Determine dimensions (SDXL prefers 1024x1024) + width, height = (512, 512) + if "xl" in image_model.lower(): + width, height = (1024, 1024) + + try: + # Check if ComfyUI is running + try: + requests.get(comfy_url, timeout=2) + except: + return f"Error: ComfyUI server ({comfy_url}) appears to be down. Please start it first or configure the URL with '/config comfy '." + + # Define the workflow directly + workflow = { + "1": { + "inputs": {"ckpt_name": image_model}, + "class_type": "CheckpointLoaderSimple" + }, + "2": { + "inputs": { + "text": f"masterpiece, best quality, highly detailed, {prompt}", + "clip": ["1", 1] + }, + "class_type": "CLIPTextEncode" + }, + "3": { + "inputs": { + "text": "blurry, low quality, distorted, watermark, nsfw", + "clip": ["1", 1] + }, + "class_type": "CLIPTextEncode" + }, + "4": { + "inputs": {"width": width, "height": height, "batch_size": 1}, + "class_type": "EmptyLatentImage" + }, + "5": { + "inputs": { + "seed": random.randint(1, 1000000000), "steps": 20, "cfg": 7.0, + "sampler_name": "dpmpp_2m", "scheduler": "karras", + "denoise": 1.0, "model": ["1", 0], + "positive": ["2", 0], "negative": ["3", 0], + "latent_image": ["4", 0] + }, + "class_type": "KSampler" + }, + "6": { + "inputs": {"samples": ["5", 0], "vae": ["1", 2]}, + "class_type": "VAEDecode" + }, + "7": { + "inputs": { + "filename_prefix": "ollama_cli_", + "images": ["6", 0] + }, + "class_type": "SaveImage" + } + } + + payload = {"prompt": workflow} + response = requests.post(f"{comfy_url}/prompt", json=payload, timeout=10) + + if response.status_code == 200: + data = response.json() + prompt_id = data.get('prompt_id', 'unknown') + return f"Image generation queued successfully on {comfy_url}.\nPrompt ID: {prompt_id}\nCheck your ComfyUI output folder for 'ollama_cli_' prefixed files." + else: + return f"Error form ComfyUI: {response.status_code} - {response.text}" + + except Exception as e: + return f"Error generating image: {str(e)}" + +@tool( + name="speak_text", + description="Convert text to speech using Piper TTS (high quality)", + parameters={ + "text": {"type": "string", "description": "Text to speak"}, + "output_file": {"type": "string", "description": "Output filename", "default": "output.wav"} + } +) +def speak_text(text: str, output_file: str = "output.wav") -> str: + """Uses the best available TTS engine (Kokoro > Piper > System)""" + config = load_config() + + # 1. Try Kokoro (Ultra-Realistic) + kokoro_python = os.path.expanduser(config.get("kokoro_path", "~/.ollama-cli/kokoro/venv/bin/python")) + if os.path.exists(kokoro_python): + try: + # Inline script to run in Kokoro venv + kokoro_script = f""" +import sys +import soundfile as sf +import os +try: + from kokoro import KPipeline + import torch +except ImportError: + print("Dependencies missing") + sys.exit(1) + +text = sys.argv[1] +output_file = sys.argv[2] + +# Initialize pipeline +pipeline = KPipeline(lang_code='a') +generator = pipeline(text, voice='af_bella', speed=1) + +all_audio = [] +for gs, ps, audio in generator: + if audio is not None: + all_audio.append(audio) + +if all_audio: + import numpy as np + full_audio = np.concatenate(all_audio) + sf.write(output_file, full_audio, 24000) + print("Success") +else: + print("No audio generated") + sys.exit(1) +""" + # Run in venv + subprocess.run([kokoro_python, "-c", kokoro_script, text, output_file], + check=True, capture_output=True) + + if sys.platform == "darwin": + subprocess.run(["afplay", output_file]) + return f"Audio saved to {output_file} and played (using Ultra-Realistic Kokoro TTS)" + except Exception as e: + # Continue to fallback + pass + + # 2. Try Piper (High-Quality) + piper_config = config.get("piper", {}) + piper_bin = os.path.expanduser(piper_config.get("path", "~/.ollama-cli/piper/piper")) + model_path = os.path.expanduser(piper_config.get("model", "~/.ollama-cli/piper/voice.onnx")) + + if os.path.exists(piper_bin) and os.path.exists(model_path): + try: + # Piper reads from stdin and writes to file + cmd = f'echo "{text}" | {piper_bin} --model {model_path} --output_file {output_file}' + subprocess.run(cmd, shell=True, check=True, capture_output=True) + + # Auto-play on macOS + if sys.platform == "darwin": + subprocess.run(["afplay", output_file]) + + return f"Audio saved to {output_file} and played (using high-quality Piper TTS)" + except Exception as e: + pass + + # 3. Fallback to system 'say' command on macOS + if sys.platform == "darwin": + try: + subprocess.run(["say", "-o", output_file, text], check=True) + subprocess.run(["afplay", output_file]) + return f"Audio saved to {output_file} and played (using system 'say' fallback)" + except Exception as e: + return f"Error using 'say': {e}" + + return "No compatible TTS engine found. Please run the installer." diff --git a/build/lib/ollama_cli/tools/memory.py b/build/lib/ollama_cli/tools/memory.py new file mode 100644 index 0000000..5e4a2c2 --- /dev/null +++ b/build/lib/ollama_cli/tools/memory.py @@ -0,0 +1,59 @@ +import json +import os +from .base import tool + +PROJECT_MEMORY_FILE = ".ollama-cli/memory.json" + +def load_project_memory(): + if os.path.exists(PROJECT_MEMORY_FILE): + try: + with open(PROJECT_MEMORY_FILE, 'r') as f: + return json.load(f) + except: + pass + return {} + +def save_project_memory(memory): + os.makedirs(os.path.dirname(PROJECT_MEMORY_FILE), exist_ok=True) + with open(PROJECT_MEMORY_FILE, 'w') as f: + json.dump(memory, f, indent=2) + +@tool( + name="remember_fact", + description="Store a key piece of information about the project", + parameters={ + "fact": {"type": "string", "description": "The fact to remember (e.g., 'API key is in .env', 'Main entry point is src/index.ts')"} + } +) +def remember_fact(fact: str) -> str: + memory = load_project_memory() + facts = memory.get("facts", []) + if fact not in facts: + facts.append(fact) + memory["facts"] = facts + save_project_memory(memory) + return f"Remembered: {fact}" + return "Fact already known." + +@tool( + name="recall_facts", + description="Recall all stored facts about the project", + parameters={} +) +def recall_facts() -> str: + memory = load_project_memory() + facts = memory.get("facts", []) + if not facts: + return "No project facts stored." + return "Project Facts:\n" + "\n".join(f"- {f}" for f in facts) + +@tool( + name="clear_memory", + description="Clear project memory", + parameters={} +) +def clear_memory() -> str: + if os.path.exists(PROJECT_MEMORY_FILE): + os.remove(PROJECT_MEMORY_FILE) + return "Project memory cleared." + return "Memory was already empty." \ No newline at end of file diff --git a/build/lib/ollama_cli/tools/system.py b/build/lib/ollama_cli/tools/system.py new file mode 100644 index 0000000..329fcc9 --- /dev/null +++ b/build/lib/ollama_cli/tools/system.py @@ -0,0 +1,46 @@ +import subprocess +import os +from .base import tool + +@tool( + name="execute_bash", + description="Execute a bash command and return its output", + parameters={ + "command": {"type": "string", "description": "The command to execute"} + } +) +def execute_bash(command: str) -> str: + # Basic safety check + dangerous = ['rm -rf /', 'dd if=', 'mkfs', ':(){:|:&};:'] + if any(d in command for d in dangerous): + return f"⚠️ DANGEROUS COMMAND BLOCKED: {command}" + + try: + result = subprocess.run( + command, + shell=True, + capture_output=True, + text=True, + cwd=os.getcwd(), + timeout=60 + ) + output = result.stdout + result.stderr + return f"Exit code: {result.returncode}\n{output}" if output else f"Exit code: {result.returncode} (no output)" + except subprocess.TimeoutExpired: + return "Error: Command timed out after 60 seconds" + except Exception as e: + return f"Error: {str(e)}" + +@tool( + name="get_env_info", + description="Get information about the current system environment", + parameters={} +) +def get_env_info() -> str: + import sys + import platform + return f"""OS: {platform.system()} {platform.release()} +Python: {sys.version} +Shell: {os.environ.get('SHELL', 'unknown')} +User: {os.environ.get('USER', 'unknown')} +CWD: {os.getcwd()}""" diff --git a/build/lib/ollama_cli/tools/web.py b/build/lib/ollama_cli/tools/web.py new file mode 100644 index 0000000..bef085e --- /dev/null +++ b/build/lib/ollama_cli/tools/web.py @@ -0,0 +1,67 @@ +import requests +import urllib.parse +from .base import tool + +@tool( + name="web_search", + description="Search the web for information", + parameters={ + "query": {"type": "string", "description": "The search query"} + } +) +def web_search(query: str) -> str: + """Enhanced web search with DuckDuckGo""" + try: + encoded = urllib.parse.quote(query) + # Use DuckDuckGo Instant Answer API + api_url = f"https://api.duckduckgo.com/?q={encoded}&format=json" + response = requests.get(api_url, timeout=10) + data = response.json() + + results = [] + if data.get("AbstractText"): + results.append(f"Summary: {data['AbstractText']}") + + for topic in data.get("RelatedTopics", [])[:5]: + if isinstance(topic, dict) and "Text" in topic: + results.append(f"- {topic['Text']}") + + return "\n".join(results) if results else "No direct results found. Try a different query." + except Exception as e: + return f"Search error: {str(e)}" + +@tool( + name="read_url", + description="Read and extract text content from a URL", + parameters={ + "url": {"type": "string", "description": "The URL to read"} + } +) +def read_url(url: str) -> str: + try: + response = requests.get(url, timeout=10) + response.raise_for_status() + + try: + from bs4 import BeautifulSoup + soup = BeautifulSoup(response.text, 'html.parser') + + # Remove scripts and styles + for script in soup(["script", "style"]): + script.decompose() + + text = soup.get_text() + + # Clean up whitespace + lines = (line.strip() for line in text.splitlines()) + chunks = (phrase.strip() for line in lines for phrase in line.split(" ")) + text = '\n'.join(chunk for chunk in chunks if chunk) + + return text[:10000] + ("\n... (truncated)" if len(text) > 10000 else "") + + except ImportError: + # Fallback if bs4 is missing (though we checked) + return response.text[:10000] + + except Exception as e: + return f"Error reading URL: {str(e)}" diff --git a/build/lib/ollama_cli/ui/__init__.py b/build/lib/ollama_cli/ui/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/build/lib/ollama_cli/ui/formatter.py b/build/lib/ollama_cli/ui/formatter.py new file mode 100644 index 0000000..b9cd1f8 --- /dev/null +++ b/build/lib/ollama_cli/ui/formatter.py @@ -0,0 +1,40 @@ +from rich.console import Console +from rich.markdown import Markdown +from rich.panel import Panel +from rich.live import Live +from rich.text import Text +from rich.syntax import Syntax +from typing import Optional + +console = Console() + +def print_markdown(text: str): + console.print(Markdown(text)) + +def print_panel(text: str, title: Optional[str] = None, style: str = "blue"): + console.print(Panel(text, title=title, border_style=style)) + +def print_error(text: str): + console.print(f"[bold red]Error:[/bold red] {text}") + +def print_status(text: str): + console.print(f"[bold blue]>>>[/bold blue] {text}") + +class StreamingDisplay: + def __init__(self, title: Optional[str] = None): + self.text = "" + self.live = Live(Text(self.text), refresh_per_second=10) + self.title = title + + def __enter__(self): + self.live.start() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.live.stop() + + def update(self, chunk: str): + self.text += chunk + # Try to parse markdown if it looks complete-ish, otherwise just show text + # For performance, we might just show raw text during streaming + self.live.update(Markdown(self.text) if "```" in self.text else Text(self.text)) diff --git a/build/lib/ollama_cli/ui/repl.py b/build/lib/ollama_cli/ui/repl.py new file mode 100644 index 0000000..9534678 --- /dev/null +++ b/build/lib/ollama_cli/ui/repl.py @@ -0,0 +1,25 @@ +from prompt_toolkit import PromptSession +from prompt_toolkit.history import FileHistory +from prompt_toolkit.styles import Style +from prompt_toolkit.formatted_text import HTML +import os + +class REPL: + def __init__(self): + history_file = os.path.expanduser("~/.ollama-cli-history") + self.session = PromptSession(history=FileHistory(history_file)) + self.style = Style.from_dict({ + 'prompt': 'ansicyan bold', + 'command': 'ansigreen', + }) + + def get_input(self) -> str: + try: + return self.session.prompt( + HTML('> '), + style=self.style + ).strip() + except KeyboardInterrupt: + return "" + except EOFError: + return "/quit" diff --git a/install.sh b/install.sh index 3328b28..41d10ff 100755 --- a/install.sh +++ b/install.sh @@ -82,18 +82,51 @@ if [[ $REPLY =~ ^[Yy]$ ]]; then # 5. Download Image Generation Model echo "" - echo "[*] Downloading Image Generation Model (DreamShaper 8)..." + echo "-------------------------------------------" + echo "Select Image Generation Model:" + echo "1) DreamShaper 8 (Good quality, fast, ~2GB)" + echo "2) Juggernaut XL (Photo-realistic, high detail, ~6GB)" + read -p "Choose option (1 or 2): " MODEL_CHOICE + echo "" + MODEL_DIR="$INSTALL_DIR/models/checkpoints" mkdir -p "$MODEL_DIR" - MODEL_PATH="$MODEL_DIR/dreamshaper_8_pruned.safetensors" + + if [[ "$MODEL_CHOICE" == "2" ]]; then + MODEL_NAME="juggernaut_xl.safetensors" + MODEL_URL="https://huggingface.co/RunDiffusion/Juggernaut-XL-v9/resolve/main/Juggernaut-XL_v9_RunDiffusionPhoto_v2.safetensors" + echo "[*] Downloading Juggernaut XL..." + else + MODEL_NAME="dreamshaper_8_pruned.safetensors" + MODEL_URL="https://huggingface.co/Lykon/DreamShaper/resolve/main/DreamShaper_8_pruned.safetensors" + echo "[*] Downloading DreamShaper 8..." + fi + + MODEL_PATH="$MODEL_DIR/$MODEL_NAME" if [ ! -f "$MODEL_PATH" ]; then - echo " This may take a few minutes (approx. 2GB)..." - curl -L "https://huggingface.co/Lykon/DreamShaper/resolve/main/DreamShaper_8_pruned.safetensors" -o "$MODEL_PATH" + echo " This may take a few minutes..." + curl -L "$MODEL_URL" -o "$MODEL_PATH" echo "✅ Model downloaded." else echo "✅ Model already exists." fi + # Update config with the chosen model name + python3 -c " +import json +import os +config_path = os.path.expanduser('~/.ollama-cli-config.json') +if os.path.exists(config_path): + with open(config_path, 'r') as f: + config = json.load(f) +else: + config = {} +config['image_model'] = '$MODEL_NAME' +config['comfy_output_path'] = os.path.expanduser('$INSTALL_DIR/output') +with open(config_path, 'w') as f: + json.dump(config, f, indent=2) +" + echo "" echo "[!] To use Image Generation, you must start ComfyUI:" echo " cd $INSTALL_DIR" diff --git a/output.wav b/output.wav new file mode 100644 index 0000000000000000000000000000000000000000..cd1295f40a1114da20640219d7e78ae0342fc549 GIT binary patch literal 145964 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
    ?+x>%Qn*Y5(RlvT$ zfDl#a?cix3>hV<2I`4RwAiI0KZM1T9EQrh+OW`5=$ON8kbTclqpina$i>$@=(-zWE z<`eEG@n+dnhm$UMo!7f`y7V|_*|CM5EGz9SWe`+qUZOjt2gmEFMwdllweta8`TP>rCH_n;HAlyc09Nj(RJ6a`oQyyRi?v->(0? zr+ifV9Mx>YVe(5(vfVll|DX|3IYTx^pBiyt%)C)|hfj|Z1&wyj5DK_o=~QH_t(90p zyfA&!YmBRHYtcN`KC#^12qu_D`Xa&8Lz}~_;hy2bkb;020k;Dx{YH9?b@7mNGVY=M zkms-fT54NIlv$^l-ay%DFe zYv^n`L^Dt-A%&Tt(zi`(DsH^lc&~v`_p2(gw5`A{|4z=y98)$U*ZU8r=za0G;BW{hje);jp-pj~H@;ur` zx|}*4r1O0y-eI@J;edU%)K0ogED)^YOL)=T3v5sNCis^1wP~U;LZ?-o?`3yU+MhIk zZ&*<`qxM}Dy<%PorC@9Ur)W_rqikX6g5vT&mOS_Tk@-P6do!n}#{b&%J^iEeYp>^L z|HJNVdb};AzViz;(*CWxyU%^kJ8mkkJz>SeMU%RwnO^t+M_�iDP{x3mK@$fFBC|m<*^MDp{>MGWI7}7a5Onfd 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 VglHqTC7zRaiPzvUQV6kS{vSA{Sz!PG literal 0 HcmV?d00001 diff --git a/src/ollama_cli.egg-info/SOURCES.txt b/src/ollama_cli.egg-info/SOURCES.txt index c19923b..a434739 100644 --- a/src/ollama_cli.egg-info/SOURCES.txt +++ b/src/ollama_cli.egg-info/SOURCES.txt @@ -20,9 +20,12 @@ src/ollama_cli/integrations/notify.py src/ollama_cli/tools/__init__.py src/ollama_cli/tools/base.py src/ollama_cli/tools/code.py +src/ollama_cli/tools/execution.py src/ollama_cli/tools/filesystem.py src/ollama_cli/tools/git.py src/ollama_cli/tools/knowledge.py +src/ollama_cli/tools/media.py +src/ollama_cli/tools/memory.py src/ollama_cli/tools/system.py src/ollama_cli/tools/web.py src/ollama_cli/ui/__init__.py diff --git a/src/ollama_cli/__pycache__/main.cpython-312.pyc b/src/ollama_cli/__pycache__/main.cpython-312.pyc index 5aa5b45ed4707b63e8b86992cb867bf2d416ffc5..f22a8d00c1b7a86daa8085f6f8f9e9a9ba181a18 100644 GIT binary patch delta 10015 zcma(%YfxL)nfK}~A@P)ucnGcx!pMM(jWGse3ZlWfrun8YNRY&>ClZsK+;K~% zI!sEQlkh$2P^k4(dv|+lO9yr8bjQK22F(sTb!L?DP&3|1%EK@;2}+x zwiDFx6WtVZe!}exxjfYLC{QAKPL`o)ugf!H*VH7@SZ0EOaX~)jK)Hr7`lxdX^m4O| zlb)eiW{7c3fxb@Ya+7W!Qi*c;?3!J6sy&4ODdY5T(+pcljZJRneAEcgAxx^xOL@FL z!7@;nqb5CWhU35wL-2C4Gjw9vt)FS@N1g%Lz(^8Ll(Gr}unOobBzCy&7r$zV*L>jteO)WK=@Gl&J%~6WgvZPF*6{v*?1*^;=)3GA74+^Aftikb8 zx(t&S28b~WzgSwhC56w{4XEW9^)Nq|o+}wE;ZN%F^I;)mivm(8ZS*!iOzQbPnYsMi z>BeJ7Pz9!TZn}7tc=6Vw#f#IPcUQ6?m#lAld$T> zYSE(31Q#VY902jY2(8kVHKgQjeG%s^gD#0npCS-XC8PyD{ zq+T!rHc4h6ExCHWz@&9bw>+iv0jx-PQ}Ke*lcLflx)mdjC$-L^+rT*u_;5Xl=7v6m zLSG>jK`O_2%X(N=ylTr8j3nQ z=$>Q-4!Pj2HN*Ls@dg+|5V%q~X~!_YE2(MLrwFaZF%PCAzw~okNVn$DH?7QbR!CaC)5X z8NVoz1!T9&<73z)+cV5ylGijdn3Ruoo@Cug#7>5GIU7QvP2d5Bb%HIfapwpa#=))e zTtYI^eaJgLJmcVePTwS#R! zCB}w9Nf3%KQlw6}Cct_Gq!PplY7Bxr<|IO(23;Pw%7>mJ9EW*#ij|g4t|5lP zXd1sVVe41 zkz#6Lp}@5Gi`HW%Qm-VxSeAe)_zwFAn1@8}?PwiRoyhn#k~F+J9oW_sO#}y?1P>=s zshAx5JtjlWdJ1;~U%9}sSVM&p#!M`TOD1#BHOzd^ZeOd%{ozj4i9)Z_#W z`v@hd4WISjDFDn77fsL`SZT_9PM60%!SgVGfiT?}JV2)HpBbKd1++xUOXE6rumU64h2 zC!3BM8)`E6|H?D-nfc|a9JU$(4)V?U1+~(l;gMLn5GEiTa}YykVhV0@aA=&4NkPMy ziW3e2jztF?+X)Tt%`eG(4mvTC;gAj}r10O(Z&LQc2X|D#|7U(}=%hrweKjLHJo4fT zaf!t&3#*^Bkwlj1s_u$z*0r2j9`BbDnfmxyDUofMRY$Wd4{~zDlYGNm)=T>z0aIrzy(*`2IT!%#^F2CMZoP`HXV--V_XK~Hr~tI{-DVA z^7yQDb^Fd)>FnWa+PR)3Q{@9w$sA}{j;RkW4lSOJSdT56I^z9^x<7svlkdBiS1poP z*Cfc#ESaj-k+&^&EgpKaL^CB!q#AQ0MOJ-M97> zEJ{Of!D@S_ut!e3t8dlA>nFAr1H68^zZly0igCNY8u~x0M(rI-8fxvh-D`#Z`xXk? zpG#U0=!10px|?r;kGs3JL;GQA6SV(U(g^J@sBF~kLZ}h4rLIT5hghmdU-uL(T6jfs z&_|RypoG``a`bV(HXYhu%GEt((qE>h_gJLAw8(qPZUu{rNV0{0h1#iefeki=uqDs) zA5*2|)JqmUIm#O>R`PeZ_F8zEq91VB0e;z96+*j?^`oW*5gJ&OO|pZ~#FRY;Pj$7Q zI4;D8VFcfX8ni)$kbpx7639Yng1tq~_GJdMOxL^!Y z770xTrE0>b1YY=|B`~hy7s^YjP-w%Cn99}?K^csqFu36v%YchE-%^u<##M0Nwn*gx z8DG6kUy)i02}lESKg7y~L-4fbX7bh#1{AN2h&`wv2_gXiIap#A zOpx!*OKc&&&appWTH0g7NydFqw#wXm2kHPhDT$6Wl=O`CP4ZA!p zZj^y^VuJNfxgcM1BAC++Nd!0H^+5R_ZqJAUehFZcb@D`IetmV{pV^Aa`9m|1a8L}( zdRbd8+%qJMkN<6jB_{~FC4z!*+hQ;Bmde~R*%;^b#N_nk_yiY|LTVG!3YtS!24=D& zh|a|ytF#p8aKww{B*tls;6TX^$Fr5CT^9f~ra@E!Fs2lK5D;!5F~x+Fb&hi}6;6*} z7FZwRjUp3=?nvlDl6+WHLt?+d|9hpSdJ8NJ?o((k5T6!VUS=Yyf(Lm;v%Zz$x~1Z} zXx^U3M1?x{Qu}IQ(WTC~#KxCZ-3X;+9VV}IgxhAz9&Ra{%bok$8~X3(-^gD$v`8=8 zjz!8lBE_BIBdf;ZNXhOc<8FK&9=)^cPFdt+PsDm=+4StHiCWcfUC~!8=_}@sEbD6@ znk_44`;yr{?_21Jnj6E1KGjom`xbH+RJRoo%l>73^Qyk!>hUYb=LYyERaqHEq{L_W zgsJ#f`n z1!QANRFqP1nAT6BVg0 zYeAWG@Jp3ol2sE(U#Dz~j7Z2$!_u{{%7fX0^lT(umlMbV<_?gaoGWEOkLOAqPy^=* zfpdxGE1abkX60CNPQ}Q)P9LWX{Bc={T*&?P|HpyZpdH*>(18ig)4eJQ8U*zX*m=1D z@bCa+pg|+73_Pu0si{#|ZDpG(ST7?kk0JHCh!QxL#I>jlsgO^U;1hOVLe5t$pM*z3 zU+NK$5{c<0JW}BR>?Oj|%Lefg{f zEKq9mhZ0EW=g^Hv7zRk}5m-$AQmDs3(1o*yf?y^>d+ZXSeik9T3J*&!3jU+%IcFN@@VN92o8o z+HC1D3CqNkaJ12ITErA>C%RiYI%0Cp$GRpUb;Tvbn0$owPEN3}a*3FV@zC6~%Qwni zgirQw5lrs(I%$qILLC}DkV_zkMyI*WB;3Wni?Q&56jS?#9Qe0jE+)tCm=}CNIY6gxqJcy{}s0V;^ zQ-JG&9(gWCPQ`Rs4O9lg)QL}#oE(g4u~czf_!5Uzcnr965ST(l@|@hz5suBj0YCWj zg^EpUN4p&?27;(9*JrNHL~JJ`r_L<*o?Yo3T8WR6e_4*$XPC^ zy*;+l^vqJzGm%rLmz#Q43(Mw9mI|w8kFJ``*R_!F(r>hV|LBdQ3;t;3frz#BzNsy4 z22&o{iA=)-vlV6XE9UAYb2Stx9uqk_YgqkoOUYdO+}?R_q_`=peNb$e$ z;oy&4KX65hkA$_WA*%8bp~$L-`$J(-_+SDnvhB#7@@R1vK&kRa1Zl8dlg*N|J620d zXXUfb@2KKZfc=NDP*l&gV2D`j?wj_+b-*m1O_W;adgqSKKYzRacEw_MggP45J)pL{ zu6a!}zwdVGPb5E6zpIV{G^AV2D|0{swsc2#pN?$nxo|4~VG@o2*J{fuDRJ6G(TH775pT2MD`QvI~`D<P?9XvMX)u3eD*T*7*Qg)wYNm=oO$=Z%O+1=eF z>eop+_A2kz%c1|FOwy@VeyEjnW-5hZ+A9Qc%dqp$B)N~z+C`I{{M4>zLp<6+xR5__;c5`aq7MNCA5Wq3kdLE zSQNyq&f0oXP@)oGxKt364I=)y7y!pD!G04p-#`tz_(1@L#D`%~tYG0bL$F93`yOg) zP=hbv6zZV{l^?mULvxXMBvHw&aebbA``plD0$z`HlkyHSZc_9~(>vco7;h8@7&j?Wpp@cT_H|EYmQng=;l z?o(sq=ocuSYJ8#YFF~we7t)IT05$7f{kf_UYRr>vripz5qcFV$SIFOQ{N&)rMA65D z6ZUFc>giys#L|wEq(B2K`;WW%g&@E&Al5Qfeg z68Q&hEd}~iLFybdsw%sVR7X^;+x#((-KMc*1-YrwHjyP^%l4bjHo#^~Gx9f%M^|o=6-1O?u$=JlT#8G;-q*y!i|L@5pF!j zNvFcxiFhOupB|5$gq{!$NkL;f!5Jkuom>4_C>rimW=Ris<_Z2ft~<<43gNMFG0cTx zJa;M_nLIHSshc$!_dj`%8w4I=&^Sxia$n)k@-8`BL1pm_fO(Xn4Mk5;3we(U*WNaj zP}Bf^-`tU>hcylSAD+`;jpb5}PuB2SWn<-19?UBrue(gCJqk(Ytx5cLz=^p%Mk|G8 z2$f!;^2i9^?{?tVtoj{*oMmCivQW^C3i@6dP1taO){g(sVkboSpIVbOhb)gbO}KE# z?!|EcG6aSYbP)vYk`ec1qQ0wbKI9 ziY4^2UU18Lu$3*1lOw~G#=n4g_nVqD6*+jxm&WIFu}X5WY7CDfda?}23fR@Fs5`H! zTU)9eU^p$5FH=@WV9b#NO=zK4s`LyhMSr@l{csAHI>n8hq4;Y=w;@8 zV2Osb{A|nD5S#Nm+X2ev|BK@%I8sxZHWwG~XtF`dkXmvVNq|$W_;|f{E*p`xviX~y zo#SnFk8d+)B_L}arz6KxfuvwsVkHua6P6r`dfAeu*hY{jG=w1XEN3kg?cj82O!B#h6G7WYQ~~`(m)Ev zs1!aUCH11Jr6_a~EM_t!ox;nN6{bNlq{1R$ia)CC)E$D3Xk&1GRTFoNG6XjBZHp(r ze>O!sz09KFkslQlFPoRlE8~B(?pS-H`kH3$RiOB_XP^|VJE{l_asaQFbmc+wLABD{ zXe@@oO>=<2j!uBTYi|em`!*ZE56uMiLw8pTG#}Z>;G?oG6EwH{WN@pkTL;Z;iVSXB zECiMs2KH!fS6c>xTFvbsJFsWX?k}aNZro7YU>*gHd|K5ir}4|Rm4)MES6Tp^@(Cp3 zgyZ;fZ54HOZKD=5+QVe#2%c!DtsyoMq6BmU@HvG7(tjR6Qg^iX@R9xl0|Lpe!ZEzw zP#hrA)Z?onz3l{e2{=vwTpLi*a55~7j>q7BAWUF=qhCw(5&Cejv6B7(t-aW|LKQp$ z4_QXiR_%qT!|!!CNf9jRL5tLg9da4PQwiFDBo?TZkH?GtA{=Qda2nE6=_1vH5*mCk zP)JXhm5Tb4fE_RS3-M&V9;cdg8b;PYvUB29OCBfFGBXRMK0%*%Gsu8=hWyfrh(h?S zKq20K(5WMdOZx_me;eTZa;7jRSeX@R$e7GYlCRkwG*XAq3$UMr4vGz$C;|=a1v*)u zW|=U^+6gEHWjxnQ|APw{U2ez1}f@%^A{ zPKj$RFbG9O;Uqy+e4b=j)#q~@pWG2=JqIF@P&D-9zW%yNAwCBAVRQ_%9aXgbJ79=B zm}egS%~iT0-7Ra`C~LXnYJG@G4TTH+o1W5z{VCdGF8tK$S(&>i;qy&cf9S3eVdMw<$SVERE z;#=+R;*F}Kf3iOJMF50+sn-+)QV77;kLs|!qb^9Q$eR^CL3$l^nR@Ej>*h8Xe4y{z z2F;B+wx`8#qftltK|^l~dsE}JoxdNc{vfY%R{(Xy5dEopa2=wwETF8drS>rQ0i0UU9FK{9g$ zfg@(Fqu_`o3B5#eiSqHZ%!2Q0Wc}m55B`P{`m_4;lWLpeZOp*zaL&`=1+hjQ#iHc( zu%oVKkFPzDc|>%Qb;TjV;9z2UJ!gg>sQAiLWVWm?G#&}_@PgqKPb3gH6Ke8w;e=+; z+-JaCuXm1_5aY2V8;OT_QSn|u$%Bxl07Ff^--CWq2c-rW#F9FA`HYzob_Qv4~b%%e?Pd_niA&9PjwRI~Ej zD*tZxFZwU_-?23>=3+yIW{QzkV&rlmC&qB{)&8EF*Ik*(W?F&6AnlV2^ed_YQ z?S9?8`t;q(-5Zs=?^HJ5akVUJH?7WP(~@b0U+r5t{)yF}G7u&y3$i*F4x9e7)JFfCqk6vrI7P@xiV^9BG>(d+7r@u~V$of}eF=)E3Y3}Nv*EWYmA%G+mf!w;2tTZ(l2@B-N1cm@2PI#SwMgo-L%aI-dweT0`0h~h*Xgyn%ayZz) z>exdB%>zHh?xRx0+AzgBF4|Lwv{xJtl{Ph?!JiMfyc}DY>Y+@mbM;6HL345NO4X%f z4@o!WS*twobIRNg?0rT>oG^p`e9+T11T(@g0U5UyULnI*2`UX2sK~hLj8MkFc#6_V zCK?)#2|t4wKW;s=>fV#ojgIp1vC&b1Fig^MQFskH!VmC+L;Y}q?T1UW3ft+mk;6Zt z@SeehbBZ8v1Q15TTLiq0HwKH1H=qmFN1`1+9K6TfLxUS=@E+QK4;{L1EST4Q$s{yX S^%qdtspFPsmh%ET>+g?D zkP(DmJotA?r)2T=uxZ@Lp*$*}B9dn*R6-&;hsrY%DX20l?nqVmO{Z*zR__)AE6K*Re*EXZJNH@ZG?ls)%A}c?NVb)o#Tw;_qIH(NbGzu)Ym7-0axx&CpF;G>y$*TnqjD mkCbXu*_db3GLLKOynmsRdR00Z$l&rxLj=u5J`K(%EB*qI!LNt_ delta 396 zcmdlWbWA|~G%qg~0}$MtG&}Pj3j@Pr5C;apGC~^zDO_`yQ@B%j=5R!@ zrtr2fM6spt0ZH~0ejv$F$*3tX@lej>!;I#P9Fu=DdT*{^`ozd~i=`m3Bx7~2Qm)K)nvZKoSj;Ei?u8LNl+4Sb(;co`Kx$uTpk Mf97IglmHtB0LrOc8UO$Q diff --git a/src/ollama_cli/core/config.py b/src/ollama_cli/core/config.py index 8be3ff5..00ee674 100644 --- a/src/ollama_cli/core/config.py +++ b/src/ollama_cli/core/config.py @@ -18,6 +18,12 @@ DEFAULT_CONFIG = { "mcp_servers": {}, "ollama_url": "http://localhost:11434", "comfy_url": "http://localhost:9000", + "comfy_output_path": "~/ComfyUI/output", + "image_model": "dreamshaper_8_pruned.safetensors", + "piper": { + "path": "~/.ollama-cli/piper/piper", + "model": "~/.ollama-cli/piper/voice.onnx" + }, "ntfy": { "enabled": False, "topic": "", diff --git a/src/ollama_cli/main.py b/src/ollama_cli/main.py index c5914e7..64142cb 100644 --- a/src/ollama_cli/main.py +++ b/src/ollama_cli/main.py @@ -60,13 +60,18 @@ class OllamaCLI: base_prompt = """You are a highly capable AI assistant powered by Ollama. You have access to a variety of tools to help you complete tasks. -When you need to use a tool, output a tool call in the following format: +CRITICAL RULES: +1. When you need to use a tool, you MUST use the EXACT format below. +2. Output ONLY the tool call. DO NOT explain what you are doing. +3. DO NOT repeat a tool call if you have already received a result for it. +4. If you have the answer, just give it. Do not use tools unless necessary. + -tool_name +name_of_tool {"param": "value"} -You can call multiple tools in one response. Be precise and helpful.""" +You can call multiple tools in one response by repeating the block above.""" tool_desc = registry.generate_system_prompt_snippet() @@ -110,7 +115,7 @@ You can call multiple tools in one response. Be precise and helpful.""" [blue]Filesystem:[/blue] read_file, write_file, list_directory, grep_search, get_tree [blue]Code & Python:[/blue] run_python, replace_text, code_analyze_file, run_linter [blue]Web:[/blue] web_search, read_url - [blue]Media:[/blue] generate_image, speak_text + [blue]Media:[/blue] generate_image, speak_text, get_comfy_status [blue]Knowledge:[/blue] kb_add, kb_search, remember_fact, recall_facts, clear_memory [bold]Integrations:[/bold] @@ -119,12 +124,28 @@ You can call multiple tools in one response. Be precise and helpful.""" /notify setup - Setup ntfy.sh remote control [bold]Configuration:[/bold] - /config ollama - Set Ollama server URL (default: http://localhost:11434) - /config comfy - Set ComfyUI server URL (default: http://127.0.0.1:8188) + /config ollama - Set Ollama server URL + /config comfy - Set ComfyUI server URL + /config comfy_output - Set ComfyUI output folder path + /config piper_path - Set path to Piper binary + /config piper_model - Set path to Piper voice model """ console.print(help_text) elif cmd == '/config': - if len(parts) > 2: + if len(parts) == 1: + cfg = load_config() + config_display = f""" +[bold cyan]Current Configuration:[/bold cyan] + [blue]Ollama URL:[/blue] {cfg.get('ollama_url')} + [blue]ComfyUI URL:[/blue] {cfg.get('comfy_url')} + [blue]Comfy Output:[/blue] {cfg.get('comfy_output_path')} + [blue]Image Model:[/blue] {cfg.get('image_model')} + [blue]Piper Path:[/blue] {cfg.get('piper', {}).get('path')} + [blue]Piper Model:[/blue] {cfg.get('piper', {}).get('model')} + [blue]Default Model:[/blue] {cfg.get('default_model')} +""" + console.print(config_display) + elif len(parts) > 2: subcmd = parts[1].lower() val = parts[2] if subcmd == 'ollama': @@ -134,10 +155,23 @@ You can call multiple tools in one response. Be precise and helpful.""" elif subcmd == 'comfy': update_config("comfy_url", val) print_status(f"ComfyUI URL updated to: {val}") + elif subcmd == 'comfy_output': + update_config("comfy_output_path", val) + print_status(f"ComfyUI output path updated to: {val}") + elif subcmd == 'piper_path': + cfg = load_config() + cfg["piper"]["path"] = val + save_config(cfg) + print_status(f"Piper binary path updated to: {val}") + elif subcmd == 'piper_model': + cfg = load_config() + cfg["piper"]["model"] = val + save_config(cfg) + print_status(f"Piper model path updated to: {val}") else: - print_error("Usage: /config ") + print_error("Usage: /config ") else: - print_error("Usage: /config ") + print_error("Usage: /config ") elif cmd == '/quit': print_status("Goodbye!") sys.exit(0) @@ -255,6 +289,7 @@ You can call multiple tools in one response. Be precise and helpful.""" def process_chat_cycle(self): """The main loop for chat and tool execution.""" + executed_tool_calls = set() for iteration in range(self.max_tool_iterations): full_response = "" try: @@ -274,11 +309,24 @@ You can call multiple tools in one response. Be precise and helpful.""" # Execute tool calls results = [] for name, params in tool_calls: + # Prevent identical tool call loops across the entire cycle + call_key = f"{name}:{json.dumps(params, sort_keys=True)}" + if call_key in executed_tool_calls: + print_status(f"Skipping duplicate tool call: [dim]{name}[/dim]") + continue + executed_tool_calls.add(call_key) + result = self.execute_tool(name, params) + # Show result to user + print_status(f"Result: [dim]{result}[/dim]") results.append(f"Tool Result ({name}):\n{result}") + if not results: + # If we had tool calls but they were all duplicates, we must stop + break + # Add all results to conversation - self.messages.append({"role": "user", "content": "\n\n".join(results)}) + self.messages.append({"role": "user", "content": "\n\n".join(results) + "\n\nTool execution complete. If the task is finished, provide a final response to the user. Do not repeat the tool call."}) # If we made it here, loop again to let the model process results @@ -288,17 +336,67 @@ You can call multiple tools in one response. Be precise and helpful.""" break def parse_tool_calls(self, text: str) -> List[tuple]: - pattern = r'\s*(.*?)\s*(.*?)\s*' - matches = re.findall(pattern, text, re.DOTALL) - calls = [] - for name, params_str in matches: - name = name.strip() - try: - params = json.loads(params_str.strip()) - calls.append((name, params)) - except Exception as e: - print_error(f"Failed to parse parameters for {name}: {e}") + + # 1. Try to find blocks (handles unclosed tags) + tc_blocks = re.findall(r'(.*?)(?:|$)', text, re.DOTALL) + if tc_blocks: + for block in tc_blocks: + if not block.strip(): continue + # Try to find tool_name tag (handles unclosed) + name_match = re.search(r'(.*?)(?:|$)', block, re.DOTALL) + name = name_match.group(1).strip() if name_match else None + + # Try to find parameters tag (handles unclosed) + params_match = re.search(r'(.*?)(?:|$)', block, re.DOTALL) + if name and params_match: + try: + params_str = params_match.group(1).strip() + # Auto-fix unclosed JSON if needed + if params_str.startswith('{') and not params_str.endswith('}'): + params_str += '}' + calls.append((name, json.loads(params_str))) + except: + # Last ditch: search for any JSON-like object in the parameters string + json_match = re.search(r'(\{.*\})', params_str, re.DOTALL) + if json_match: + try: calls.append((name, json.loads(json_match.group(1)))) + except: pass + + # 2. If no blocks, try searching for tool-specific tags + if not calls: + for tool_name in registry.tools.keys(): + # Handle or + attr_pattern = fr'<{tool_name}\s+(.*?)[\s/]*>' + attr_matches = re.findall(attr_pattern, text, re.DOTALL) + for attr_str in attr_matches: + attrs = {} + kv_pairs = re.findall(r'(\w+)\s*=\s*["\'](.*?)["\']', attr_str) + for k, v in kv_pairs: + attrs[k] = v + if attrs: + calls.append((tool_name, attrs)) + + # Handle {json} + block_pattern = fr'<{tool_name}>(.*?)(?:|$)' + block_matches = re.findall(block_pattern, text, re.DOTALL) + for b in block_matches: + json_match = re.search(r'(\{.*\})', b, re.DOTALL) + if json_match: + try: calls.append((tool_name, json.loads(json_match.group(1)))) + except: pass + + # 3. Final fallback: look for tool_name followed by JSON { ... } + if not calls: + for tool_name in registry.tools.keys(): + json_pattern = fr'{tool_name}\s*({{.*?}})' + json_matches = re.findall(json_pattern, text, re.DOTALL) + for json_str in json_matches: + try: + calls.append((tool_name, json.loads(json_str.strip()))) + except: + pass + return calls def execute_tool(self, name: str, params: Dict) -> str: diff --git a/src/ollama_cli/tools/__pycache__/code.cpython-312.pyc b/src/ollama_cli/tools/__pycache__/code.cpython-312.pyc index 8312266efb05ff586ad33b9a45fcd19fa378f2e2..1e1a926a3b796b51cef37f5f308baf213b4b223e 100644 GIT binary patch delta 1280 zcmYjPO=ufO6rS1D?yP=Re^zA4j^dRpnJBfB#!cELDNQUVIJDGBBU5nyg3zog1tXc! zs5Qa}y~}*>?f2gM z-psuHGtwS+K9OY`pwD-=)^oaCa|XC5tQ{`HXstd`wQg`2;Q(efxWV7GZSpJ^0PIpN zc!;iHy6mbYBjJNLEStR`_!x0O<#7lfqSgLxHNqoU!QKrXhc~!=Ix#F#g-BmT#G^Na zS1OWa8OFyRa`3+Wu!E?J3#qhvRa2L=m2k2|zR0Ch3k%CiKcKLHk~5w1KVDjo2Pv$=qr@$+cGSJo=zOi5+<}Veu0nz zIj!{Xyh>-Wcj@I+QOOrHB~vKnv69Oxg#B3LI4eAmETz+Gv6v~b@s<{GN>i~y0yGx4 zk|CDICPfZ12r>v!Xrul>$q7b{GKf-$OTFyst0A^!b!jn`$11+Mn9{N&#!|;9wC!x- z=|Wyp^BNhaOi`ks^dI79iEo>5lEt4=xDGl#S%^Fj4E=(BLeBz6T7e^XPHY8UUzMNB zp0$bkz?K~AQ~|V(-0^Jt2XAL;86&$ov&&PaBLau-$G2Mc$o7G$$I-|B=D@|Kr7zn7x%sT7|3kZ$|y*Ag*s&y$e(U)|LE%{9XCs5cSh8^A&RhAbrfsNqG- z4+@D0Y8>kNG3&I*H;&5FkZ+uU)c+j{)1qxE)?Js()u G4g3%OPc%RP delta 194 zcmcbm^H_=RG%qg~0}u#Jn4Niyb0VJv(=~>P8s&^K6Bp>3N~8$QVNQ{p!?cELH8V&R z14D{z3R@~miWCrYr?RB6rAW81u7*f4M6p*gYRYX6U|i2VIZ9B4M<5`vBtxMjU!f!; zb#jHEJ)_m;9fFmNjFyv)geNmvO+F^9%4Gr6TLdCZCw~>b%xei`X>t@vfkd4rUlmcF d{8vPh+Z)Jb1mfcB43iZ_Cr#!P^I-7;@&R-4F2eu- diff --git a/src/ollama_cli/tools/__pycache__/execution.cpython-312.pyc b/src/ollama_cli/tools/__pycache__/execution.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eecb82688f0403d2c852da4ffda0351add1ab2ec GIT binary patch literal 1827 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 M&3UlQuE3f922}uws{jB1 delta 708 zcmYjMOK1~O6n!(3`Q&FOu{J5T=}Vd#Vy8@J5+p1IWShi+Ib~ahlLnr#wf3KB zVUdakKfa_s32~Pqr$~C{ekPZfm16&tmEp`B!6`hSQdOB0aISz`@~K4-^!0L6Xb`DpOABE{{AEVsRlD{_NOTL6MSGUW^Y}c$p&}i^w|_abuq!qs zPR`-kl$xHA71(T{AQe61gusfe-E)QPRUFmwuA#>^YZYs$J&wrALS7|3&`U9=!T{of zfSl{lsHtUvo}h-8XJh-G)2o4%K>dWT!Y?oCv_7hzFzHX7&j8*z;}M{JG&1N#m4HM(A$Lm@Xba2tkE^RD-8c_FmQdW3_!D| zmy&3cN}@0Jv3_Qob&vU(Z7*fUeik@Nkr2F!+KC8`jA);`BEb)+2??_1KNga+bJ9Xy zzDBOV9R;R@G`IL|!dDmL-}}PGg^8V$H|o7XW8}JVcX~&>cfc|tcf|8FeIN={tTNa@ U;PffWZ6prOtm)~<4AZ{xx0tU?4Xqs zR~aX+GOEp%P;8Y(Rjzopq*QoHQF)S+>@J(+p%GGP+|pJl73V>3=#naz{F48io*uA- ztld<$Db6{4F8}$@rT_DN|CxW!$+1xIxc=^oz>P+V`Y-&VKBNR*o;A`Gb%jC{qJz|k z64N6zdDo0+NUR;vl2|vQBe8x&k2FXdG=z*JMw&u8qz{@x<`FYZX(-N!49JK~$c!w= z`lTj7-?H7*D&3OO9F&Xlpwy1?QNfqm&E=>H<#86|;4FR}a(=A8nbB5_N31_BADObP z3qRInTiHmfB2)~$mSCMAE=6UycU1nRWJg3#7HFgSfjjHgAo>aeUY#~Fec}Yabb>UMa~-tv145P@X^dW!-g4>#l(a_c#ILp zIVKqKu|Z~7WJiM>b12~VLs2BmOh@=he=su5G#-qE{Ih2UT(Tj`M?z6iHV7gQt>cBK zp}8Usae|KzL`7)n(tu{OQBJmQ&eY1fFdO1{E2t}5qAU+DBF77|eT)-9Z|K4+h^!bB z;=KpQIo~AFP03**{!BNSabf)?BBKHm;YqcOhRlaCF2>1bMNMx2#p~ZDDzU+CW;n|E z0{(!HQA(MCLyQ<xB4akILBl9)wF!e`h zV!5XI6SVl?4O-|E`Yq?sI!3AEJVd=;Kv8psgaO$~VKfN}k58aKvt=~Txu*4LXlj_! zQLmAf`zJ`t+mM!#Z~wE*%-e6888Js)y4Q?q(;Q`Yl<|$NHqAx0l?ml1jIaj^5+(>8 z*Yv{_a^ih=O*2f@Qev*ENgc(P%$ZT4n6Ks}%&HVz+ow54p`tnSMKees@iaY6&1lb2 z)3mEN-uAN?#|Jp!N8fVa$pOX>-#7WD@rOPh5X1;S%P2NMHY+wG#s^;%j*DWn+v90z z?R4*gzn1QF8!oDoG{~W?;ZE&K=*`jm=OvB7y?3Fv2B1Mbaz7cE^iUD@Ph*gAJN@GjR)((WZ%s zo0&|1?s)xSo{#W&NUwfJ4MH9tm-t zNHEBT*k)fa;1PIV=GBW|@j)qgLL3UP?&vI!_bmyqG~qwM>|UatGgNNrU%!9pNZMX> z#d6uQxJPQ-w`Sje-+pw}eso2fF-o(xk*3 zSn0d#7@9wn&U4()t6I&gO19m}+nz3~yzaW{`eNsTG40rvG_E=t(iM%V(v($dJ0X=1 zNzS3?2FkfDEA1VLX20et=#|7jv)o{wYJi1odw^nvw!MJ5+y415|OwBG^Zg<{x zt?ZVH&q($&>B8>i-OD>A#}UbP3&L2Qweb)=& z7_92w_ZbfNQs1=q6+!ebRwE7_l{joA>26&7uXJB0M8Das!(p%aNR{@t#+)N%+TS{9 zoG#Nt_@mPw?_zTL|Fe#wK5_iH&i~gS2tc~(={KiEhW7v!%<1Oz$Ovaf7At^;nMXa- z;WXDHlUmzMnJLk%*4Y|aAoDGY+7lq`oCM%xjGFK)J=ZhL}Yi6E?N?jTmZ{s67&NT#mM(3XoX|y;PBqAz?KMYreFgTGDSFdCx~? zs8&E1IARhTm;%v)lb7SyVae9j_b(@36)A~m%UPhg<)(({_V z(uUeqsp?7mSB`mypGI$eY?`xbsDw4EnGIE zt6(O;UQKE%g1rjRA#xfRw=c z0bYfL2rsxak6}Yl{4U&7GZVz`U{(26EDxhKz664JaTdoAOv1oStoEDlJzj^s32>7{xR7muy}@eW&X?puxu2AcNwU4I1&&z zSx+{JY>GyNfEbwKWK)T=auOWsG8@8ImQbJdk;4546GI&tRS0*0n?L#yNo>89*RGJgM&CYgoBefI0J!9 zck(8P<3^9-HF%5>qf5hMJI7;V%$MPSfLmiMJQ;>9DCd)%dddt_F5M8>8*$sFwb%iUD(xgU>oUl?u^_;DO+=7& zcv65bz$YbZL+p%f0(t@LBgBCt#tU7@dc1hRFE&l0fPojoT;iK?g?unY$Q$OS;RDNf zxm-W?dbxXuXG`dHzf$#ckRa@X;QLF|1ABS8WZQM~Rr6OhUpL%n_`2yvQ|jl-Lu)mI zYbD1Q^xqb5OP+oR;QFnvM!z1rF?N4@$LjWuyTzT7z4IA}0DoL1gt8eHSmBPc0kQlv z3{4jS<`Or}ps{9>swbO{+2q86okH4FTkxJlV90>b6{&12T`YF3Rk>5{rnbLRE)Q|mfEt#4b` zY0YOe4{grRkAHf6u{70p&*omwrM5Kyhb?PQS9UznYYTGMZB*4Dz0$GLC~X@~S1`%y z#eOI#FIYEGRh}nS15_!XDu(FSl-a5J9ZT=5w5*n$NLTJ6BA?r+irVW#SBFw9sfo31 zyKf&@5z=+;<#*FnH7VaxOX}TpbzPE8nwEs+zNPVWePgOR*$-Vor@2&>>q)*DIxV6a zcc%K1SuD-vUv|zQysZraOqHPX_5Gr)A!>{vHk7eDPhWVEerB zA#nb^m-i+Il6z8PQgz4j*_F||j-Sn&9$6jttyQa5VEk*=?P;ajFZTg~2eV?jYD$); z0&68*^8>(}%PX!oTy3~t*0fsIlzQhMMt(gabqz`uV!RH2DPS+C~Rq0u) z*fl@&%s}Ooq)RL2k3B3bTWtO1xu=vhr*eV*uFA7yUaRU}u%#W9$);6@`;oKc%CXDG z7C-!(6W`W%tkrid41HT%pK>n`-Pv(0UEj87`L?9uk=;pL8!f-eSuX}bPs%8J*>{d2 z;(h44X=m9dC%GPT_)q$l0~ZBbJy9EF5P$AvYe0xS*dtbvX4G0 z+`AlDKEIMHoxLEv!>&`*D1A-?(TIk_(K(GDM}AEZN6ruqb3&R&g|1ZN{T;olJ9;J8 zzGdNd*YEazv-h?~a-5QEr@mkRg@%Srmo98dol6ZuldjXG`5DbqXnscX!}I-G%2tTo z0Bz}f*R9$VdvklLM9OQuXKE#GfVLOHLHJ6(sVMH>1#W;Nj{d#Wiph#YXE_dcnfkl6 zD_t~BcWZID*E~?E{kRYJBoi= zJ~DyiP4o|$6Ex5=3#N0gnF(7Udj&CPc*6{AHNS=}=NGOeRUnm}U*rFq^gLvLMY`z? z(zjYYqpBM}{eNg}%C>$N);^zHFBOmr1_!xdaN@bYW|}kq^qM8icqhPKD7=}0zkk-f zP(-eI{+PYC8ysud%bBfqGdRLZ5_EzZ14rJiQguZ@`=4&L^&Tz5dd+SFS3uTf1g-$< zoF1-PAiStwpg$W`QX7Q-rP`K_+Mn@nsGA3l#`1(d>rxwq?2^l6Y#F)m82AvF#=*#BgpV-2jAArqcKac^?#D@C3u zES#dlPX6L{vT7Z*Bz%T<6%qH=j3bvmND=>mz&5j5bk)Q3hV0=aHKoqF} zwuyj2tWXeiBo#850G@s007dbo1~Wh^DsBl7!)iP7ZfszXNra*nUQ5i%%LroG_Gm(od%bKUIqAU6n;3sWn2LK zBpgn%f%OAHnT_Z5k*Ov3iWm~a-SDFf#NaX@W{t}4GR_1=o^3wGf!|0F;WrDJuLV9J z*O08oiw`atkQy)j@drXmJWBZ>F0b6GoGiW@Zv1BA20$+nrY5edVG{WIu(_EyW|(*$ zBsa|RDw=8TIf^KE0Tfuh0KHO&zhO$5ZjdH2ew;fnwh zz%)SB2F4HTI?DPcU2%s(GL@Mjg+OX|!@^+p3_c_LyOm9*g;1}o9}wuDLUO~v!GeIL z+%Bs^O)uba;7Q~^!~tfivH?C)xYy*Tan1}5VmO$>K`jJukx95D#$tp&$~mgJBBm(& zs{9&;$J9=?k@Zl2n`2hqzSl?gZu5j>=@Gskr z?MYqoTyhYu56hNqx8Igdd!^wEQUN=E@S&}U46`kXmg;Vc_iV@CFtkVN(9TPa_axhU z&n=Y6`bpxW#KO!SW7WDwZ*Ep)9!|EW%9oH-zUOxPJzKw;-JgWHY`tr1%g)k>t_l{D(MC3yB|q;p2L=xj9|fn95IS zB}daeTN5d5ejyx!ZShT6U+&Q!>Nh2QMg0!yciPT=7xlXybH81?V%6i&Vea3qU8&aN z&}AN=wJWW<{vPd02aVG`dI;ge#2^Qvzr<0l@(o;owDJvL$Bp8g2}4TOje?h*#{f*W z`XW4sO)~uSO!>6xa9wiCju9}~y}zdTah%4^yBDS&*!kcP?&Yt*D-Hib8H6xintnj# zKcLJHD9ZyX?*V0fK;`^_D*6Le|DUuzZ7xmQ&ptBQ7rUhP!PSD}>spQ7@Z4z7XrAO! z8bjJ#p0=NSWO6KyOPwD`lcCkZ@a8HtbXQWlPC=9!eu|@KdC-&u>)YV7&-6`t8$lDqt;Wy^mJysjKk+a#17$CuPFAlWityH*_A319(*u;z?p$|RSW zU5h3`8*&lAF;KuVT2v~M0_vft3cElqEppW@KreR16zoO;0`yW}3d%CzR=xDSC6}ZW z6}@y8`{vEen>RD>ee>S@DHaP8C|~~a)7)o4LjHygzo>0y$TxKDrGL3~Gg61`Ff)M;+hH8k;FZU?IuvnK zBaYVM3F(eA`naw01i3?;h%Ye{^|@|cBfhq*1RjCUpG(OVl7oMM93wU@OO#wA6h7>` zEng#LIsmO}J#9l8cd0N!u4LJ+QZk(zZjj&3*^ce@UAp|<`LWYiCohg&eDCtL$+PEv zH0CN+i5Fc3P30|v$v2L zJny#R_&2cQb4H&?U4M6x(v@VgKMq);PRv(16Zg4pT_ZKhlqUiIUcx=zhN#MS&{Sz- z$q0^L1^25SUM8)ZE-gPt5JO)uS7f~C7^=WCxoW|@$yrX=0*~W{fGwH@E->&2#JWJ3 zy9&EiC>fF)Y-0fTYokV87YP*Tv&~t)9VrK31kqw&0s=@;g7+`nyYO>qA@nG;ka(1+ z>&I736 zIYY02zzwL(tYLn5YJ3!-qh10m(F>rb2cc| zGrTyPhTe-NG{C?D*0gd(M~7j&WoY)>8OKf=U`EBMl(-vV+|G(z3HM^iuHqT~5S+>l zmrMa(aNx?iLBX9{qPPXx2vINm%>&a5Z9iP!p*1>#659%+)18oXM*@gRvs4V$q@6-! zH<(e712EE)pv~!Ag&OsP9Y7OweM){MT_F|OP{!Q=TFtITFSk9y*}HnR?<|_q%nNR^ zihi?bm5Uie@#2XqPjj%lS%MhG1meb!#rL2R`;i<0V&if2Hr~WR>_)N&hu#M=M_wjL z{P1e*`*UX-(OnPEKR7>MUXoU$19M}IP-OnZv(UbV8haRh5S_nTR}VA}9Qq{qN%=uy zO+C1Hwyq8|Qm-wt#Sfn*jyzS5G$Q&2C?#%G@T{57dyulL#$~Sshsf(T!i#zMu)hnX zEY9`|CLqsWv%u`=FH$4_(_i3In^qvPGi4`#VT!NOGsMS$@0}{Fd4)ho@XT1y3l>9WJ zkP7|1T&9LpmEO|Fe@6iHQs$`G9yd0%lheG&ZY-jsXHMoVu)BRK(-AldtJ?iQ z{!acmNB(S#e*V$&=<-L)*(XEKjM0DDcqx98NKrrz1|9J_5Xgsc2@RZ8vhqfeDlk;WCH**rHzJ6TqD zpT<_l;H~hv1siBp4;y!Z`9D2n4)i3uPMQMs?b3bR_!yz!uZm>I-H{n@>j&CI_&+iyLTp2=mD@b@g z_7>f`kxlhv!D6@a{FFEWGw^j_<6F8ZQ%b)kk*|sN4e9^3fA}fsd!dAA{KYPrrq{zn z3O2NTjpUhc)%fDRCR~a0(mElj3sO^-lEIgupd>ZpDm}a?Hwjcr@(Zjs5`O>sZcoMD vM)HS^MCxTo0r_x%o~HBJ7X<5#NT+K(?#qG=d|8F2Q4}gqay<;3XPf^35?i22 literal 0 HcmV?d00001 diff --git a/src/ollama_cli/tools/__pycache__/system.cpython-312.pyc b/src/ollama_cli/tools/__pycache__/system.cpython-312.pyc index abacd7a53a0bdec81f2b71466020044f9236508a..9df78038b0be588f45ad20dcb87449ba017c64a7 100644 GIT binary patch delta 822 zcmbVK%}*0S6rbJh?(A+qu~>~9oRBCjaPi|nD)FKS2V#v4Au%zevQyd8?wZ*xgbh&e zBglz1HNk|Ka55%%@ov0%@>0`-b>)vJ){`gSw9r4mIlPbGoA-Nf-kUGspYh1Ia9Brh zYHtoqPp(BS3Y)c&`Ee1^fDtw#Ci1NQS#w-kB9LM-+bRLHcuLz< zVUWCVXIMaJ9~pvyifE&!g$=v3 z^6Q0AKdEmNqJ6V(7>a?9#GVUW>VGh3XAI{M0t(#?p0O8VyvND`dKb!~RPa9Ig*+*f zO%q=ro~A!obC+x@O*~c4F41BUa5+0OH9PC$vbAj4tColLIfpU`NORe#hn}L}nZKJR zDa{YSWPSBnG3WB}`$})nK4!E?a}M?K3S|!LgjFs>RUe;jzz@I%838CIvTLB&br25k zj1U&*z}T4>=8;z*wooU8gu7^R7V?l3_|1h7;8J;wzspny;;q_HJdou zP*M;XRgM*P$J{nwEN?w-Yf>oDMv|Q9Kqn}L>!S)uc6*G0&nCFweg9P_OSVxi(%UQz zF8n9tuzE?EzR`{h3d1MkNK{(dg;wldD?0E8OQP7;v5=@P{zjlWK>$uT&}IAwZVbT6 delta 94 zcmca9ypNafG%qg~0}yCWoSn(YGLcV$F>IoGI7=m?Chx{o%UGE-*(Qr~Dkuv8m3#)N pD-s40zc_4i^HWN5QtgU#fm}u)F7})p&*{$fkxztCsYnMX0009y6n+2z diff --git a/src/ollama_cli/tools/__pycache__/web.cpython-312.pyc b/src/ollama_cli/tools/__pycache__/web.cpython-312.pyc index 7e741fb1bef97e5f2f04e9dbc85b3acf9fe33c0b..1d5096241aceb335aa38eb86bea25f8243adfe00 100644 GIT binary patch delta 2188 zcmaJ?T})I*6rQ<%dl&W(cNY<`Tty4lpN)StN)S=7wxu<#AdRGktUDK2Tz1LayFtlr z*AUZ?h6e~umqeRv(Pyeu{ZZQJB=^jj zGv}O{^PMy2{@}c|+4ZZ_S%zTz@yXTjpYoKe3QtTmyx+tjO(0I5bFKXya+%lwg!7 z+C_r~Lxy4iql98nLyH@Iyk9!-zu(4lFE))|(Kq z~kD!bESu}#dxav9Ms-#TZMom$Ykc)?3MW1oZ zlAxsgp$vyx^9U~bQ9Y-q3(P3Pld>eM*mNG(f^j3<7mswt;=`lm14e2n5*Q9rU8AO? zhiG`%Fh$+C6w%hz6|1TJ@U1li=|C82D}m1WAdjqSN;L+Xg;C7dr#OuJkIS~Fy_E8R2Ilm1}Q*ZPkxz}8rGEnXK26I!7R0Mmg|g;;jHI2MA6 zm_zNdn7ITuSa|Q?qcr{>^TaNQ8?(4oZj@7$QxLMK4@bfV-3MkWPxrI>qX&SP((nKc z>YC0xD+MIzYp{4J28TgG-vuO(fxmm!cf0GCo}YWN?{+PpIJ4yMzIkx6FCCeRq@!7X z_hfX@-(5gv&pu53?EH23usm4!yb4Sy)%H9*I-ZacxRBKHVp8c|P@O|6N1351h6du% z3(qI?C1IoriEaW*9U4O+j@s5t{MPnsXBKNAk1~kIY!&!p_`;qBK*UoP2n3WygT|ww zprH}fM5Epll*Xvl6T{Jd)*_n})kQafF|7t-dT21LYrTCj+N&EuBd(kLc|CQ2?q-_; zYr3YLXrb89a7@=sxnDDSEn%6JF{?;v9n<9_TGT9m6ZEKI9ZD4t(=9+$JKf3nZH(j{ z>+sTOfUS=LF-6OfDdj<;sYVd$058jVepqjSLO~*nFt@rL-q>Z7^Hht0-@G;H=Befk zKYMz`yJuWpl|6UmEsOG&6}dhqcY#2A=500F<$Ywyo&vP%;7TN(aNE6aOn%1O~Cvz?@Rv+v@oV059u6mm8%gz}&yZ88_yDi6a h9_f)y;<%iv1_vhVa|ouH`iE>IoGI7=m?Chx`_znB;`H*aR1!4#m$UL*lj zb&IF8C?_W~Nv|NWs5rGq6ePrwln7+90GUM!Ac7T0tYr8MBpHe{fW$8jo80`A(wtPg bB3&St5r~UDC+qOJbA99!X4EXw0SW*B>RKVl diff --git a/src/ollama_cli/tools/media.py b/src/ollama_cli/tools/media.py index b691167..04922fa 100644 --- a/src/ollama_cli/tools/media.py +++ b/src/ollama_cli/tools/media.py @@ -7,6 +7,56 @@ import random from .base import tool from ..core.config import load_config +@tool( + name="generate_image", + description="Generate an image using the local Stable Diffusion workflow (ComfyUI)", + parameters={ + "prompt": {"type": "string", "description": "The image description"} + } +) +@tool( + name="get_comfy_status", + description="Check the status of image generation jobs or the ComfyUI queue", + parameters={ + "prompt_id": {"type": "string", "description": "Optional: Specific prompt ID to check", "default": ""} + } +) +def get_comfy_status(prompt_id: str = "") -> str: + """Queries the ComfyUI API for queue or history status""" + config = load_config() + comfy_url = config.get("comfy_url", "http://127.0.0.1:8188").rstrip("/") + + try: + if prompt_id: + # Check history for specific prompt + resp = requests.get(f"{comfy_url}/history/{prompt_id}", timeout=5) + if resp.status_code == 200: + history = resp.json() + if prompt_id in history: + return f"Status for {prompt_id}: Completed. Result saved." + + # Check queue if not in history + resp = requests.get(f"{comfy_url}/queue", timeout=5) + if resp.status_code == 200: + queue_data = resp.json() + for job in queue_data.get("queue_running", []): + if job[1] == prompt_id: return f"Status for {prompt_id}: Currently Running." + for job in queue_data.get("queue_pending", []): + if job[1] == prompt_id: return f"Status for {prompt_id}: Pending in Queue." + + return f"Status for {prompt_id}: ID not found in current queue or history." + else: + # General queue status + resp = requests.get(f"{comfy_url}/queue", timeout=5) + if resp.status_code == 200: + queue_data = resp.json() + running = len(queue_data.get("queue_running", [])) + pending = len(queue_data.get("queue_pending", [])) + return f"ComfyUI Queue Status: {running} running, {pending} pending." + return f"Error: Could not retrieve queue from {comfy_url}" + except Exception as e: + return f"Error connecting to ComfyUI: {str(e)}" + @tool( name="generate_image", description="Generate an image using the local Stable Diffusion workflow (ComfyUI)", @@ -18,7 +68,13 @@ def generate_image(prompt: str) -> str: """Invokes the ComfyUI API directly with a predefined workflow""" config = load_config() comfy_url = config.get("comfy_url", "http://127.0.0.1:8188").rstrip("/") + image_model = config.get("image_model", "dreamshaper_8_pruned.safetensors") + # Determine dimensions (SDXL prefers 1024x1024) + width, height = (512, 512) + if "xl" in image_model.lower(): + width, height = (1024, 1024) + try: # Check if ComfyUI is running try: @@ -27,10 +83,9 @@ def generate_image(prompt: str) -> str: return f"Error: ComfyUI server ({comfy_url}) appears to be down. Please start it first or configure the URL with '/config comfy '." # Define the workflow directly - # Based on the user's previous generate_image.py, but using the configured URL workflow = { "1": { - "inputs": {"ckpt_name": "dreamshaper_8_pruned.safetensors"}, + "inputs": {"ckpt_name": image_model}, "class_type": "CheckpointLoaderSimple" }, "2": { @@ -48,7 +103,7 @@ def generate_image(prompt: str) -> str: "class_type": "CLIPTextEncode" }, "4": { - "inputs": {"width": 512, "height": 512, "batch_size": 1}, + "inputs": {"width": width, "height": height, "batch_size": 1}, "class_type": "EmptyLatentImage" }, "5": { @@ -80,7 +135,44 @@ def generate_image(prompt: str) -> str: if response.status_code == 200: data = response.json() prompt_id = data.get('prompt_id', 'unknown') - return f"Image generation queued successfully on {comfy_url}.\nPrompt ID: {prompt_id}\nCheck your ComfyUI output folder for 'ollama_cli_' prefixed files." + + # Start monitoring for the new image + output_path = os.path.expanduser(config.get("comfy_output_path", "~/ComfyUI/output")) + + # Helper to find the newest file with our prefix + def find_newest_image(): + import glob + files = glob.glob(os.path.join(output_path, "ollama_cli_*")) + if not files: return None + return max(files, key=os.path.getctime) + + initial_newest = find_newest_image() + + # Polling loop (max 60 seconds) + import time + found_file = None + for _ in range(30): + # Check status via API + status_check = get_comfy_status(prompt_id) + if "Completed" in status_check: + # Give filesystem a moment to sync + time.sleep(1) + current_newest = find_newest_image() + if current_newest and current_newest != initial_newest: + found_file = current_newest + break + + time.sleep(2) + + if found_file: + # Open the image automatically + if sys.platform == "darwin": + subprocess.run(["open", found_file]) + else: + subprocess.run(["xdg-open", found_file]) + return f"Image generation complete!\nSaved to: {found_file}\nOpening image now..." + + return f"Image generation queued successfully on {comfy_url}.\nPrompt ID: {prompt_id}\nMonitoring timed out, but the image should appear in {output_path} soon." else: return f"Error form ComfyUI: {response.status_code} - {response.text}" @@ -96,26 +188,83 @@ def generate_image(prompt: str) -> str: } ) def speak_text(text: str, output_file: str = "output.wav") -> str: - """Uses the high-quality Piper TTS installed by the installer""" - piper_dir = os.path.expanduser("~/.ollama-cli/piper") - piper_bin = os.path.join(piper_dir, "piper") - model_path = os.path.join(piper_dir, "voice.onnx") + """Uses the best available TTS engine (Kokoro > Piper > System)""" + config = load_config() + + # 1. Try Kokoro (Ultra-Realistic) + kokoro_python = os.path.expanduser(config.get("kokoro_path", "~/.ollama-cli/kokoro/venv/bin/python")) + if os.path.exists(kokoro_python): + try: + # Inline script to run in Kokoro venv + kokoro_script = f""" +import sys +import soundfile as sf +import os +try: + from kokoro import KPipeline + import torch +except ImportError: + print("Dependencies missing") + sys.exit(1) + +text = sys.argv[1] +output_file = sys.argv[2] + +# Initialize pipeline +pipeline = KPipeline(lang_code='a') +generator = pipeline(text, voice='af_bella', speed=1) + +all_audio = [] +for gs, ps, audio in generator: + if audio is not None: + all_audio.append(audio) + +if all_audio: + import numpy as np + full_audio = np.concatenate(all_audio) + sf.write(output_file, full_audio, 24000) + print("Success") +else: + print("No audio generated") + sys.exit(1) +""" + # Run in venv + subprocess.run([kokoro_python, "-c", kokoro_script, text, output_file], + check=True, capture_output=True) + + if sys.platform == "darwin": + subprocess.run(["afplay", output_file]) + return f"Audio saved to {output_file} and played (using Ultra-Realistic Kokoro TTS)" + except Exception as e: + # Continue to fallback + pass + + # 2. Try Piper (High-Quality) + piper_config = config.get("piper", {}) + piper_bin = os.path.expanduser(piper_config.get("path", "~/.ollama-cli/piper/piper")) + model_path = os.path.expanduser(piper_config.get("model", "~/.ollama-cli/piper/voice.onnx")) if os.path.exists(piper_bin) and os.path.exists(model_path): try: # Piper reads from stdin and writes to file cmd = f'echo "{text}" | {piper_bin} --model {model_path} --output_file {output_file}' subprocess.run(cmd, shell=True, check=True, capture_output=True) - return f"Audio saved to {output_file} (using high-quality Piper TTS)" + + # Auto-play on macOS + if sys.platform == "darwin": + subprocess.run(["afplay", output_file]) + + return f"Audio saved to {output_file} and played (using high-quality Piper TTS)" except Exception as e: - return f"Error using Piper: {e}. Falling back to system TTS..." + pass - # Fallback to simple 'say' command on macOS + # 3. Fallback to system 'say' command on macOS if sys.platform == "darwin": try: subprocess.run(["say", "-o", output_file, text], check=True) - return f"Audio saved to {output_file} (using system 'say' fallback)" + subprocess.run(["afplay", output_file]) + return f"Audio saved to {output_file} and played (using system 'say' fallback)" except Exception as e: return f"Error using 'say': {e}" - return "TTS engine not found. Please run the installer to set up Piper." + return "No compatible TTS engine found. Please run the installer." diff --git a/src/ollama_cli/tools/memory.py b/src/ollama_cli/tools/memory.py index 4ec450f..5e4a2c2 100644 --- a/src/ollama_cli/tools/memory.py +++ b/src/ollama_cli/tools/memory.py @@ -45,9 +45,7 @@ def recall_facts() -> str: facts = memory.get("facts", []) if not facts: return "No project facts stored." - return "Project Facts: -" + " -".join(f"- {f}" for f in facts) + return "Project Facts:\n" + "\n".join(f"- {f}" for f in facts) @tool( name="clear_memory",