brokerd: memory calls to memoryd with the session's state; labels only stricter
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
//! 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user