use std::sync::Arc;
use async_trait::async_trait;
use oris_runtime::{
agent::{create_agent_with_runtime, Command},
chain::Chain,
error::ToolError,
prompt_args,
tools::{InMemoryStore, SimpleContext, Tool, ToolResult, ToolRuntime},
};
use serde_json::{json, Value};
struct StateAwareTool;
#[async_trait]
impl Tool for StateAwareTool {
fn name(&self) -> String {
"get_conversation_summary".to_string()
}
fn description(&self) -> String {
"Get a summary of the current conversation".to_string()
}
fn requires_runtime(&self) -> bool {
true
}
async fn run(&self, _input: Value) -> Result<String, ToolError> {
Ok("This tool requires runtime".to_string())
}
async fn run_with_runtime(
&self,
_input: Value,
runtime: &ToolRuntime,
) -> Result<ToolResult, Box<dyn std::error::Error>> {
let state = runtime.state().await;
let messages = &state.messages;
let human_count = messages
.iter()
.filter(|m| {
matches!(
m.message_type,
oris_runtime::schemas::MessageType::HumanMessage
)
})
.count();
let ai_count = messages
.iter()
.filter(|m| {
matches!(
m.message_type,
oris_runtime::schemas::MessageType::AIMessage
)
})
.count();
let summary = format!(
"Conversation has {} user messages and {} AI responses",
human_count, ai_count
);
Ok(ToolResult::text(summary))
}
}
struct ContextAwareTool;
#[async_trait]
impl Tool for ContextAwareTool {
fn name(&self) -> String {
"get_user_info".to_string()
}
fn description(&self) -> String {
"Get information about the current user".to_string()
}
fn requires_runtime(&self) -> bool {
true
}
async fn run(&self, _input: Value) -> Result<String, ToolError> {
Ok("This tool requires runtime".to_string())
}
async fn run_with_runtime(
&self,
_input: Value,
runtime: &ToolRuntime,
) -> Result<ToolResult, Box<dyn std::error::Error>> {
let context = runtime.context();
let user_id = context.user_id().unwrap_or("unknown");
let session_id = context.session_id().unwrap_or("none");
Ok(ToolResult::text(format!(
"User ID: {}, Session ID: {}",
user_id, session_id
)))
}
}
struct StoreAwareTool;
#[async_trait]
impl Tool for StoreAwareTool {
fn name(&self) -> String {
"save_preference".to_string()
}
fn description(&self) -> String {
"Save a user preference to persistent storage".to_string()
}
fn parameters(&self) -> Value {
json!({
"type": "object",
"properties": {
"key": {
"type": "string",
"description": "Preference key"
},
"value": {
"type": "string",
"description": "Preference value"
}
},
"required": ["key", "value"]
})
}
fn requires_runtime(&self) -> bool {
true
}
async fn run(&self, _input: Value) -> Result<String, ToolError> {
Ok("This tool requires runtime".to_string())
}
async fn run_with_runtime(
&self,
input: Value,
runtime: &ToolRuntime,
) -> Result<ToolResult, Box<dyn std::error::Error>> {
let key = input["key"]
.as_str()
.ok_or_else(|| ToolError::MissingInput("key".to_string()))?
.to_string();
let value = input["value"]
.as_str()
.ok_or_else(|| ToolError::MissingInput("value".to_string()))?
.to_string();
runtime
.store()
.put(&["preferences"], &key, json!(value))
.await;
Ok(ToolResult::text(format!(
"Saved preference: {} = {}",
key, value
)))
}
}
struct StateUpdateTool;
#[async_trait]
impl Tool for StateUpdateTool {
fn name(&self) -> String {
"set_custom_field".to_string()
}
fn description(&self) -> String {
"Set a custom field in the agent state".to_string()
}
fn parameters(&self) -> Value {
json!({
"type": "object",
"properties": {
"field": {
"type": "string",
"description": "Field name"
},
"value": {
"type": "string",
"description": "Field value"
}
},
"required": ["field", "value"]
})
}
fn requires_runtime(&self) -> bool {
true
}
async fn run(&self, _input: Value) -> Result<String, ToolError> {
Ok("This tool requires runtime".to_string())
}
async fn run_with_runtime(
&self,
input: Value,
runtime: &ToolRuntime,
) -> Result<ToolResult, Box<dyn std::error::Error>> {
let field = input["field"]
.as_str()
.ok_or_else(|| ToolError::MissingInput("field".to_string()))?
.to_string();
let value = input["value"].clone();
let mut state = runtime.state().await;
state.set_field(field.clone(), value.clone());
let command = Command::UpdateState {
fields: {
let mut fields = std::collections::HashMap::new();
fields.insert(field, value);
fields
},
};
Ok(ToolResult::with_command(
format!("Field set successfully"),
command,
))
}
}
#[tokio::main]
async fn main() {
let context = Arc::new(SimpleContext::new().with_user_id("user123".to_string()));
let store = Arc::new(InMemoryStore::new());
let agent = create_agent_with_runtime(
"gpt-4o-mini",
&[
Arc::new(StateAwareTool),
Arc::new(ContextAwareTool),
Arc::new(StoreAwareTool),
Arc::new(StateUpdateTool),
],
Some("You are a helpful assistant with access to runtime information"),
Some(context),
Some(store),
None, None, None, )
.expect("Failed to create agent");
let result = agent
.invoke(prompt_args! {
"input" => "What's my user ID and save a preference with key 'theme' and value 'dark'"
})
.await
.expect("Failed to invoke agent");
println!("Result: {}", result);
}