feat: Enhance port selection with availability check (#5966)
This change improves the robustness of the llama.cpp extension's server port selection. Previously, the `getRandomPort()` method only checked for ports already in use by active sessions, which could lead to model load failures if the chosen port was occupied by another external process. This change introduces a new Tauri command, `is_port_available`, which performs a system-level check to ensure the randomly selected port is truly free before attempting to start the llama-server. It also adds a retry mechanism with a maximum number of attempts (20,000) to find an available port, throwing an error if no suitable port is found within the specified range after all attempts. This enhancement prevents port conflicts and improves the reliability and user experience of the llama.cpp extension within Jan. Closes #5965
This commit is contained in:
parent
eb714776ba
commit
f61ce886a0
@ -1075,15 +1075,27 @@ export default class llamacpp_extension extends AIEngine {
|
||||
* Function to find a random port
|
||||
*/
|
||||
private async getRandomPort(): Promise<number> {
|
||||
let port: number
|
||||
do {
|
||||
port = Math.floor(Math.random() * 1000) + 3000
|
||||
} while (
|
||||
Array.from(this.activeSessions.values()).some(
|
||||
const MAX_ATTEMPTS = 20000
|
||||
let attempts = 0
|
||||
|
||||
while (attempts < MAX_ATTEMPTS) {
|
||||
const port = Math.floor(Math.random() * 1000) + 3000
|
||||
|
||||
const isAlreadyUsed = Array.from(this.activeSessions.values()).some(
|
||||
(info) => info.port === port
|
||||
)
|
||||
|
||||
if (!isAlreadyUsed) {
|
||||
const isAvailable = await invoke<boolean>('is_port_available', { port })
|
||||
if (isAvailable) return port
|
||||
}
|
||||
|
||||
attempts++
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
'Failed to find an available port for the model to load'
|
||||
)
|
||||
return port
|
||||
}
|
||||
|
||||
private async sleep(ms: number): Promise<void> {
|
||||
|
||||
@ -502,7 +502,6 @@ fn parse_device_output(output: &str) -> ServerResult<Vec<DeviceInfo>> {
|
||||
Ok(devices)
|
||||
}
|
||||
|
||||
|
||||
fn parse_device_line(line: &str) -> ServerResult<Option<DeviceInfo>> {
|
||||
let line = line.trim();
|
||||
|
||||
@ -534,9 +533,15 @@ fn parse_device_line(line: &str) -> ServerResult<Option<DeviceInfo>> {
|
||||
if memory_parts.len() >= 2 {
|
||||
if let (Ok(total_mem), Ok(free_mem)) = (
|
||||
parse_memory_value(memory_parts[0].trim()),
|
||||
parse_memory_value(memory_parts[1].trim())
|
||||
parse_memory_value(memory_parts[1].trim()),
|
||||
) {
|
||||
log::info!("Parsed device - ID: '{}', Name: '{}', Mem: {}, Free: {}", id, name, total_mem, free_mem);
|
||||
log::info!(
|
||||
"Parsed device - ID: '{}', Name: '{}', Mem: {}, Free: {}",
|
||||
id,
|
||||
name,
|
||||
total_mem,
|
||||
free_mem
|
||||
);
|
||||
|
||||
return Ok(Some(DeviceInfo {
|
||||
id,
|
||||
@ -590,9 +595,10 @@ fn is_memory_pattern(content: &str) -> bool {
|
||||
parts.iter().all(|part| {
|
||||
let part = part.trim();
|
||||
// Each part should start with a number and contain "MiB"
|
||||
part.split_whitespace().next()
|
||||
.map_or(false, |first_word| first_word.parse::<i32>().is_ok()) &&
|
||||
part.contains("MiB")
|
||||
part.split_whitespace()
|
||||
.next()
|
||||
.map_or(false, |first_word| first_word.parse::<i32>().is_ok())
|
||||
&& part.contains("MiB")
|
||||
})
|
||||
}
|
||||
|
||||
@ -609,10 +615,7 @@ fn parse_memory_value(mem_str: &str) -> ServerResult<i32> {
|
||||
// Take the first part which should be the number
|
||||
let number_str = parts[0];
|
||||
number_str.parse::<i32>().map_err(|_| {
|
||||
ServerError::ParseError(format!(
|
||||
"Could not parse memory value: '{}'",
|
||||
number_str
|
||||
))
|
||||
ServerError::ParseError(format!("Could not parse memory value: '{}'", number_str))
|
||||
})
|
||||
}
|
||||
|
||||
@ -643,3 +646,9 @@ pub async fn is_process_running(pid: i32, state: State<'_, AppState>) -> Result<
|
||||
|
||||
Ok(alive)
|
||||
}
|
||||
|
||||
// check port availability
|
||||
#[tauri::command]
|
||||
pub fn is_port_available(port: u16) -> bool {
|
||||
std::net::TcpListener::bind(("127.0.0.1", port)).is_ok()
|
||||
}
|
||||
|
||||
@ -96,6 +96,7 @@ pub fn run() {
|
||||
core::utils::extensions::inference_llamacpp_extension::server::load_llama_model,
|
||||
core::utils::extensions::inference_llamacpp_extension::server::unload_llama_model,
|
||||
core::utils::extensions::inference_llamacpp_extension::server::get_devices,
|
||||
core::utils::extensions::inference_llamacpp_extension::server::is_port_available,
|
||||
core::utils::extensions::inference_llamacpp_extension::server::generate_api_key,
|
||||
core::utils::extensions::inference_llamacpp_extension::server::is_process_running,
|
||||
])
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user