@@ -178,164 +178,174 @@ void chat(Transformer *transformer, Tokenizer *tokenizer, Sampler *sampler, cons
178178}
179179
180180int main (int argc, char *argv[]) {
181- CLI ::App app{" LEAP Inference Engine\n A high-performance, distributed LLM inference runner.\n " };
181+ CLI ::App app{" LEAP Inference Engine - High-performance distributed LLM inference" };
182+ app.description (" A specialized runner for LEAP models supporting single-node and distributed pipelined inference.\n "
183+ " Example (Single Node):\n "
184+ " ./inference model.bin -p \" Hello, how are you?\"\n\n "
185+ " Example (Distributed Master):\n "
186+ " ./inference model.bin --role master --next-host 192.168.1.10 --next-port 9999 --split 16\n\n "
187+ " Example (Distributed Worker):\n "
188+ " ./inference model.bin --role worker --port 9999 --next-host 192.168.1.11 --next-port 9999 --split 16 --end 32" );
189+
182190 argv = app.ensure_utf8 (argv);
183191
184- std::string checkpoint_path;
192+ // Core arguments
193+ std::string model_path;
185194 std::string tokenizer_path = " tokenizer.bin" ;
195+ app.add_option (" model" , model_path, " Path to the model checkpoint file (.bin)" )
196+ ->required ()
197+ ->check (CLI ::ExistingFile);
198+
199+ // General Configuration
200+ auto *gen_group = app.add_option_group (" General" , " Basic inference settings" );
201+ std::string mode = " generate" ;
202+ bool chat_mode = false ;
203+ gen_group->add_flag (" -c,--chat" , chat_mode, " Run in interactive chat mode" );
204+ gen_group->add_option (" -t,--tokenizer" , tokenizer_path, " Path to the tokenizer file" )
205+ ->check (CLI ::ExistingFile)
206+ ->capture_default_str ();
207+ int n_predict = 4096 ;
208+ gen_group->add_option (" -n,--n-predict" , n_predict, " Maximum number of tokens to generate (0 = model max)" )
209+ ->capture_default_str ();
210+ unsigned long long rng_seed = 0 ;
211+ gen_group->add_option (" --seed" , rng_seed, " Random seed for reproducibility (0 = use current time)" )
212+ ->capture_default_str ();
213+
214+ // Sampling Configuration
215+ auto *sample_group = app.add_option_group (" Sampling" , " Tokens selection parameters" );
186216 float temperature = 1 .0f ;
217+ sample_group->add_option (" --temp" , temperature, " Temperature for sampling (higher = more creative, 0.0 = greedy)" )
218+ ->check (CLI ::NonNegativeNumber)
219+ ->capture_default_str ();
187220 float topp = 0 .9f ;
188- int steps = 4096 ;
221+ sample_group->add_option (" --top-p" , topp, " Top-P (nucleus) sampling threshold" )
222+ ->check (CLI::Range (0.0 , 1.0 ))
223+ ->capture_default_str ();
224+
225+ // Input
226+ auto *input_group = app.add_option_group (" Input" , " Prompt configuration" );
189227 std::string prompt;
190- unsigned long long rng_seed = 0 ;
191- std::string mode = " generate" ;
228+ input_group->add_option (" -p,--prompt" , prompt, " Initial input prompt for the model" );
192229 std::string system_prompt;
193-
194- // Distributed args
195- std::string dist_mode_str = " single" ;
196- std::string ip = " 0.0.0.0" ;
197- std::string master_ip = " " ;
230+ input_group->add_option (" --system" , system_prompt, " System prompt for chat mode" );
231+
232+ // Distributed Inference
233+ auto *dist_group = app.add_option_group (" Distributed" , " Configuration for multi-node pipeline parallelism" );
234+ std::string role = " single" ;
235+ dist_group->add_option (" --role" , role, " Node role in the cluster" )
236+ ->check (CLI::IsMember ({" single" , " master" , " worker" }))
237+ ->capture_default_str ();
238+ std::string transport_type = " tcp" ;
239+ dist_group->add_option (" --transport" , transport_type, " Network transport protocol" )
240+ ->check (CLI::IsMember ({" tcp" , " udp" , " kernel" }))
241+ ->capture_default_str ();
242+
243+ std::string host = " 0.0.0.0" ;
244+ dist_group->add_option (" --host" , host, " Local IP to bind for incoming connections (Workers)" )
245+ ->capture_default_str ();
198246 int port = 9999 ;
199- int split_layer = 0 ;
200- std::string next_ip = " " ;
247+ dist_group->add_option (" --port" , port, " Local port to bind for incoming connections (Workers)" )
248+ ->check (CLI::Range (1 , 65535 ))
249+ ->capture_default_str ();
250+
251+ std::string next_host = " " ;
252+ dist_group->add_option (" --next-host" , next_host, " IP address of the next node in the pipeline" );
201253 int next_port = 0 ;
254+ dist_group->add_option (" --next-port" , next_port, " Port of the next node in the pipeline" )
255+ ->check (CLI::Range (0 , 65535 ));
256+
257+ int split_layer = 0 ;
258+ dist_group->add_option (" --split" , split_layer, " Layer index to start processing on this node" )
259+ ->capture_default_str ();
202260 int end_layer = 0 ;
261+ dist_group->add_option (" --end" , end_layer, " Layer index to stop processing (exclusive, 0 = until end)" )
262+ ->capture_default_str ();
203263
204- // --- Options ---
205- app.add_option (" checkpoint" , checkpoint_path, " Path to the model checkpoint file (e.g., model.bin)" )
206- ->required ()
207- ->check (CLI ::ExistingFile);
208-
209- // Group: General
210- auto *gen_opts = app.add_option_group (" General Configuration" );
211- gen_opts->add_option (" -z,--tokenizer" , tokenizer_path, " Path to the tokenizer file" )
212- ->check (CLI ::ExistingFile);
213- gen_opts->add_option (" -m,--mode" , mode, " Inference mode: 'generate' for completion, 'chat' for dialog" )
214- ->check (CLI::IsMember ({" generate" , " chat" }));
215- gen_opts->add_option (" -n,--steps" , steps, " Maximum number of steps to run (0 = max_seq_len)" );
216- gen_opts->add_option (" -s,--seed" , rng_seed, " Random seed (0 = use time)" );
217-
218- // Group: Sampling
219- auto *sample_opts = app.add_option_group (" Sampling Parameters" );
220- sample_opts->add_option (" -t,--temperature" , temperature, " Temperature for sampling [0.0, inf)" )
221- ->check (CLI ::NonNegativeNumber);
222- sample_opts->add_option (" -p,--top-p" , topp, " Top-P (Nucleus) sampling probability [0.0, 1.0]" )
223- ->check (CLI::Range (0.0 , 1.0 ));
224-
225- // Group: Input
226- auto *input_opts = app.add_option_group (" Input" );
227- input_opts->add_option (" -i,--prompt" , prompt, " Initial user prompt" );
228- input_opts->add_option (" -y,--system-prompt" , system_prompt, " System prompt (only used in chat mode)" );
229-
230- // Group: Distributed
231- auto *dist_opts = app.add_option_group (" Distributed Inference" , " Configuration for multi-node inference" );
232- dist_opts->add_option (" --dist" , dist_mode_str, " Distributed mode" )
233- ->check (CLI::IsMember ({" single" , " master" , " worker" , " master-udp" , " worker-udp" , " worker-kernel" }));
234-
235- dist_opts->add_option (" --ip" , ip, " Bind IP address (Worker only). Default: 0.0.0.0" );
236- dist_opts->add_option (" --port" , port, " Bind Port (Worker only). Default: 9999" )
237- ->check (CLI::Range (1 , 65535 ));
238- dist_opts->add_option (" --split" , split_layer, " Layer index to split at (start layer for worker)" );
239- dist_opts->add_option (" --end-layer" , end_layer, " Layer index to stop at (exclusive, for worker). Default: n_layers" );
240-
241- dist_opts->add_option (" --master-ip" , master_ip, " Master IP address (required for worker-kernel mode)" );
242-
243- dist_opts->add_option (" --next-ip" , next_ip, " Target IP address for outgoing connection (Master -> Worker 1, Worker -> Next)" );
244- dist_opts->add_option (" --next-port" , next_port, " Target Port for outgoing connection" )
245- ->check (CLI::Range (0 , 65535 )); // 0 means unset
264+ std::string master_host = " " ;
265+ dist_group->add_option (" --master-host" , master_host, " Master IP address (required for kernel transport)" );
246266
247267 CLI11_PARSE (app, argc, argv);
248268
249- // --- Post-Processing / Defaults ---
250- if (rng_seed <= 0 ) rng_seed = static_cast <unsigned int >(std::time (nullptr ));
251- if (steps < 0 ) steps = 0 ;
269+ // Post-processing
270+ if (chat_mode) mode = " chat" ;
271+ if (rng_seed == 0 ) rng_seed = static_cast <unsigned int >(std::time (nullptr ));
272+ if (n_predict < 0 ) n_predict = 0 ;
252273
253274 try {
254- auto transformer = Transformer::create (checkpoint_path );
275+ auto transformer = Transformer::create (model_path );
255276
256- std::cout << " Model loaded successfully. " << std::endl;
257- std::cout << " Config: [ "
277+ std::cout << " Model loaded: " << model_path << std::endl;
278+ std::cout << " Architecture: "
258279 << transformer->config .n_layers << " layers, "
259280 << transformer->config .dim << " dim, "
260281 << transformer->config .n_heads << " heads, "
261282 << transformer->config .vocab_size << " vocab, "
262- << transformer->config .seq_len << " seq_len] " << std::endl;
283+ << transformer->config .seq_len << " context " << std::endl;
263284
264- if (steps == 0 || steps > transformer->config .seq_len ) {
265- steps = transformer->config .seq_len ;
285+ if (n_predict == 0 || n_predict > transformer->config .seq_len ) {
286+ n_predict = transformer->config .seq_len ;
266287 }
267288
268289 if (end_layer == 0 ) end_layer = transformer->config .n_layers ;
269290
270291 // Setup Distributed Mode
271- DistributedMode dist_mode = DistributedMode::Single;
292+ DistributedMode dist_role = DistributedMode::Single;
293+ if (role == " master" ) dist_role = DistributedMode::Master;
294+ else if (role == " worker" ) dist_role = DistributedMode::Worker;
295+
272296 std::unique_ptr<Transport> transport = nullptr ;
273297
274- if (dist_mode_str == " master" ) {
275- dist_mode = DistributedMode::Master;
298+ if (dist_role == DistributedMode::Master) {
276299 if (split_layer <= 0 || split_layer >= transformer->config .n_layers ) {
277- std::cerr << " Error: Invalid split layer for master mode. Must be > 0 and < n_layers." << std::endl;
278- return 1 ;
300+ throw std::runtime_error (" Invalid --split layer for master. Must be > 0 and < n_layers." );
279301 }
280- if (next_ip.empty () || next_port == 0 ) {
281- std::cerr << " Error: Master mode requires --next-ip and --next-port to connect to the first worker." << std::endl;
282- return 1 ;
302+ if (next_host.empty () || next_port == 0 ) {
303+ throw std::runtime_error (" Master role requires --next-host and --next-port to connect to workers." );
283304 }
284305
285- transport = std::make_unique<TcpTransport>(next_ip, next_port, false , " " , 0 );
286- transport->initialize ();
287- } else if (dist_mode_str == " worker" ) {
288- dist_mode = DistributedMode::Worker;
289- if (split_layer <= 0 || split_layer >= transformer->config .n_layers ) {
290- std::cerr << " Error: Invalid split layer for worker mode." << std::endl;
291- return 1 ;
306+ if (transport_type == " tcp" ) {
307+ transport = std::make_unique<TcpTransport>(next_host, next_port, false , " " , 0 );
308+ } else if (transport_type == " udp" ) {
309+ transport = std::make_unique<UdpTransport>(host, port, false , next_host, next_port);
310+ } else {
311+ throw std::runtime_error (" Master role does not support 'kernel' transport (Workers only)." );
292312 }
293- transport = std::make_unique<TcpTransport>(ip, port, true , next_ip, next_port);
294- transport->initialize ();
295- } else if (dist_mode_str == " udp" ) {
296- std::cerr << " Error: Use specific modes: master-udp, worker-udp, worker-kernel" << std::endl;
297- return 1 ;
298- } else if (dist_mode_str == " master-udp" ) {
299- dist_mode = DistributedMode::Master;
300- if (next_ip.empty () || next_port == 0 ) {
301- std::cerr << " Error: Master UDP mode requires --next-ip and --next-port." << std::endl;
302- return 1 ;
313+ } else if (dist_role == DistributedMode::Worker) {
314+ if (split_layer <= 0 || split_layer >= transformer->config .n_layers ) {
315+ throw std::runtime_error (" Invalid --split layer for worker." );
303316 }
304- // Bind to local ip:port, send to next_ip:next_port
305- transport = std::make_unique<UdpTransport>(ip, port, false , next_ip, next_port);
306- transport->initialize ();
307- } else if (dist_mode_str == " worker-udp" ) {
308- dist_mode = DistributedMode::Worker;
309- transport = std::make_unique<UdpTransport>(ip, port, true , next_ip, next_port);
310- transport->initialize ();
311- } else if (dist_mode_str == " worker-kernel" ) {
317+
318+ if (transport_type == " tcp" ) {
319+ transport = std::make_unique<TcpTransport>(host, port, true , next_host, next_port);
320+ } else if (transport_type == " udp" ) {
321+ transport = std::make_unique<UdpTransport>(host, port, true , next_host, next_port);
322+ } else if (transport_type == " kernel" ) {
312323#ifndef __linux__
313- std::cerr << " Error: --dist worker-kernel is only supported on Linux." << std::endl;
314- return 1 ;
324+ throw std::runtime_error (" Kernel transport is only supported on Linux." );
315325#else
316- dist_mode = DistributedMode::Worker;
317- std::string target_ip = master_ip.empty () ? ip : master_ip;
318- transport = std::make_unique<KernelTransport>(target_ip, port, next_ip, next_port);
319- transport->initialize ();
326+ std::string target = master_host.empty () ? host : master_host;
327+ transport = std::make_unique<KernelTransport>(target, port, next_host, next_port);
320328#endif
321- } else if (dist_mode_str != " single" ) {
322- // CLI11 validation covers this, but safe fallback
323- std::cerr << " Error: Unknown distributed mode: " << dist_mode_str << std::endl;
324- return 1 ;
329+ }
330+ }
331+
332+ if (transport) {
333+ transport->initialize ();
325334 }
326335
327336 DistributedConfig dist_config;
328- dist_config.mode = dist_mode ;
337+ dist_config.mode = dist_role ;
329338 dist_config.split_layer = split_layer;
330339 dist_config.end_layer = end_layer;
331340 dist_config.transport = transport.get ();
332- dist_config.next_ip = next_ip ;
341+ dist_config.next_ip = next_host ;
333342 dist_config.next_port = next_port;
334- dist_config.is_tail = (dist_mode == DistributedMode::Worker && next_ip .empty ());
343+ dist_config.is_tail = (dist_role == DistributedMode::Worker && next_host .empty ());
335344
336345 transformer->set_distributed_config (dist_config);
337346
338- if (dist_mode == DistributedMode::Worker) {
347+ if (dist_role == DistributedMode::Worker) {
348+ std::cout << " Node started as Worker [Layers " << split_layer << " to " << end_layer << " ]" << std::endl;
339349 transformer->worker_loop ();
340350 return 0 ;
341351 }
@@ -345,12 +355,12 @@ int main(int argc, char *argv[]) {
345355 Sampler sampler (transformer->config .vocab_size , temperature, topp, rng_seed);
346356
347357 if (mode == " generate" ) {
348- generate (transformer.get (), &tokenizer, &sampler, prompt, steps );
358+ generate (transformer.get (), &tokenizer, &sampler, prompt, n_predict );
349359 } else if (mode == " chat" ) {
350- chat (transformer.get (), &tokenizer, &sampler, prompt, system_prompt, steps );
360+ chat (transformer.get (), &tokenizer, &sampler, prompt, system_prompt, n_predict );
351361 }
352362 } catch (const std::exception &e) {
353- std::cerr << " Runtime Error: " << e.what () << std::endl;
363+ std::cerr << " \n [ Error] " << e.what () << std::endl;
354364 return 1 ;
355365 }
356366
0 commit comments