升级组件
This commit is contained in:
parent
1c1b96f5d4
commit
751de27e45
28
Cargo.toml
28
Cargo.toml
@ -4,18 +4,18 @@ version = "1.3.1"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
reqwest = { version = "0.13.3", default-features = false, features = ["json", "rustls", "multipart"] }
|
||||
reqwest = { version = "0.13.4", default-features = false, features = ["json", "rustls", "multipart"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
regex = "1.12"
|
||||
regex = "1.13"
|
||||
serde_json = "1.0"
|
||||
async-trait = "0.1"
|
||||
thiserror = "2.0.18"
|
||||
tokio = { version = "1.52", features = ["full"] }
|
||||
thiserror = "2.0.19"
|
||||
tokio = { version = "1.53", features = ["full"] }
|
||||
tokio-util = { version = "0.7", features = ["rt", "io"] }
|
||||
dashmap = "6.1"
|
||||
uuid = { version = "1.23", features = ["v4"] }
|
||||
dashmap = "6.2"
|
||||
uuid = { version = "1.24", features = ["v4"] }
|
||||
axum = { version = "0.8", features = ["ws", "multipart"] }
|
||||
tokio-tungstenite = { version = "0.29.0", features = ["rustls-tls-webpki-roots", "rustls"] }
|
||||
tokio-tungstenite = { version = "0.30.0", features = ["rustls-tls-webpki-roots", "rustls"] }
|
||||
futures-util = "0.3"
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
dirs = "6.0.0"
|
||||
@ -26,21 +26,21 @@ tracing-appender = "0.2"
|
||||
time = { version = "0.3", features = ["formatting", "local-offset"] }
|
||||
anyhow = "1.0"
|
||||
mime_guess = "2.0"
|
||||
base64 = "0.22"
|
||||
sha2 = "0.10"
|
||||
base64 = "0.23"
|
||||
sha2 = "0.11"
|
||||
tempfile = "3"
|
||||
cron = "0.16"
|
||||
cron = "0.17"
|
||||
chrono-tz = "0.10"
|
||||
ratatui = "0.30"
|
||||
crossterm = { version = "0.29", features = ["event-stream"] }
|
||||
termimad = "0.34"
|
||||
termimad = "0.35"
|
||||
textwrap = "0.16"
|
||||
unicode-width = "0.2"
|
||||
chrono = "0.4"
|
||||
sqlx = { version = "0.8", features = ["sqlite", "macros", "chrono", "runtime-tokio"] }
|
||||
jieba-rs = "0.9"
|
||||
sqlx = { version = "0.9", features = ["sqlite", "macros", "chrono", "runtime-tokio"] }
|
||||
jieba-rs = "0.10"
|
||||
which = "8"
|
||||
rmcp = { version = "1.7", default-features = false, features = [
|
||||
rmcp = { version = "2.2", default-features = false, features = [
|
||||
"client",
|
||||
"transport-child-process",
|
||||
"transport-streamable-http-client-reqwest",
|
||||
|
||||
@ -5,7 +5,7 @@ use std::sync::{Arc, Mutex};
|
||||
|
||||
use anyhow::Context;
|
||||
use http::{HeaderName, HeaderValue};
|
||||
use rmcp::model::{CallToolRequestParams, RawContent};
|
||||
use rmcp::model::{CallToolRequestParams, ContentBlock};
|
||||
use rmcp::transport::streamable_http_client::StreamableHttpClientTransportConfig;
|
||||
use rmcp::transport::{StreamableHttpClientTransport, TokioChildProcess};
|
||||
use rmcp::{Peer, RoleClient, ServiceExt};
|
||||
@ -85,14 +85,14 @@ impl McpConnection {
|
||||
fn extract_text(result: &rmcp::model::CallToolResult) -> String {
|
||||
let mut parts = Vec::new();
|
||||
for content in &result.content {
|
||||
match &**content {
|
||||
RawContent::Text(text) => {
|
||||
match content {
|
||||
ContentBlock::Text(text) => {
|
||||
parts.push(text.text.clone());
|
||||
}
|
||||
RawContent::Image(image) => {
|
||||
ContentBlock::Image(image) => {
|
||||
parts.push(format!("[image: {}]", image.mime_type,));
|
||||
}
|
||||
RawContent::Resource(resource) => match &resource.resource {
|
||||
ContentBlock::Resource(resource) => match &resource.resource {
|
||||
rmcp::model::ResourceContents::TextResourceContents { text, .. } => {
|
||||
parts.push(format!(
|
||||
"[resource text: {}]",
|
||||
@ -102,6 +102,7 @@ fn extract_text(result: &rmcp::model::CallToolResult) -> String {
|
||||
rmcp::model::ResourceContents::BlobResourceContents { uri, .. } => {
|
||||
parts.push(format!("[resource blob: {}]", uri));
|
||||
}
|
||||
_ => parts.push("[unsupported resource]".to_string()),
|
||||
},
|
||||
_ => {
|
||||
parts.push("[unsupported content]".to_string());
|
||||
|
||||
@ -103,8 +103,9 @@ impl super::Storage {
|
||||
let fts_query = jieba()
|
||||
.cut(query, true)
|
||||
.into_iter()
|
||||
.filter(|w| w.len() > 1 || w.bytes().any(|b| b > 127))
|
||||
.map(|w| format!("\"{}\"", w.replace('"', "")))
|
||||
.map(|token| token.word)
|
||||
.filter(|word| word.len() > 1 || word.bytes().any(|b| b > 127))
|
||||
.map(|word| format!("\"{}\"", word.replace('"', "")))
|
||||
.collect::<Vec<_>>()
|
||||
.join(" OR ");
|
||||
|
||||
@ -138,8 +139,9 @@ impl super::Storage {
|
||||
let terms: Vec<String> = jieba()
|
||||
.cut(query, true)
|
||||
.into_iter()
|
||||
.filter(|w| w.len() > 1 || w.bytes().any(|b| b > 127))
|
||||
.map(|w| w.replace(['%', '_'], ""))
|
||||
.map(|token| token.word)
|
||||
.filter(|word| word.len() > 1 || word.bytes().any(|b| b > 127))
|
||||
.map(|word| word.replace(['%', '_'], ""))
|
||||
.collect();
|
||||
|
||||
if !terms.is_empty() {
|
||||
@ -163,7 +165,8 @@ impl super::Storage {
|
||||
like_clauses
|
||||
);
|
||||
|
||||
let mut query_builder = sqlx::query(&sql);
|
||||
// The only interpolated fragment is a generated sequence of bind placeholders.
|
||||
let mut query_builder = sqlx::query(sqlx::AssertSqlSafe(sql));
|
||||
for term in &terms {
|
||||
let pattern = format!("%{}%", term);
|
||||
query_builder = query_builder.bind(pattern.clone()).bind(pattern);
|
||||
@ -205,8 +208,9 @@ impl super::Storage {
|
||||
let terms: Vec<String> = jieba()
|
||||
.cut(q, true)
|
||||
.into_iter()
|
||||
.filter(|w| w.len() > 1 || w.bytes().any(|b| b > 127))
|
||||
.map(|w| w.replace(['%', '_'], ""))
|
||||
.map(|token| token.word)
|
||||
.filter(|word| word.len() > 1 || word.bytes().any(|b| b > 127))
|
||||
.map(|word| word.replace(['%', '_'], ""))
|
||||
.collect();
|
||||
|
||||
if terms.is_empty() {
|
||||
@ -234,7 +238,8 @@ impl super::Storage {
|
||||
like_clauses
|
||||
);
|
||||
|
||||
let mut query_builder = sqlx::query(&sql);
|
||||
// The only interpolated fragment is a generated sequence of bind placeholders.
|
||||
let mut query_builder = sqlx::query(sqlx::AssertSqlSafe(sql));
|
||||
for term in &terms {
|
||||
let pattern = format!("%{}%", term);
|
||||
query_builder = query_builder.bind(pattern.clone()).bind(pattern);
|
||||
|
||||
@ -29,7 +29,7 @@ const INSERT_MESSAGE_SQL: &str = r#"
|
||||
fn insert_message_query<'a>(
|
||||
session_id: &'a str,
|
||||
msg: &'a crate::storage::message::MessageMeta,
|
||||
) -> sqlx::query::Query<'a, Sqlite, sqlx::sqlite::SqliteArguments<'a>> {
|
||||
) -> sqlx::query::Query<'a, Sqlite, sqlx::sqlite::SqliteArguments> {
|
||||
sqlx::query(INSERT_MESSAGE_SQL)
|
||||
.bind(&msg.id)
|
||||
.bind(session_id)
|
||||
@ -424,13 +424,19 @@ impl Storage {
|
||||
("job_runs", "delivery_error", "delivery_error TEXT"),
|
||||
] {
|
||||
let pragma = format!("PRAGMA table_info({table})");
|
||||
let columns = sqlx::query(&pragma).fetch_all(&mut *tx).await?;
|
||||
// `table` comes exclusively from the fixed migration list above.
|
||||
let columns = sqlx::query(sqlx::AssertSqlSafe(pragma))
|
||||
.fetch_all(&mut *tx)
|
||||
.await?;
|
||||
if !columns
|
||||
.iter()
|
||||
.any(|row| row.get::<String, _>("name") == column)
|
||||
{
|
||||
let alter = format!("ALTER TABLE {table} ADD COLUMN {definition}");
|
||||
sqlx::query(&alter).execute(&mut *tx).await?;
|
||||
// All identifiers and definitions come from the fixed migration list above.
|
||||
sqlx::query(sqlx::AssertSqlSafe(alter))
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
@ -461,9 +467,11 @@ impl Storage {
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
sqlx::query(&format!("PRAGMA user_version = {SCHEMA_VERSION}"))
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
sqlx::query(sqlx::AssertSqlSafe(format!(
|
||||
"PRAGMA user_version = {SCHEMA_VERSION}"
|
||||
)))
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
Ok(())
|
||||
}
|
||||
@ -1082,7 +1090,8 @@ impl Storage {
|
||||
where_extra
|
||||
);
|
||||
|
||||
let mut count_query = sqlx::query(&count_sql).bind(session_id);
|
||||
// `where_extra` contains only the fixed timestamp predicates constructed above.
|
||||
let mut count_query = sqlx::query(sqlx::AssertSqlSafe(count_sql)).bind(session_id);
|
||||
if let Some(bt) = before_time {
|
||||
count_query = count_query.bind(bt);
|
||||
}
|
||||
@ -1092,7 +1101,7 @@ impl Storage {
|
||||
let count_row = count_query.fetch_one(self.pool()).await?;
|
||||
let total: i64 = count_row.get("total");
|
||||
|
||||
let mut select_query = sqlx::query(&select_sql).bind(session_id);
|
||||
let mut select_query = sqlx::query(sqlx::AssertSqlSafe(select_sql)).bind(session_id);
|
||||
if let Some(bt) = before_time {
|
||||
select_query = select_query.bind(bt);
|
||||
}
|
||||
@ -1602,7 +1611,7 @@ mod tests {
|
||||
vec!["locked_at", "lock_owner", "lease_until"],
|
||||
),
|
||||
] {
|
||||
let columns = sqlx::query(&format!("PRAGMA table_info({table})"))
|
||||
let columns = sqlx::query(sqlx::AssertSqlSafe(format!("PRAGMA table_info({table})")))
|
||||
.fetch_all(storage.pool())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user