120 lines
4.4 KiB
Rust
120 lines
4.4 KiB
Rust
//! Where decided `recall` and `memory` calls go: to `memoryd` over its `brokerd` socket, with the
|
|
//! session's state, instead of a container (M5a spec, section 8). `Routed` sends every other call
|
|
//! to the configured runtime.
|
|
|
|
use std::os::unix::net::UnixStream;
|
|
use std::path::PathBuf;
|
|
use std::time::Duration;
|
|
|
|
use proto::{
|
|
Envelope, MemoryCall, Message, PROTOCOL_VERSION, ToolRequest, ToolResponse, read_frame,
|
|
write_frame,
|
|
};
|
|
|
|
use crate::policy::Label;
|
|
use crate::runner::{RunError, RunOutput, RunSpec, Runtime};
|
|
|
|
/// The sentence when `memoryd` cannot be reached or answers wrongly: fixed, it reaches the model.
|
|
pub const UNREACHABLE: &str =
|
|
"memory is unavailable: memoryd did not answer (see docs/runbook.md#memoryd-unreachable)";
|
|
|
|
/// How long `brokerd` waits for `memoryd` to answer one call.
|
|
pub const MEMORY_WAIT: Duration = Duration::from_secs(30);
|
|
|
|
/// A client of `memoryd`'s `brokerd` socket. One connection per call.
|
|
pub struct MemoryPort {
|
|
socket: PathBuf,
|
|
wait: Duration,
|
|
}
|
|
|
|
impl MemoryPort {
|
|
pub fn new(socket: PathBuf, wait: Duration) -> MemoryPort {
|
|
MemoryPort { socket, wait }
|
|
}
|
|
|
|
fn ask(&self, spec: &RunSpec) -> Option<ToolResponse> {
|
|
// `None` at the first thing that fails. 1. A MemoryCall: request with the spec's session,
|
|
// call, `spec.tool().as_str()`, and `spec.arguments().canonical_json()`; taint and
|
|
// untrusted from `spec.state()`. 2. `UnixStream::connect(&self.socket)`, then read and
|
|
// write timeouts of `self.wait`. 3. `write_frame` an Envelope { v: PROTOCOL_VERSION, id:
|
|
// spec.call().0, final: true, msg: Message::MemoryCall(call) }. 4. `read_frame`; a
|
|
// Message::ToolResponse is Some(it), any other message None.
|
|
let call = MemoryCall {
|
|
request: ToolRequest {
|
|
session: spec.session().clone(),
|
|
call: spec.call(),
|
|
tool: spec.tool().as_str().to_string(),
|
|
arguments: spec.arguments().canonical_json(),
|
|
},
|
|
taint: spec.state().taint,
|
|
untrusted: spec.state().untrusted,
|
|
};
|
|
let mut stream = UnixStream::connect(&self.socket).ok()?;
|
|
stream.set_read_timeout(Some(self.wait)).ok()?;
|
|
stream.set_write_timeout(Some(self.wait)).ok()?;
|
|
write_frame(
|
|
&mut stream,
|
|
&Envelope {
|
|
v: PROTOCOL_VERSION,
|
|
id: spec.call().0,
|
|
r#final: true,
|
|
msg: Message::MemoryCall(call),
|
|
},
|
|
)
|
|
.ok()?;
|
|
let env = read_frame(&mut stream).ok()?;
|
|
match env.msg {
|
|
Message::ToolResponse(response) => Some(response),
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Runtime for MemoryPort {
|
|
fn run(&self, spec: &RunSpec) -> Result<RunOutput, RunError> {
|
|
self.run_labelled(spec).map(|(output, _)| output)
|
|
}
|
|
|
|
fn run_labelled(&self, spec: &RunSpec) -> Result<(RunOutput, Option<Label>), RunError> {
|
|
// `self.ask(spec)`: Some(Result { content, class, untrusted, truncated }) -> Ok((RunOutput
|
|
// { content, truncated }, Some(Label { class, untrusted }))); Some(Failed { message }) ->
|
|
// Err(RunError::Failed(message)); anything else, None included ->
|
|
// Err(RunError::Unavailable(UNREACHABLE.to_string())).
|
|
match self.ask(spec) {
|
|
Some(ToolResponse::Result {
|
|
content,
|
|
class,
|
|
untrusted,
|
|
truncated,
|
|
}) => Ok((
|
|
RunOutput { content, truncated },
|
|
Some(Label { class, untrusted }),
|
|
)),
|
|
Some(ToolResponse::Failed { message }) => Err(RunError::Failed(message)),
|
|
_ => Err(RunError::Unavailable(UNREACHABLE.to_string())),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// The memory tools to `memoryd`, everything else to `other`.
|
|
pub struct Routed {
|
|
pub memory: MemoryPort,
|
|
pub other: Box<dyn Runtime>,
|
|
}
|
|
|
|
impl Runtime for Routed {
|
|
fn run(&self, spec: &RunSpec) -> Result<RunOutput, RunError> {
|
|
self.run_labelled(spec).map(|(output, _)| output)
|
|
}
|
|
|
|
fn run_labelled(&self, spec: &RunSpec) -> Result<(RunOutput, Option<Label>), RunError> {
|
|
// `spec.tool().is_memory()` -> `self.memory.run_labelled(spec)`; otherwise
|
|
// `self.other.run_labelled(spec)`.
|
|
if spec.tool().is_memory() {
|
|
self.memory.run_labelled(spec)
|
|
} else {
|
|
self.other.run_labelled(spec)
|
|
}
|
|
}
|
|
}
|