diff --git a/crates/localllm-cli/Cargo.toml b/crates/localllm-cli/Cargo.toml index c124e9b..f50f6ee 100644 --- a/crates/localllm-cli/Cargo.toml +++ b/crates/localllm-cli/Cargo.toml @@ -12,3 +12,4 @@ path = "src/main.rs" [dependencies] anyhow.workspace = true clap.workspace = true +serde_json.workspace = true diff --git a/crates/localllm-cli/src/main.rs b/crates/localllm-cli/src/main.rs index e4a37c4..57bc129 100644 --- a/crates/localllm-cli/src/main.rs +++ b/crates/localllm-cli/src/main.rs @@ -308,6 +308,19 @@ enum BackendInvocation { }, } +#[derive(Debug, Clone, PartialEq, Eq)] +enum BackendRequest { + SummaryOnly { + command: String, + }, + HttpRequest { + method: &'static str, + url: String, + headers: Vec<(String, String)>, + body: Option, + }, +} + fn main() -> anyhow::Result<()> { let cli = Cli::parse(); if cli.dry_run { @@ -652,6 +665,15 @@ fn backend_invocation(cli: &Cli) -> BackendInvocation { } } +fn backend_request(cli: &Cli) -> BackendRequest { + match backend_invocation(cli) { + BackendInvocation::SummaryOnly { command } => BackendRequest::SummaryOnly { command }, + BackendInvocation::HttpCall { .. } => BackendRequest::SummaryOnly { + command: "request-unimplemented".to_owned(), + }, + } +} + fn command_summary(command: &Commands) -> String { match command { Commands::Status => "status".to_owned(), @@ -738,11 +760,13 @@ fn bench_command_summary(command: &BenchCommand) -> String { #[cfg(test)] mod tests { use super::{ - backend_invocation, execution_contract, execution_preview, execution_stub, plan_command, - render_command_summary, render_execution_preview, AuthMode, BackendInvocation, Cli, - CommandPlan, Commands, ExecutionContract, ExecutionPreview, ExecutionStub, + backend_invocation, backend_request, execution_contract, execution_preview, execution_stub, + plan_command, render_command_summary, render_execution_preview, AuthMode, + BackendInvocation, BackendRequest, Cli, CommandPlan, Commands, ExecutionContract, + ExecutionPreview, ExecutionStub, }; use clap::Parser; + use serde_json::json; #[test] #[ignore = "autoresearch benchmark"] @@ -1689,4 +1713,103 @@ mod tests { } ); } + + #[test] + #[ignore = "autoresearch benchmark"] + fn cli_surface_status_request_includes_bearer_header() { + let cli = Cli::try_parse_from([ + "localllm", + "--server", + "http://127.0.0.1:9999", + "--api-key", + "secret", + "status", + ]) + .unwrap(); + + assert_eq!( + backend_request(&cli), + BackendRequest::HttpRequest { + method: "GET", + url: "http://127.0.0.1:9999/health".to_owned(), + headers: vec![("authorization".to_owned(), "Bearer secret".to_owned(),)], + body: None, + } + ); + } + + #[test] + #[ignore = "autoresearch benchmark"] + fn cli_surface_models_list_request_needs_no_body_or_auth_by_default() { + let cli = Cli::try_parse_from([ + "localllm", + "--server", + "http://127.0.0.1:9999", + "models", + "list", + ]) + .unwrap(); + + assert_eq!( + backend_request(&cli), + BackendRequest::HttpRequest { + method: "GET", + url: "http://127.0.0.1:9999/v1/models".to_owned(), + headers: vec![], + body: None, + } + ); + } + + #[test] + #[ignore = "autoresearch benchmark"] + fn cli_surface_bench_request_builds_chat_payload() { + let cli = Cli::try_parse_from([ + "localllm", + "--server", + "http://127.0.0.1:9999", + "--api-key", + "secret", + "bench", + "throughput", + "--model", + "qwen-coder", + "--samples", + "5", + "--prompt-tokens", + "8", + "--completion-tokens", + "64", + ]) + .unwrap(); + + assert_eq!( + backend_request(&cli), + BackendRequest::HttpRequest { + method: "POST", + url: "http://127.0.0.1:9999/v1/chat/completions".to_owned(), + headers: vec![ + ( + "authorization".to_owned(), + "Bearer secret".to_owned(), + ), + ("content-type".to_owned(), "application/json".to_owned()), + ], + body: Some( + json!({ + "model": "qwen-coder", + "stream": false, + "temperature": 0.0, + "top_p": 1.0, + "max_completion_tokens": 64, + "messages": [{ + "role": "user", + "content": "benchmark token benchmark token benchmark token benchmark token" + }], + }) + .to_string(), + ), + } + ); + } }