chore: make cli command in simple-chat (#145)
add config,chat,help command
This commit is contained in:
parent
6c2c02454d
commit
5e4d447b32
3 changed files with 116 additions and 70 deletions
|
|
@ -18,3 +18,4 @@ rmcp = { git = "https://github.com/modelcontextprotocol/rust-sdk", features = [
|
||||||
"transport-child-process",
|
"transport-child-process",
|
||||||
"transport-sse",
|
"transport-sse",
|
||||||
], no-default-features = true }
|
], no-default-features = true }
|
||||||
|
clap = { version = "4.0", features = ["derive"] }
|
||||||
|
|
@ -3,13 +3,13 @@
|
||||||
A simple chat client implementation using the Model Context Protocol (MCP) SDK. It just a example for developers to understand how to use the MCP SDK. This example use the easiest way to start a MCP server, and call the tool directly. No need embedding or complex third library or function call(because some models can't support function call).Just add tool in system prompt, and the client will call the tool automatically.
|
A simple chat client implementation using the Model Context Protocol (MCP) SDK. It just a example for developers to understand how to use the MCP SDK. This example use the easiest way to start a MCP server, and call the tool directly. No need embedding or complex third library or function call(because some models can't support function call).Just add tool in system prompt, and the client will call the tool automatically.
|
||||||
|
|
||||||
|
|
||||||
## Config
|
|
||||||
the config file is in `src/config.toml`. you can change the config to your own.Move the config file to `/etc/simple-chat-client/config.toml` for system-wide configuration.
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
After configuring the config file, you can run the example:
|
After configuring the config file, you can run the example:
|
||||||
```bash
|
```bash
|
||||||
cargo run --bin simple_chat
|
./simple_chat --help # show help info
|
||||||
|
./simple_chat config > config.toml # output default config to file
|
||||||
|
./simple_chat chat --config my_config.toml # start chat with specified config
|
||||||
|
./simple_chat chat --config my_config.toml --model gpt-4o-mini # start chat with specified model
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use std::sync::Arc;
|
use std::{process::exit, sync::Arc};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use clap::{Parser, Subcommand};
|
||||||
use simple_chat_client::{
|
use simple_chat_client::{
|
||||||
chat::ChatSession,
|
chat::ChatSession,
|
||||||
client::OpenAIClient,
|
client::OpenAIClient,
|
||||||
|
|
@ -8,13 +9,54 @@ use simple_chat_client::{
|
||||||
tool::{Tool, ToolSet, get_mcp_tools},
|
tool::{Tool, ToolSet, get_mcp_tools},
|
||||||
};
|
};
|
||||||
|
|
||||||
//default config path
|
#[derive(Parser)]
|
||||||
const DEFAULT_CONFIG_PATH: &str = "/etc/simple-chat-client/config.toml";
|
#[command(author, version, about = "Simple Chat Client")]
|
||||||
|
struct Cli {
|
||||||
|
/// Config file path
|
||||||
|
#[arg(short, long, value_name = "FILE")]
|
||||||
|
config: Option<String>,
|
||||||
|
|
||||||
|
#[command(subcommand)]
|
||||||
|
command: Commands,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subcommand)]
|
||||||
|
enum Commands {
|
||||||
|
/// Output default config template
|
||||||
|
Config,
|
||||||
|
|
||||||
|
/// Start chat
|
||||||
|
Chat {
|
||||||
|
/// Specify the model name
|
||||||
|
#[arg(short, long)]
|
||||||
|
model: Option<String>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
|
let cli = Cli::parse();
|
||||||
|
|
||||||
|
match cli.command {
|
||||||
|
Commands::Config => {
|
||||||
|
println!("{}", include_str!("../config.toml"));
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Commands::Chat { model } => {
|
||||||
// load config
|
// load config
|
||||||
let config = Config::load(DEFAULT_CONFIG_PATH).await?;
|
let config_path = cli.config;
|
||||||
|
let mut config = match config_path {
|
||||||
|
Some(path) => Config::load(&path).await?,
|
||||||
|
None => {
|
||||||
|
println!("No config file provided, using default config");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// if command line specify model, override config file setting
|
||||||
|
if let Some(model_name) = model {
|
||||||
|
config.model_name = Some(model_name);
|
||||||
|
}
|
||||||
|
|
||||||
// create openai client
|
// create openai client
|
||||||
let api_key = config
|
let api_key = config
|
||||||
|
|
@ -22,23 +64,23 @@ async fn main() -> Result<()> {
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap_or_else(|| std::env::var("OPENAI_API_KEY").expect("need set api key"));
|
.unwrap_or_else(|| std::env::var("OPENAI_API_KEY").expect("need set api key"));
|
||||||
let url = config.chat_url.clone();
|
let url = config.chat_url.clone();
|
||||||
println!("url is {:?}", url);
|
println!("use api address: {:?}", url);
|
||||||
let openai_client = Arc::new(OpenAIClient::new(api_key, url, config.proxy));
|
let openai_client = Arc::new(OpenAIClient::new(api_key, url, config.proxy));
|
||||||
|
|
||||||
// create tool set
|
// create tool set
|
||||||
let mut tool_set = ToolSet::default();
|
let mut tool_set = ToolSet::default();
|
||||||
|
|
||||||
// load mcp
|
// load MCP
|
||||||
if config.mcp.is_some() {
|
if config.mcp.is_some() {
|
||||||
let mcp_clients = config.create_mcp_clients().await?;
|
let mcp_clients = config.create_mcp_clients().await?;
|
||||||
|
|
||||||
for (name, client) in mcp_clients {
|
for (name, client) in mcp_clients {
|
||||||
println!("loading mcp tools: {}", name);
|
println!("load MCP tool: {}", name);
|
||||||
let server = client.peer().clone();
|
let server = client.peer().clone();
|
||||||
let tools = get_mcp_tools(server).await?;
|
let tools = get_mcp_tools(server).await?;
|
||||||
|
|
||||||
for tool in tools {
|
for tool in tools {
|
||||||
println!("adding tool: {}", tool.name());
|
println!("add tool: {}", tool.name());
|
||||||
tool_set.add_tool(tool);
|
tool_set.add_tool(tool);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -53,7 +95,7 @@ async fn main() -> Result<()> {
|
||||||
.unwrap_or_else(|| "gpt-4o-mini".to_string()),
|
.unwrap_or_else(|| "gpt-4o-mini".to_string()),
|
||||||
);
|
);
|
||||||
|
|
||||||
// build system prompt with tool info
|
// build system prompt
|
||||||
let mut system_prompt =
|
let mut system_prompt =
|
||||||
"you are a assistant, you can help user to complete various tasks. you have the following tools to use:\n".to_string();
|
"you are a assistant, you can help user to complete various tasks. you have the following tools to use:\n".to_string();
|
||||||
|
|
||||||
|
|
@ -63,7 +105,8 @@ async fn main() -> Result<()> {
|
||||||
"\ntool name: {}\ndescription: {}\nparameters: {}\n",
|
"\ntool name: {}\ndescription: {}\nparameters: {}\n",
|
||||||
tool.name(),
|
tool.name(),
|
||||||
tool.description(),
|
tool.description(),
|
||||||
serde_json::to_string_pretty(&tool.parameters()).unwrap_or_default()
|
serde_json::to_string_pretty(&tool.parameters())
|
||||||
|
.expect("failed to serialize tool parameters")
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,6 +122,8 @@ async fn main() -> Result<()> {
|
||||||
|
|
||||||
// start chat
|
// start chat
|
||||||
session.chat().await?;
|
session.chat().await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue