swarm-controller: GET inference answers configured: null without a key
`GET /api/agents/{name}/inference` answered `"configured": {}` both for
an agent with no key in `agent-inference` and for a stored value with no
fields, so a reader could not tell "the swarm sets nothing" from "a
setting with every field unset". It now reads the key as
`Option<AgentInference>`: an absent or deleted key is `null`, a stored
`{}` stays `{}`. The shared `schedules::read` helper is unchanged.
`swarmctl agent inference get` prints "no swarm setting; the agent runs
on its own model and effort" for `null`; `set` merges into an empty
value in that case, as before.
The agent itself still treats no key and `{}` the same.
Refs #5034
This commit is contained in:
parent
47bd83054f
commit
7364ccdc98
3 changed files with 58 additions and 14 deletions
|
|
@ -30,7 +30,7 @@ struct Settings {
|
|||
/// What `GET` answers: the settings, and what the agent reported running on.
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct View {
|
||||
configured: Settings,
|
||||
configured: Option<Settings>,
|
||||
effective: Option<Effective>,
|
||||
}
|
||||
|
||||
|
|
@ -90,7 +90,7 @@ pub(crate) fn get(socket: &Path, agent: &str) -> Result<()> {
|
|||
))?;
|
||||
print!(
|
||||
"{}{}",
|
||||
render(&view.configured),
|
||||
render_configured(view.configured.as_ref()),
|
||||
render_effective(view.effective.as_ref())
|
||||
);
|
||||
Ok(())
|
||||
|
|
@ -108,7 +108,7 @@ pub(crate) fn set(socket: &Path, agent: &str, flags: SetFlags) -> Result<()> {
|
|||
None::<&()>,
|
||||
"inference-get",
|
||||
))?;
|
||||
let wanted = flags.merge(current.configured)?;
|
||||
let wanted = flags.merge(current.configured.unwrap_or_default())?;
|
||||
let stored: Settings = rt.block_on(send(
|
||||
socket,
|
||||
hyper::Method::PUT,
|
||||
|
|
@ -134,6 +134,14 @@ pub(crate) fn clear(socket: &Path, agent: &str) -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// The swarm's setting, or one line saying it has none.
|
||||
fn render_configured(s: Option<&Settings>) -> String {
|
||||
s.map_or_else(
|
||||
|| "no swarm setting; the agent runs on its own model and effort\n".to_owned(),
|
||||
render,
|
||||
)
|
||||
}
|
||||
|
||||
/// One line per field, naming the unset ones.
|
||||
fn render(s: &Settings) -> String {
|
||||
const UNSET: &str = "(not set; the agent's own)";
|
||||
|
|
@ -213,13 +221,24 @@ mod tests {
|
|||
"model_source":"swarm","effort_source":"local_file"}}"#,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(view.configured.model.as_deref(), Some("opus"));
|
||||
assert_eq!(
|
||||
view.configured.as_ref().and_then(|s| s.model.as_deref()),
|
||||
Some("opus")
|
||||
);
|
||||
assert_eq!(
|
||||
render_effective(view.effective.as_ref()),
|
||||
"running model: opus (swarm)\nrunning effort: medium (local_file)\n"
|
||||
);
|
||||
let unknown: View = serde_json::from_str(r#"{"configured":{},"effective":null}"#).unwrap();
|
||||
let unknown: View =
|
||||
serde_json::from_str(r#"{"configured":null,"effective":null}"#).unwrap();
|
||||
assert!(render_configured(unknown.configured.as_ref()).starts_with("no swarm setting"));
|
||||
assert!(render_effective(unknown.effective.as_ref()).starts_with("running: not reported"));
|
||||
// A stored setting with no fields still lists every field as unset.
|
||||
let empty: View = serde_json::from_str(r#"{"configured":{},"effective":null}"#).unwrap();
|
||||
assert_eq!(
|
||||
render_configured(empty.configured.as_ref()).lines().count(),
|
||||
3
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
Loading…
Reference in a new issue