Skip to content

Commit eefca9e

Browse files
committed
changes to port selecting & settings
1 parent 1dd586a commit eefca9e

4 files changed

Lines changed: 748 additions & 20 deletions

File tree

src-tauri/src/lib.rs

Lines changed: 59 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,32 @@ use std::path::Path;
33
use std::net::TcpStream;
44
use std::time::Duration;
55
use serde_json::json;
6-
use regex::Regex;
76

87
#[cfg(target_os = "windows")]
98
use std::os::windows::process::CommandExt;
109

10+
// Default Ollama port
11+
const DEFAULT_OLLAMA_PORT: u16 = 11434;
12+
13+
// Helper function to get Ollama port from environment or use default
14+
fn get_ollama_port() -> u16 {
15+
std::env::var("OLLAMA_PORT")
16+
.ok()
17+
.and_then(|s| s.parse::<u16>().ok())
18+
.unwrap_or(DEFAULT_OLLAMA_PORT)
19+
}
20+
21+
// Helper function to get Ollama base URL
22+
fn get_ollama_base_url() -> String {
23+
let port = get_ollama_port();
24+
format!("http://localhost:{}", port)
25+
}
26+
27+
// Helper function to get Ollama API endpoint
28+
fn get_ollama_api_url(endpoint: &str) -> String {
29+
format!("{}/api/{}", get_ollama_base_url(), endpoint)
30+
}
31+
1132
// Helper function to generate extended PATH based on platform
1233
fn get_extended_path() -> String {
1334
let current_path = std::env::var("PATH").unwrap_or_default();
@@ -53,11 +74,33 @@ fn get_platform() -> String {
5374
}
5475
}
5576

77+
#[tauri::command]
78+
fn get_ollama_port_config() -> u16 {
79+
get_ollama_port()
80+
}
81+
82+
#[tauri::command]
83+
fn set_ollama_port_config(port: u16) -> Result<String, String> {
84+
if port < 1024 || port > 65535 {
85+
return Err("Port must be between 1024 and 65535".to_string());
86+
}
87+
88+
std::env::set_var("OLLAMA_PORT", port.to_string());
89+
Ok(format!("Ollama port set to {}", port))
90+
}
91+
92+
#[tauri::command]
93+
fn get_ollama_url() -> String {
94+
get_ollama_base_url()
95+
}
96+
5697
#[tauri::command]
5798
fn check_ollama_service_running() -> bool {
58-
// Check if Ollama service is running by attempting to connect to port 11434
99+
// Check if Ollama service is running by attempting to connect to the configured port
100+
let port = get_ollama_port();
101+
let address = format!("127.0.0.1:{}", port);
59102
TcpStream::connect_timeout(
60-
&"127.0.0.1:11434".parse().unwrap(),
103+
&address.parse().unwrap(),
61104
Duration::from_millis(1000)
62105
).is_ok()
63106
}
@@ -366,8 +409,9 @@ async fn list_installed_models() -> Result<Vec<String>, String> {
366409
// If ollama list fails, try to use the API directly
367410
if cfg!(target_os = "windows") {
368411
// On Windows, try to use curl to call the API
412+
let api_url = get_ollama_api_url("tags");
369413
let api_result = Command::new("curl")
370-
.args(["-s", "http://localhost:11434/api/tags"])
414+
.args(["-s", &api_url])
371415
.output();
372416

373417
match api_result {
@@ -603,10 +647,11 @@ async fn stop_ollama_service() -> Result<String, String> {
603647
#[tauri::command]
604648
async fn load_ollama_model(model_name: String) -> Result<String, String> {
605649
// Load a model by making a simple request to it
650+
let api_url = get_ollama_api_url("generate");
606651
let output = Command::new("curl")
607652
.args([
608653
"-X", "POST",
609-
"http://localhost:11434/api/generate",
654+
&api_url,
610655
"-H", "Content-Type: application/json",
611656
"-d", &format!(r#"{{"model": "{}", "prompt": "hello", "stream": false}}"#, model_name)
612657
])
@@ -620,7 +665,7 @@ async fn load_ollama_model(model_name: String) -> Result<String, String> {
620665
if stdout.contains("error") {
621666
Err(format!("Failed to load model '{}': {}", model_name, stdout))
622667
} else {
623-
Ok(format!("Model '{}' loaded successfully. API Response received. Command: curl -X POST http://localhost:11434/api/generate", model_name))
668+
Ok(format!("Model '{}' loaded successfully. API Response received. Command: curl -X POST {}", model_name, api_url))
624669
}
625670
} else {
626671
let error = String::from_utf8_lossy(&output.stderr);
@@ -732,18 +777,19 @@ async fn scan_for_models() -> Result<Vec<String>, String> {
732777

733778
#[tauri::command]
734779
fn check_ollama_service_status() -> Result<bool, String> {
735-
// Primary method: Check if Ollama service is running by testing TCP connection to port 11434
780+
// Primary method: Check if Ollama service is running by testing TCP connection to the configured port
736781
if check_ollama_service_running() {
737782
return Ok(true);
738783
}
739784

740785
// Secondary method: Try to call the API directly
786+
let api_url = get_ollama_api_url("tags");
741787
let output = Command::new("curl")
742788
.args([
743789
"-s",
744790
"--connect-timeout", "2",
745791
"--max-time", "3",
746-
"http://localhost:11434/api/tags"
792+
&api_url
747793
])
748794
.output();
749795

@@ -884,7 +930,7 @@ async fn fix_windows_ollama_service() -> Result<String, String> {
884930
attempts += 1;
885931
}
886932

887-
fix_info.push_str(" ⚠ Service was started but is not responding on port 11434\n");
933+
fix_info.push_str(&format!(" ⚠ Service was started but is not responding on port {}\n", get_ollama_port()));
888934
fix_info.push_str(" Try restarting the app or running 'ollama serve' manually\n");
889935

890936
Err(fix_info)
@@ -1451,7 +1497,10 @@ pub fn run() {
14511497
diagnose_windows_ollama_issues,
14521498
fix_windows_ollama_service,
14531499
ask_ollama_verbose,
1454-
search_web
1500+
search_web,
1501+
get_ollama_port_config,
1502+
set_ollama_port_config,
1503+
get_ollama_url
14551504
])
14561505
.setup(|app| {
14571506
if cfg!(debug_assertions) {

src/app/services/ollamaService.ts

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,44 @@
11
import { invoke } from '@tauri-apps/api/core';
22

3+
// Custom port management
4+
let cachedPort: number | null = null;
5+
6+
export async function getOllamaPort(): Promise<number> {
7+
if (cachedPort === null) {
8+
try {
9+
cachedPort = await invoke('get_ollama_port_config') as number;
10+
} catch (error) {
11+
console.warn('Failed to get Ollama port from backend, using default:', error);
12+
cachedPort = 11434;
13+
}
14+
}
15+
return cachedPort;
16+
}
17+
18+
export function clearPortCache(): void {
19+
cachedPort = null;
20+
}
21+
22+
export async function setOllamaPort(port: number): Promise<string> {
23+
try {
24+
const result = await invoke('set_ollama_port_config', { port }) as string;
25+
cachedPort = port; // Update cache
26+
return result;
27+
} catch (error: any) {
28+
throw new Error(`Failed to set Ollama port: ${error.message || error}`);
29+
}
30+
}
31+
32+
export async function getOllamaBaseUrl(): Promise<string> {
33+
try {
34+
return await invoke('get_ollama_url') as string;
35+
} catch (error) {
36+
// Fallback to constructing URL from port
37+
const port = await getOllamaPort();
38+
return `http://localhost:${port}`;
39+
}
40+
}
41+
342
// Streaming Ollama wrapper for real-time text generation
443
export async function askOllamaStreaming(
544
prompt: string,
@@ -18,7 +57,8 @@ export async function askOllamaStreaming(
1857
stream: true
1958
};
2059

21-
const res = await fetch("http://localhost:11434/api/generate", {
60+
const baseUrl = await getOllamaBaseUrl();
61+
const res = await fetch(`${baseUrl}/api/generate`, {
2262
method: "POST",
2363
headers: { "Content-Type": "application/json" },
2464
body: JSON.stringify(requestBody),
@@ -77,7 +117,8 @@ export async function askOllama(
77117
requestBody.images = images;
78118
}
79119

80-
const res = await fetch("http://localhost:11434/api/generate", {
120+
const baseUrl = await getOllamaBaseUrl();
121+
const res = await fetch(`${baseUrl}/api/generate`, {
81122
method: "POST",
82123
headers: { "Content-Type": "application/json" },
83124
body: JSON.stringify(requestBody),
@@ -124,7 +165,8 @@ export async function askOllama(
124165
export async function listOllamaModels(): Promise<string[]> {
125166
try {
126167
// Primary method: Try the API directly
127-
const res = await fetch("http://localhost:11434/api/tags", {
168+
const baseUrl = await getOllamaBaseUrl();
169+
const res = await fetch(`${baseUrl}/api/tags`, {
128170
method: "GET",
129171
signal: AbortSignal.timeout(5000), // 5 second timeout
130172
});
@@ -198,7 +240,8 @@ export async function forceRefreshModels(): Promise<string[]> {
198240
try {
199241
// On Windows, sometimes we need to force a refresh of the Ollama connection
200242
// First, try to "wake up" Ollama by making a simple API call
201-
await fetch("http://localhost:11434/api/tags", {
243+
const baseUrl = await getOllamaBaseUrl();
244+
await fetch(`${baseUrl}/api/tags`, {
202245
method: "GET",
203246
signal: AbortSignal.timeout(3000),
204247
}).catch(() => {}); // Ignore errors, this is just to wake up the service
@@ -244,7 +287,8 @@ export interface DownloadProgress {
244287
export async function checkOllamaStatus(): Promise<OllamaStatus> {
245288
try {
246289
// Primary method: Try to connect to Ollama API using standard fetch
247-
const res = await fetch("http://localhost:11434/api/tags", {
290+
const baseUrl = await getOllamaBaseUrl();
291+
const res = await fetch(`${baseUrl}/api/tags`, {
248292
method: "GET",
249293
signal: AbortSignal.timeout(3000), // 3 second timeout
250294
});

src/components/Chat.tsx

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,19 @@
22

33
import { useState, useEffect, useRef, useCallback } from "react";
44
import { useTheme } from "next-themes";
5-
import { askOllama, listOllamaModels, forceRefreshModels, checkOllamaStatus, askOllamaVerbose, askOllamaStreaming, searchWeb, type OllamaStatus } from "@/app/services/ollamaService";
5+
import { askOllama, listOllamaModels, forceRefreshModels, checkOllamaStatus, askOllamaVerbose, askOllamaStreaming, searchWeb, clearPortCache, type OllamaStatus } from "@/app/services/ollamaService";
66
import { PlaceholdersAndVanishInput } from "@/components/ui/placeholders-and-vanish-input";
77
import { TextGenerateEffect } from "@/components/ui/enhanced-text-generate-effect";
88
import { StreamingTextEffect } from "@/components/ui/streaming-text-effect";
99
import { MarkdownRenderer } from "@/components/MarkdownRenderer";
1010
import { ThinkingRenderer } from "@/components/ThinkingRenderer";
1111
import { Button } from "@/components/ui/button";
1212
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
13-
import { MessageSquare, Bot, User, Plus, MoreHorizontal, Menu, Sun, Moon, Trash2, X, Settings, Terminal, Eye, EyeOff, RefreshCw, BarChart3, Search } from "lucide-react";
13+
import { MessageSquare, Bot, User, Plus, MoreHorizontal, Menu, Sun, Moon, Trash2, X, Settings as SettingsIcon, Terminal, Eye, EyeOff, RefreshCw, BarChart3, Search } from "lucide-react";
1414
import { ShineBorder } from "@/components/magicui/shine-border";
1515
import { OllamaChatIcon } from "@/components/ui/ollama-chat-icon";
1616
import { OllamaSetupOverlay } from "@/components/OllamaSetupOverlay";
17+
import { Settings } from "@/components/Settings";
1718

1819
interface Message {
1920
id: string;
@@ -62,6 +63,7 @@ export default function Chat() {
6263
const [openMenuId, setOpenMenuId] = useState<string | null>(null);
6364
const [ollamaStatus, setOllamaStatus] = useState<OllamaStatus>({ isInstalled: false, isRunning: false });
6465
const [showOllamaSetup, setShowOllamaSetup] = useState(false);
66+
const [showSettings, setShowSettings] = useState(false);
6567
const [commandLogs, setCommandLogs] = useState<CommandLog[]>([]);
6668
const [showLogs, setShowLogs] = useState(false);
6769
const [clearConfirmation, setClearConfirmation] = useState<ClearConfirmationState>({
@@ -761,6 +763,23 @@ export default function Chat() {
761763
}
762764
}, [refreshModels]);
763765

766+
const handlePortChanged = useCallback(async () => {
767+
// When the port changes, refresh Ollama status and models to use the new port
768+
try {
769+
// Clear cached port to force re-fetch from backend
770+
clearPortCache();
771+
772+
const status = await checkOllamaStatus();
773+
setOllamaStatus(status);
774+
775+
if (status.isRunning) {
776+
await refreshModels();
777+
}
778+
} catch (error) {
779+
console.error('Error updating status after port change:', error);
780+
}
781+
}, [refreshModels]);
782+
764783
const handleStatsRequest = async () => {
765784
// Toggle verbose mode for future messages
766785
setVerboseMode(!verboseMode);
@@ -943,10 +962,10 @@ export default function Chat() {
943962
<Button
944963
variant="ghost"
945964
size="sm"
946-
onClick={() => setShowOllamaSetup(true)}
947-
title="Ollama Settings"
965+
onClick={() => setShowSettings(true)}
966+
title="Settings"
948967
>
949-
<Settings className="w-4 h-4" />
968+
<SettingsIcon className="w-4 h-4" />
950969
</Button>
951970
<Button
952971
variant="ghost"
@@ -1477,6 +1496,14 @@ export default function Chat() {
14771496
onCommandLog={addCommandLog}
14781497
onCommandUpdate={updateCommandLog}
14791498
/>
1499+
1500+
{/* Settings Modal */}
1501+
<Settings
1502+
isOpen={showSettings}
1503+
onClose={() => setShowSettings(false)}
1504+
onPortChanged={handlePortChanged}
1505+
onOllamaStatusChange={handleOllamaStatusChange}
1506+
/>
14801507
</div>
14811508
);
14821509
}

0 commit comments

Comments
 (0)