A local-first private AI assistant for everyday use. Runs on-device models with encrypted P2P sync, and supports sharing chats publicly on ATProto.
10
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #2 from tileshq/feat/mlx-runner

Basic mlx-runner from modelfile

authored by

Anandu Pavanan and committed by
GitHub
4bf12368 aa023f97

+405 -30
+243 -1
Cargo.lock
··· 3 3 version = 4 4 4 5 5 [[package]] 6 + name = "anstream" 7 + version = "0.6.21" 8 + source = "registry+https://github.com/rust-lang/crates.io-index" 9 + checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" 10 + dependencies = [ 11 + "anstyle", 12 + "anstyle-parse", 13 + "anstyle-query", 14 + "anstyle-wincon", 15 + "colorchoice", 16 + "is_terminal_polyfill", 17 + "utf8parse", 18 + ] 19 + 20 + [[package]] 21 + name = "anstyle" 22 + version = "1.0.13" 23 + source = "registry+https://github.com/rust-lang/crates.io-index" 24 + checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" 25 + 26 + [[package]] 27 + name = "anstyle-parse" 28 + version = "0.2.7" 29 + source = "registry+https://github.com/rust-lang/crates.io-index" 30 + checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" 31 + dependencies = [ 32 + "utf8parse", 33 + ] 34 + 35 + [[package]] 36 + name = "anstyle-query" 37 + version = "1.1.4" 38 + source = "registry+https://github.com/rust-lang/crates.io-index" 39 + checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" 40 + dependencies = [ 41 + "windows-sys", 42 + ] 43 + 44 + [[package]] 45 + name = "anstyle-wincon" 46 + version = "3.0.10" 47 + source = "registry+https://github.com/rust-lang/crates.io-index" 48 + checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" 49 + dependencies = [ 50 + "anstyle", 51 + "once_cell_polyfill", 52 + "windows-sys", 53 + ] 54 + 55 + [[package]] 56 + name = "clap" 57 + version = "4.5.48" 58 + source = "registry+https://github.com/rust-lang/crates.io-index" 59 + checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae" 60 + dependencies = [ 61 + "clap_builder", 62 + "clap_derive", 63 + ] 64 + 65 + [[package]] 66 + name = "clap_builder" 67 + version = "4.5.48" 68 + source = "registry+https://github.com/rust-lang/crates.io-index" 69 + checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9" 70 + dependencies = [ 71 + "anstream", 72 + "anstyle", 73 + "clap_lex", 74 + "strsim", 75 + ] 76 + 77 + [[package]] 78 + name = "clap_derive" 79 + version = "4.5.47" 80 + source = "registry+https://github.com/rust-lang/crates.io-index" 81 + checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c" 82 + dependencies = [ 83 + "heck", 84 + "proc-macro2", 85 + "quote", 86 + "syn", 87 + ] 88 + 89 + [[package]] 90 + name = "clap_lex" 91 + version = "0.7.5" 92 + source = "registry+https://github.com/rust-lang/crates.io-index" 93 + checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" 94 + 95 + [[package]] 96 + name = "colorchoice" 97 + version = "1.0.4" 98 + source = "registry+https://github.com/rust-lang/crates.io-index" 99 + checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" 100 + 101 + [[package]] 102 + name = "heck" 103 + version = "0.5.0" 104 + source = "registry+https://github.com/rust-lang/crates.io-index" 105 + checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 106 + 107 + [[package]] 108 + name = "is_terminal_polyfill" 109 + version = "1.70.1" 110 + source = "registry+https://github.com/rust-lang/crates.io-index" 111 + checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 112 + 113 + [[package]] 6 114 name = "memchr" 7 115 version = "2.7.6" 8 116 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 18 126 ] 19 127 20 128 [[package]] 21 - name = "tilekit" 129 + name = "once_cell_polyfill" 130 + version = "1.70.1" 131 + source = "registry+https://github.com/rust-lang/crates.io-index" 132 + checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" 133 + 134 + [[package]] 135 + name = "proc-macro2" 136 + version = "1.0.101" 137 + source = "registry+https://github.com/rust-lang/crates.io-index" 138 + checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" 139 + dependencies = [ 140 + "unicode-ident", 141 + ] 142 + 143 + [[package]] 144 + name = "quote" 145 + version = "1.0.41" 146 + source = "registry+https://github.com/rust-lang/crates.io-index" 147 + checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" 148 + dependencies = [ 149 + "proc-macro2", 150 + ] 151 + 152 + [[package]] 153 + name = "strsim" 154 + version = "0.11.1" 155 + source = "registry+https://github.com/rust-lang/crates.io-index" 156 + checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 157 + 158 + [[package]] 159 + name = "syn" 160 + version = "2.0.106" 161 + source = "registry+https://github.com/rust-lang/crates.io-index" 162 + checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" 163 + dependencies = [ 164 + "proc-macro2", 165 + "quote", 166 + "unicode-ident", 167 + ] 168 + 169 + [[package]] 170 + name = "tiles" 22 171 version = "0.1.0" 23 172 dependencies = [ 173 + "clap", 24 174 "nom", 25 175 ] 176 + 177 + [[package]] 178 + name = "unicode-ident" 179 + version = "1.0.19" 180 + source = "registry+https://github.com/rust-lang/crates.io-index" 181 + checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" 182 + 183 + [[package]] 184 + name = "utf8parse" 185 + version = "0.2.2" 186 + source = "registry+https://github.com/rust-lang/crates.io-index" 187 + checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 188 + 189 + [[package]] 190 + name = "windows-link" 191 + version = "0.2.1" 192 + source = "registry+https://github.com/rust-lang/crates.io-index" 193 + checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" 194 + 195 + [[package]] 196 + name = "windows-sys" 197 + version = "0.60.2" 198 + source = "registry+https://github.com/rust-lang/crates.io-index" 199 + checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" 200 + dependencies = [ 201 + "windows-targets", 202 + ] 203 + 204 + [[package]] 205 + name = "windows-targets" 206 + version = "0.53.5" 207 + source = "registry+https://github.com/rust-lang/crates.io-index" 208 + checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" 209 + dependencies = [ 210 + "windows-link", 211 + "windows_aarch64_gnullvm", 212 + "windows_aarch64_msvc", 213 + "windows_i686_gnu", 214 + "windows_i686_gnullvm", 215 + "windows_i686_msvc", 216 + "windows_x86_64_gnu", 217 + "windows_x86_64_gnullvm", 218 + "windows_x86_64_msvc", 219 + ] 220 + 221 + [[package]] 222 + name = "windows_aarch64_gnullvm" 223 + version = "0.53.1" 224 + source = "registry+https://github.com/rust-lang/crates.io-index" 225 + checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" 226 + 227 + [[package]] 228 + name = "windows_aarch64_msvc" 229 + version = "0.53.1" 230 + source = "registry+https://github.com/rust-lang/crates.io-index" 231 + checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" 232 + 233 + [[package]] 234 + name = "windows_i686_gnu" 235 + version = "0.53.1" 236 + source = "registry+https://github.com/rust-lang/crates.io-index" 237 + checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" 238 + 239 + [[package]] 240 + name = "windows_i686_gnullvm" 241 + version = "0.53.1" 242 + source = "registry+https://github.com/rust-lang/crates.io-index" 243 + checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" 244 + 245 + [[package]] 246 + name = "windows_i686_msvc" 247 + version = "0.53.1" 248 + source = "registry+https://github.com/rust-lang/crates.io-index" 249 + checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" 250 + 251 + [[package]] 252 + name = "windows_x86_64_gnu" 253 + version = "0.53.1" 254 + source = "registry+https://github.com/rust-lang/crates.io-index" 255 + checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" 256 + 257 + [[package]] 258 + name = "windows_x86_64_gnullvm" 259 + version = "0.53.1" 260 + source = "registry+https://github.com/rust-lang/crates.io-index" 261 + checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" 262 + 263 + [[package]] 264 + name = "windows_x86_64_msvc" 265 + version = "0.53.1" 266 + source = "registry+https://github.com/rust-lang/crates.io-index" 267 + checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
+2 -1
Cargo.toml
··· 1 1 [package] 2 - name = "tilekit" 2 + name = "tiles" 3 3 version = "0.1.0" 4 4 edition = "2024" 5 5 6 6 [dependencies] 7 + clap = { version = "4.5.48", features = ["derive"] } 7 8 nom = "8"
+19
src/commands/mod.rs
··· 1 + // Module that handles CLI commands 2 + 3 + use tiles::{ 4 + core::{health, modelfile}, 5 + runner::mlx, 6 + }; 7 + 8 + pub fn run(modelfile: &str) { 9 + match modelfile::parse_from_file(modelfile) { 10 + Ok(modelfile) => { 11 + mlx::run(modelfile); 12 + } 13 + Err(err) => println!("{}", err), 14 + } 15 + } 16 + 17 + pub fn check_health() { 18 + health::check_health(); 19 + }
+34
src/core/health.rs
··· 1 + // Contains functions for health checking various dependencies 2 + 3 + use std::{env, process::Command}; 4 + 5 + //TODO: In future we can install the dependencies if not there.. 6 + pub fn check_health() { 7 + let os = env::consts::OS; 8 + println!("Running diagnosis..."); 9 + check_python3(); 10 + if os == "macos" { 11 + check_mlx_lm() 12 + } 13 + } 14 + 15 + fn check_python3() { 16 + let output = Command::new("python3") 17 + .arg("--version") 18 + .output() 19 + .ok() 20 + .map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string()); 21 + 22 + if let Some(version) = output { 23 + println!("Python3: ✅ {}", version) 24 + } else { 25 + println!("Python3: ❌ hint: Install Python3 for your OS") 26 + } 27 + } 28 + 29 + fn check_mlx_lm() { 30 + match Command::new("mlx_lm.chat").arg("--help").output() { 31 + Ok(_) => println!("mlx_lm: ✅"), 32 + _ => println!("mlx_lm: ❌ hint: run `pip install mlx-lm`"), 33 + } 34 + }
+2
src/core/mod.rs
··· 1 + pub mod health; 2 + pub mod modelfile;
+2 -1
src/lib.rs
··· 1 - pub mod modelfile; 1 + pub mod core; 2 + pub mod runner; 2 3 3 4 #[cfg(test)] 4 5 mod tests {}
+27 -9
src/main.rs
··· 1 1 use std::error::Error; 2 2 3 - use tilekit::modelfile; 4 - pub fn main() -> Result<(), Box<dyn Error>> { 5 - println!("{}:{}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")); 3 + use clap::{Parser, Subcommand}; 4 + mod commands; 5 + #[derive(Debug, Parser)] 6 + #[command(name = "tilekit")] 7 + #[command(version, about = "Run, fine-tune models locally with Modelfile", long_about = None)] 8 + struct Cli { 9 + #[command(subcommand)] 10 + command: Commands, 11 + } 6 12 7 - let mut modf = modelfile::parse_from_file("fixtures/a.modelfile")?; 8 - modf.add_parameter("temperature", "0.5")?; 9 - modf.add_message("user", "Is Rust a functional language")?; 10 - modf.add_message("assistant", "no")?; 11 - modf.build()?; 12 - println!("{:?}", modf.to_string()); 13 + #[derive(Subcommand, Debug)] 14 + enum Commands { 15 + /// Runs the given modelfile Path 16 + Run { modelfile_path: String }, 17 + 18 + /// Checks the status of dependencies 19 + Health, 20 + } 21 + pub fn main() -> Result<(), Box<dyn Error>> { 22 + let cli = Cli::parse(); 23 + match cli.command { 24 + Commands::Run { modelfile_path } => { 25 + commands::run(modelfile_path.as_str()); 26 + } 27 + Commands::Health => { 28 + commands::check_health(); 29 + } 30 + } 13 31 Ok(()) 14 32 }
+28 -18
src/modelfile.rs src/core/modelfile.rs
··· 22 22 23 23 #[allow(dead_code)] 24 24 #[derive(Debug, Clone)] 25 - enum ParamValue { 25 + pub enum ParamValue { 26 26 Int(i32), 27 27 Float(f32), 28 28 Str(String), 29 + } 30 + 31 + impl Display for ParamValue { 32 + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 33 + match self { 34 + ParamValue::Int(value) => write!(f, "{}", value), 35 + ParamValue::Str(value) => write!(f, "{}", value), 36 + ParamValue::Float(value) => write!(f, "{}", value), 37 + } 38 + } 29 39 } 30 40 31 41 #[derive(Debug, Clone)] ··· 54 64 } 55 65 #[allow(dead_code)] 56 66 #[derive(Debug, Clone)] 57 - struct Parameter { 58 - param_type: String, 59 - value: ParamValue, 67 + pub struct Parameter { 68 + pub param_type: String, 69 + pub value: ParamValue, 60 70 } 61 71 62 72 #[allow(dead_code)] 63 73 #[derive(Debug, Clone)] 64 - struct Message { 74 + pub struct Message { 65 75 role: Role, 66 76 message: String, 67 77 } ··· 74 84 75 85 #[derive(Debug, Clone)] 76 86 pub struct Modelfile { 77 - from: Option<String>, 78 - parameters: Vec<Parameter>, 79 - template: Option<String>, 80 - adapter: Option<String>, 81 - system: Option<String>, 82 - license: Option<String>, 83 - messages: Vec<Message>, 84 - data: Vec<String>, 85 - errors: Vec<String>, 87 + pub from: Option<String>, 88 + pub parameters: Vec<Parameter>, 89 + pub template: Option<String>, 90 + pub adapter: Option<String>, 91 + pub system: Option<String>, 92 + pub license: Option<String>, 93 + pub messages: Vec<Message>, 94 + pub data: Vec<String>, 95 + pub errors: Vec<String>, 86 96 } 87 97 88 98 impl Modelfile { ··· 226 236 } 227 237 228 238 pub fn parse_from_file(path: &str) -> Result<Modelfile, String> { 229 - let content = fs::read_to_string(path).expect("File read failed"); 230 - parse(content.as_str()) 239 + match fs::read_to_string(path) { 240 + Ok(content) => parse(content.as_str()), 241 + Err(err) => Err(format!("Parsing Modelfile failed due to {}", err)), 242 + } 231 243 } 232 244 233 245 pub fn parse(input: &str) -> Result<Modelfile, String> { ··· 403 415 #[cfg(test)] 404 416 mod tests { 405 417 use std::error::Error; 406 - 407 - use nom::Err; 408 418 409 419 use super::*; 410 420
+47
src/runner/mlx.rs
··· 1 + use std::process::Command; 2 + 3 + use crate::core::modelfile::Modelfile; 4 + 5 + pub fn run(modelfile: Modelfile) { 6 + // build the arg list from modelfile 7 + 8 + let mut args: Vec<String> = vec![]; 9 + args.push("--model".to_owned()); 10 + args.push(modelfile.from.unwrap()); 11 + for parameter in modelfile.parameters { 12 + let param_value = parameter.value.to_string(); 13 + match parameter.param_type.as_str() { 14 + "num_predict" => { 15 + args.push("--max-tokens".to_owned()); 16 + args.push(param_value); 17 + } 18 + "temperature" => { 19 + args.push("--temp".to_owned()); 20 + args.push(param_value); 21 + } 22 + "top_p" => { 23 + args.push("--top-p".to_owned()); 24 + args.push(param_value); 25 + } 26 + "seed" => { 27 + args.push("--seed".to_owned()); 28 + args.push(param_value); 29 + } 30 + _ => {} 31 + } 32 + } 33 + if let Some(system_prompt) = modelfile.system { 34 + args.push("--system-prompt".to_owned()); 35 + args.push(system_prompt); 36 + } 37 + if let Some(adapter_path) = modelfile.adapter { 38 + args.push("--adapter-path".to_owned()); 39 + args.push(adapter_path); 40 + } 41 + let mut mlx = Command::new("mlx_lm.chat") 42 + .args(args) 43 + .spawn() 44 + .expect("mlx runner failed"); 45 + 46 + mlx.wait().expect("wait failed"); 47 + }
+1
src/runner/mod.rs
··· 1 + pub mod mlx;