2026-03-21 22:21:14 +00:00
|
|
|
pub mod bridge;
|
feat: initial Sol virtual librarian implementation
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.
2026-03-20 21:40:13 +00:00
|
|
|
pub mod room_history;
|
|
|
|
|
pub mod room_info;
|
2026-03-21 15:51:31 +00:00
|
|
|
pub mod script;
|
feat: initial Sol virtual librarian implementation
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.
2026-03-20 21:40:13 +00:00
|
|
|
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;
|
2026-03-21 15:51:31 +00:00
|
|
|
use crate::context::ResponseContext;
|
feat: initial Sol virtual librarian implementation
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.
2026-03-20 21:40:13 +00:00
|
|
|
|
|
|
|
|
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"]
|
|
|
|
|
}),
|
|
|
|
|
),
|
2026-03-21 15:51:31 +00:00
|
|
|
Tool::new(
|
|
|
|
|
"run_script".into(),
|
|
|
|
|
"Execute a TypeScript/JavaScript snippet in a sandboxed runtime. \
|
|
|
|
|
Use this for math, date calculations, data transformations, or any \
|
|
|
|
|
computation that needs precision. The script has access to:\n\
|
|
|
|
|
- sol.search(query, opts?) — search the message archive. opts: \
|
|
|
|
|
{ room?, sender?, after?, before?, limit?, semantic? }\n\
|
|
|
|
|
- sol.rooms() — list joined rooms (returns array of {name, id, members})\n\
|
|
|
|
|
- sol.members(roomName) — get room members (returns array of {name, id})\n\
|
|
|
|
|
- sol.fetch(url) — HTTP GET (allowlisted domains only)\n\
|
|
|
|
|
- sol.memory.get(query?) — retrieve internal notes relevant to the query\n\
|
|
|
|
|
- sol.memory.set(content, category?) — save an internal note for later reference\n\
|
|
|
|
|
- sol.fs.read(path), sol.fs.write(path, content), sol.fs.list(path?) — \
|
|
|
|
|
sandboxed temp filesystem for intermediate files\n\
|
|
|
|
|
- console.log() to produce output\n\
|
|
|
|
|
All sol.* methods are async — use await. The last expression value is \
|
|
|
|
|
also captured. Output is truncated to 4096 chars."
|
|
|
|
|
.into(),
|
|
|
|
|
json!({
|
|
|
|
|
"type": "object",
|
|
|
|
|
"properties": {
|
|
|
|
|
"code": {
|
|
|
|
|
"type": "string",
|
|
|
|
|
"description": "TypeScript or JavaScript code to execute"
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"required": ["code"]
|
|
|
|
|
}),
|
|
|
|
|
),
|
feat: initial Sol virtual librarian implementation
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.
2026-03-20 21:40:13 +00:00
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-21 22:21:14 +00:00
|
|
|
/// Convert Sol's tool definitions to Mistral AgentTool format
|
|
|
|
|
/// for use with the Agents API (orchestrator agent creation).
|
|
|
|
|
pub fn agent_tool_definitions() -> Vec<mistralai_client::v1::agents::AgentTool> {
|
|
|
|
|
Self::tool_definitions()
|
|
|
|
|
.into_iter()
|
|
|
|
|
.map(|t| {
|
|
|
|
|
mistralai_client::v1::agents::AgentTool::function(
|
|
|
|
|
t.function.name,
|
|
|
|
|
t.function.description,
|
|
|
|
|
t.function.parameters,
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
.collect()
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-21 15:51:31 +00:00
|
|
|
pub async fn execute(
|
|
|
|
|
&self,
|
|
|
|
|
name: &str,
|
|
|
|
|
arguments: &str,
|
|
|
|
|
response_ctx: &ResponseContext,
|
|
|
|
|
) -> anyhow::Result<String> {
|
feat: initial Sol virtual librarian implementation
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.
2026-03-20 21:40:13 +00:00
|
|
|
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,
|
2026-03-21 15:51:31 +00:00
|
|
|
"run_script" => {
|
|
|
|
|
script::run_script(
|
|
|
|
|
&self.opensearch,
|
|
|
|
|
&self.matrix,
|
|
|
|
|
&self.config,
|
|
|
|
|
arguments,
|
|
|
|
|
response_ctx,
|
|
|
|
|
)
|
|
|
|
|
.await
|
|
|
|
|
}
|
feat: initial Sol virtual librarian implementation
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.
2026-03-20 21:40:13 +00:00
|
|
|
_ => anyhow::bail!("Unknown tool: {name}"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|