subagent headers: leave out a model or effort a header cannot hold
The values come from the agent's start request. One with a line break made async-nats panic inside the publish task, which would have stopped every subagent's rows. They are now trimmed, and skipped when empty, over 64 characters, or not a valid header value.
This commit is contained in:
parent
da5fcbfd9b
commit
0999cffdaf
1 changed files with 44 additions and 3 deletions
|
|
@ -84,19 +84,36 @@ pub struct RunInfo {
|
|||
}
|
||||
|
||||
impl RunInfo {
|
||||
/// The headers for one row: only the values there are.
|
||||
/// The headers for one row: only the values there are, and only ones a
|
||||
/// header can hold. The values come from the agent's start request, so one
|
||||
/// with a line break (which `async_nats` panics on) or an absurd length is
|
||||
/// left out rather than published.
|
||||
fn headers(&self) -> async_nats::HeaderMap {
|
||||
let mut headers = async_nats::HeaderMap::new();
|
||||
if let Some(model) = self.model.as_deref().filter(|v| !v.is_empty()) {
|
||||
if let Some(model) = header_value(self.model.as_deref()) {
|
||||
headers.insert(subagent_term::HEADER_MODEL, model);
|
||||
}
|
||||
if let Some(effort) = self.effort.as_deref().filter(|v| !v.is_empty()) {
|
||||
if let Some(effort) = header_value(self.effort.as_deref()) {
|
||||
headers.insert(subagent_term::HEADER_EFFORT, effort);
|
||||
}
|
||||
headers
|
||||
}
|
||||
}
|
||||
|
||||
/// The longest model or effort name published. Real ones are a few
|
||||
/// characters; the value rides on every row and is shown in the UI.
|
||||
const MAX_VALUE_LEN: usize = 64;
|
||||
|
||||
/// `value` trimmed, as a header value, or `None` when it is empty, too long, or
|
||||
/// not one a header can carry.
|
||||
fn header_value(value: Option<&str>) -> Option<async_nats::HeaderValue> {
|
||||
let value = value?.trim();
|
||||
if value.is_empty() || value.len() > MAX_VALUE_LEN {
|
||||
return None;
|
||||
}
|
||||
value.parse().ok()
|
||||
}
|
||||
|
||||
struct Line {
|
||||
subagent: String,
|
||||
info: Arc<RunInfo>,
|
||||
|
|
@ -486,6 +503,30 @@ mod tests {
|
|||
assert!(info.headers().is_empty());
|
||||
}
|
||||
|
||||
/// The values are the caller's. A line break would panic `async_nats`, in
|
||||
/// the task that publishes every subagent's rows.
|
||||
#[test]
|
||||
fn a_value_a_header_cannot_hold_is_left_out_and_never_panics() {
|
||||
for bad in ["a\nb", "high\r\nX: y", &"x".repeat(MAX_VALUE_LEN + 1)] {
|
||||
let info = RunInfo {
|
||||
model: Some(bad.to_owned()),
|
||||
effort: Some(bad.to_owned()),
|
||||
};
|
||||
assert!(info.headers().is_empty(), "{bad:?}");
|
||||
}
|
||||
let padded = RunInfo {
|
||||
model: Some(" opus\n".to_owned()),
|
||||
effort: None,
|
||||
};
|
||||
assert_eq!(
|
||||
padded
|
||||
.headers()
|
||||
.get(subagent_term::HEADER_MODEL)
|
||||
.map(ToString::to_string),
|
||||
Some("opus".to_owned())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn every_kind_of_line_renders_as_stamped_rows() {
|
||||
let mut ctx = ClassifyCtx::default();
|
||||
|
|
|
|||
Loading…
Reference in a new issue