Matrix bot with E2EE (matrix-sdk 0.9) that passively archives all messages to OpenSearch and responds to queries via Mistral AI with function calling tools. Core systems: - Archive: bulk OpenSearch indexer with batch/flush, edit/redaction handling, embedding pipeline passthrough - Brain: rule-based engagement evaluator (mentions, DMs, name invocations), LLM-powered spontaneous engagement, per-room conversation context windows, response delay simulation - Tools: search_archive, get_room_context, list_rooms, get_room_members registered as Mistral function calling tools with iterative tool loop - Personality: templated system prompt with Sol's librarian persona 47 unit tests covering config, evaluator, conversation windowing, personality templates, schema serialization, and search query building.
152 lines
5.5 KiB
Rust
152 lines
5.5 KiB
Rust
pub mod room_history;
|
|
pub mod room_info;
|
|
pub mod search;
|
|
|
|
use std::sync::Arc;
|
|
|
|
use matrix_sdk::Client as MatrixClient;
|
|
use mistralai_client::v1::tool::Tool;
|
|
use opensearch::OpenSearch;
|
|
use serde_json::json;
|
|
|
|
use crate::config::Config;
|
|
|
|
pub struct ToolRegistry {
|
|
opensearch: OpenSearch,
|
|
matrix: MatrixClient,
|
|
config: Arc<Config>,
|
|
}
|
|
|
|
impl ToolRegistry {
|
|
pub fn new(opensearch: OpenSearch, matrix: MatrixClient, config: Arc<Config>) -> Self {
|
|
Self {
|
|
opensearch,
|
|
matrix,
|
|
config,
|
|
}
|
|
}
|
|
|
|
pub fn tool_definitions() -> Vec<Tool> {
|
|
vec![
|
|
Tool::new(
|
|
"search_archive".into(),
|
|
"Search the message archive. Use this to find past conversations, \
|
|
messages from specific people, or about specific topics."
|
|
.into(),
|
|
json!({
|
|
"type": "object",
|
|
"properties": {
|
|
"query": {
|
|
"type": "string",
|
|
"description": "Search query for message content"
|
|
},
|
|
"room": {
|
|
"type": "string",
|
|
"description": "Filter by room name (optional)"
|
|
},
|
|
"sender": {
|
|
"type": "string",
|
|
"description": "Filter by sender display name (optional)"
|
|
},
|
|
"after": {
|
|
"type": "string",
|
|
"description": "Unix timestamp in ms — only messages after this time (optional)"
|
|
},
|
|
"before": {
|
|
"type": "string",
|
|
"description": "Unix timestamp in ms — only messages before this time (optional)"
|
|
},
|
|
"limit": {
|
|
"type": "integer",
|
|
"description": "Max results to return (default 10)"
|
|
},
|
|
"semantic": {
|
|
"type": "boolean",
|
|
"description": "Use semantic search instead of keyword (optional)"
|
|
}
|
|
},
|
|
"required": ["query"]
|
|
}),
|
|
),
|
|
Tool::new(
|
|
"get_room_context".into(),
|
|
"Get messages around a specific point in time or event in a room. \
|
|
Useful for understanding the context of a conversation."
|
|
.into(),
|
|
json!({
|
|
"type": "object",
|
|
"properties": {
|
|
"room_id": {
|
|
"type": "string",
|
|
"description": "The Matrix room ID"
|
|
},
|
|
"around_timestamp": {
|
|
"type": "integer",
|
|
"description": "Unix timestamp in ms to center the context around"
|
|
},
|
|
"around_event_id": {
|
|
"type": "string",
|
|
"description": "Event ID to center the context around"
|
|
},
|
|
"before_count": {
|
|
"type": "integer",
|
|
"description": "Number of messages before the pivot (default 10)"
|
|
},
|
|
"after_count": {
|
|
"type": "integer",
|
|
"description": "Number of messages after the pivot (default 10)"
|
|
}
|
|
},
|
|
"required": ["room_id"]
|
|
}),
|
|
),
|
|
Tool::new(
|
|
"list_rooms".into(),
|
|
"List all rooms Sol is currently in, with names and member counts.".into(),
|
|
json!({
|
|
"type": "object",
|
|
"properties": {}
|
|
}),
|
|
),
|
|
Tool::new(
|
|
"get_room_members".into(),
|
|
"Get the list of members in a specific room.".into(),
|
|
json!({
|
|
"type": "object",
|
|
"properties": {
|
|
"room_id": {
|
|
"type": "string",
|
|
"description": "The Matrix room ID"
|
|
}
|
|
},
|
|
"required": ["room_id"]
|
|
}),
|
|
),
|
|
]
|
|
}
|
|
|
|
pub async fn execute(&self, name: &str, arguments: &str) -> anyhow::Result<String> {
|
|
match name {
|
|
"search_archive" => {
|
|
search::search_archive(
|
|
&self.opensearch,
|
|
&self.config.opensearch.index,
|
|
arguments,
|
|
)
|
|
.await
|
|
}
|
|
"get_room_context" => {
|
|
room_history::get_room_context(
|
|
&self.opensearch,
|
|
&self.config.opensearch.index,
|
|
arguments,
|
|
)
|
|
.await
|
|
}
|
|
"list_rooms" => room_info::list_rooms(&self.matrix).await,
|
|
"get_room_members" => room_info::get_room_members(&self.matrix, arguments).await,
|
|
_ => anyhow::bail!("Unknown tool: {name}"),
|
|
}
|
|
}
|
|
}
|