Enhancement Suite: Anti-loop protection, Kokoro TTS integration, ComfyUI status monitoring, and auto-open image functionality
This commit is contained in:
0
build/lib/ollama_cli/integrations/__init__.py
Normal file
0
build/lib/ollama_cli/integrations/__init__.py
Normal file
75
build/lib/ollama_cli/integrations/lsp.py
Normal file
75
build/lib/ollama_cli/integrations/lsp.py
Normal file
@@ -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)}"
|
||||
103
build/lib/ollama_cli/integrations/mcp.py
Normal file
103
build/lib/ollama_cli/integrations/mcp.py
Normal file
@@ -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)}"
|
||||
64
build/lib/ollama_cli/integrations/notify.py
Normal file
64
build/lib/ollama_cli/integrations/notify.py
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user