Add basic functionality

This commit is contained in:
2026-10-05 21:51:31 +02:00
parent 91fde51eea
commit e9a3144f00
21 changed files with 2239 additions and 277 deletions
+1076 -253
View File
File diff suppressed because it is too large Load Diff
+4
View File
@@ -22,6 +22,10 @@ tauri = { version = "2", features = [] }
tauri-plugin-opener = "2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
ollama-rs = { version = "0.3.6", features= ["headers", "stream", "modelfile"] }
tauri-plugin-store = "2"
tokio = "1.53.1"
tauri-plugin-http = "2"
# Read the optimization guideline for more details: https://tauri.app/concept/size/#cargo-configuration
+7 -3
View File
@@ -2,9 +2,13 @@
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"description": "Capability for the main window",
"windows": ["main"],
"windows": [
"main"
],
"permissions": [
"core:default",
"opener:default"
"opener:default",
"store:default",
"http:default"
]
}
}
+89 -2
View File
@@ -1,16 +1,103 @@
use ollama_rs::generation::completion::GenerationResponse;
use serde::{Deserialize, Serialize};
use tokio::{io, sync::RwLock};
use std::sync::Arc;
use tauri_plugin_http::reqwest;
use ollama_rs::generation::completion::request::GenerationRequest;
use ollama_rs::{Ollama, generation::{embeddings::request::GenerateEmbeddingsRequest}, models::{LocalModel, ModelOptions, ModelInfo, create::CreateModelRequest}};
use tauri::{State, Url, Manager};
use std::str::FromStr;
pub struct OllamaState (pub Arc<RwLock<Option<Ollama>>>);
#[tauri::command]
async fn connect_ollama(url: String, api_key: String, ollama_state: State<'_, OllamaState>) -> Result<(),String>{
let url_parsed=Url::from_str(&url).map_err(|e|e.to_string())?;
let mut headers=reqwest::header::HeaderMap::new();
headers.insert("Authorization",reqwest::header::HeaderValue::from_str(&format!("Bearer {api_key}")).map_err(|e|e.to_string())?);
let ollama_instance=Ollama::builder().url(url_parsed).request_headers(headers).build();
*(*ollama_state).0.write().await = Some(ollama_instance);
Ok(())
}
#[tauri::command]
async fn prompt_embedding(ollama_state: State<'_,OllamaState>, model: String, prompt: String) -> Result<Vec<f32>,String>{
let guard=(*ollama_state).0.read().await;
let ollama=guard.as_ref().ok_or_else(|| "Ollama wurde nicht initialisiert")?;
let generate_request=GenerateEmbeddingsRequest::new(model,prompt.into());
let result=ollama.generate_embeddings(generate_request).await.map_err(|e|e.to_string())?;
if result.embeddings.get(0).is_some(){
return Ok(result.embeddings.get(0).unwrap().to_vec());
}
Ok(vec![])
}
#[tauri::command]
async fn get_models(ollama_state: State<'_,OllamaState>) -> Result<Vec<LocalModel>,String>{
let guard=(*ollama_state).0.read().await;
let ollama=guard.as_ref().ok_or_else(|| "Ollama wurde nicht initialisiert")?;
Ok(ollama.list_local_models().await.map_err(|e|e.to_string())?)
}
#[derive(Serialize, Deserialize)]
struct MyGenerationResponse {
pub response: String,
pub thinking: Option<String>
}
impl From<GenerationResponse> for MyGenerationResponse {
fn from(arg: GenerationResponse) -> Self {
MyGenerationResponse { response: arg.response, thinking: arg.thinking }
}
}
#[tauri::command]
async fn prompt_normal(ollama_state: State<'_,OllamaState>, model:String, prompt: String, thinking: bool, temperature: f32) -> Result<MyGenerationResponse,String>{
let guard=(*ollama_state).0.read().await;
let ollama=guard.as_ref().ok_or_else(|| "Ollama wurde nicht initialisiert")?;
let options=ModelOptions::default().temperature(temperature);
Ok(ollama.generate(GenerationRequest::new(model,prompt).options(options).think(thinking)).await.map_err(|e|e.to_string())?.into())
}
#[tauri::command]
async fn get_model_info(ollama_state: State<'_,OllamaState>, model:String) -> Result<ModelInfo,String>{
let guard=(*ollama_state).0.read().await;
let ollama=guard.as_ref().ok_or_else(|| "Ollama wurde nicht initialisiert")?;
Ok(ollama.show_model_info(model).await.map_err(|e|e.to_string())?)
}
#[tauri::command]
async fn create_model(ollama_state: State<'_,OllamaState>, from_model:String, system_prompt: String, template: String, new_model: String) -> Result<String,String>{
let guard=(*ollama_state).0.read().await;
let ollama=guard.as_ref().ok_or_else(|| "Ollama wurde nicht initialisiert")?;
Ok(ollama.create_model(CreateModelRequest::new(new_model).from_model(from_model).system(system_prompt).template(template)).await.map_err(|e|e.to_string())?.message)
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
#[cfg(target_os = "linux")]
#[cfg(target_os = "linux")]
// SAFETY: std::env::set_var can cause race conditions in multithreaded contexts. We have not spawned any other threads at this point.
unsafe {
std::env::set_var("WEBKIT_DISABLE_DMABUF_RENDERER", "1");
std::env::set_var("WEBKIT_DISABLE_COMPOSITING_MODE", "1");
}
tauri::Builder::default()
.plugin(tauri_plugin_store::Builder::new().build())
.plugin(tauri_plugin_opener::init())
.invoke_handler(tauri::generate_handler![])
.setup(|app| {
let handle=app.handle();
let ollama_state: OllamaState = OllamaState(Arc::new(RwLock::new(None)));
handle.manage(ollama_state);
Ok(())
})
.invoke_handler(tauri::generate_handler![connect_ollama, get_model_info, prompt_embedding, get_models, prompt_normal, create_model])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}