feat: default tool calls to early dispatch
This commit is contained in:
@@ -1348,35 +1348,78 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tool_call_dispatch_defaults_merges_and_round_trips() {
|
||||
fn tool_call_dispatch_default_merge_and_snapshots() {
|
||||
assert_eq!(
|
||||
EngineManifestConfig::default().tool_call_dispatch,
|
||||
None,
|
||||
"partial configs must preserve omission until final resolution"
|
||||
);
|
||||
|
||||
let manifest: WorkerManifest = minimal_valid().try_into().unwrap();
|
||||
assert_eq!(
|
||||
manifest.engine.tool_call_dispatch,
|
||||
crate::ToolCallDispatchMode::AfterResponse
|
||||
crate::ToolCallDispatchMode::OnToolCallComplete
|
||||
);
|
||||
|
||||
let lower = WorkerManifestConfig {
|
||||
engine: EngineManifestConfig {
|
||||
tool_call_dispatch: Some(crate::ToolCallDispatchMode::AfterResponse),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
let merged = lower.clone().merge(WorkerManifestConfig::default());
|
||||
assert_eq!(
|
||||
merged.engine.tool_call_dispatch,
|
||||
Some(crate::ToolCallDispatchMode::AfterResponse),
|
||||
"an omitted upper profile must not replace a lower explicit value"
|
||||
);
|
||||
let overridden = lower.merge(WorkerManifestConfig {
|
||||
engine: EngineManifestConfig {
|
||||
tool_call_dispatch: Some(crate::ToolCallDispatchMode::OnToolCallComplete),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
let upper = WorkerManifestConfig::default();
|
||||
});
|
||||
assert_eq!(
|
||||
lower.merge(upper).engine.tool_call_dispatch,
|
||||
overridden.engine.tool_call_dispatch,
|
||||
Some(crate::ToolCallDispatchMode::OnToolCallComplete)
|
||||
);
|
||||
|
||||
let mut configured = minimal_valid();
|
||||
configured.engine.tool_call_dispatch =
|
||||
let mut explicit_early = minimal_valid();
|
||||
explicit_early.engine.tool_call_dispatch =
|
||||
Some(crate::ToolCallDispatchMode::OnToolCallComplete);
|
||||
let explicit_early: WorkerManifest = explicit_early.try_into().unwrap();
|
||||
assert_eq!(
|
||||
explicit_early.engine.tool_call_dispatch,
|
||||
crate::ToolCallDispatchMode::OnToolCallComplete
|
||||
);
|
||||
|
||||
let mut configured = minimal_valid();
|
||||
configured.engine.tool_call_dispatch = Some(crate::ToolCallDispatchMode::AfterResponse);
|
||||
let manifest: WorkerManifest = configured.try_into().unwrap();
|
||||
let snapshot = serde_json::to_value(&manifest).unwrap();
|
||||
let restored: WorkerManifest = serde_json::from_value(snapshot).unwrap();
|
||||
assert_eq!(
|
||||
manifest.engine.tool_call_dispatch,
|
||||
crate::ToolCallDispatchMode::AfterResponse
|
||||
);
|
||||
let snapshot = crate::write_persisted_worker_manifest_snapshot(&manifest).unwrap();
|
||||
let restored = crate::read_persisted_worker_manifest_snapshot(snapshot.clone()).unwrap();
|
||||
assert_eq!(
|
||||
restored.engine.tool_call_dispatch,
|
||||
crate::ToolCallDispatchMode::OnToolCallComplete
|
||||
crate::ToolCallDispatchMode::AfterResponse,
|
||||
"an explicitly persisted after_response value must survive restore"
|
||||
);
|
||||
|
||||
let mut field_missing = snapshot;
|
||||
field_missing["manifest"]["engine"]
|
||||
.as_object_mut()
|
||||
.unwrap()
|
||||
.remove("tool_call_dispatch");
|
||||
let restored = crate::read_persisted_worker_manifest_snapshot(field_missing).unwrap();
|
||||
assert_eq!(
|
||||
restored.engine.tool_call_dispatch,
|
||||
crate::ToolCallDispatchMode::OnToolCallComplete,
|
||||
"snapshots without the field must use the new default"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -594,10 +594,10 @@ pub struct WorkerMeta {
|
||||
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ToolCallDispatchMode {
|
||||
/// Preserve the historical behavior: wait for the whole response stream.
|
||||
#[default]
|
||||
/// Wait for the whole response stream before starting any tools.
|
||||
AfterResponse,
|
||||
/// Start each call once the provider emits a trustworthy per-call completion.
|
||||
#[default]
|
||||
OnToolCallComplete,
|
||||
}
|
||||
|
||||
@@ -628,7 +628,7 @@ pub struct EngineManifest {
|
||||
pub stop_sequences: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub reasoning: Option<ReasoningControl>,
|
||||
/// Tool-call start timing. Omitted manifests preserve `after_response`.
|
||||
/// Tool-call start timing. Omitted manifests use `on_tool_call_complete`.
|
||||
#[serde(default)]
|
||||
pub tool_call_dispatch: ToolCallDispatchMode,
|
||||
/// Byte-size caps applied to tool `content` before it reaches the
|
||||
|
||||
Reference in New Issue
Block a user