update: Controllerで入力のValidationを行っていた部分をPod側に移す
This commit is contained in:
@@ -610,19 +610,15 @@ async fn controller_loop<C, St>(
|
||||
});
|
||||
continue;
|
||||
}
|
||||
if let Err(e) = pod.validate_workflow_invocations(&input) {
|
||||
let _ = event_tx.send(Event::Error {
|
||||
code: ErrorCode::InvalidRequest,
|
||||
message: e.to_string(),
|
||||
});
|
||||
continue;
|
||||
}
|
||||
// Broadcast the accepted user message so every
|
||||
// subscriber (including the submitter) can render the
|
||||
// turn header + user line from a single source of
|
||||
// truth. shared_state's `user_segments` is re-synced
|
||||
// from `pod` after the run completes, so we don't push
|
||||
// here.
|
||||
// Broadcast the user message so every subscriber
|
||||
// (including the submitter) can render the turn header
|
||||
// + user line from a single source of truth.
|
||||
// shared_state's `user_segments` is re-synced from
|
||||
// `pod` after the run completes, so we don't push
|
||||
// here. Workflow-invocation validation happens inside
|
||||
// `Pod::run` / `Pod::interrupt_and_run`; on failure the
|
||||
// turn errors out via `Event::Error { InvalidRequest }`
|
||||
// before any UserInput is committed.
|
||||
let _ = event_tx.send(Event::UserMessage {
|
||||
segments: input.clone(),
|
||||
});
|
||||
@@ -945,6 +941,7 @@ fn worker_error_code(e: &PodError) -> ErrorCode {
|
||||
_ => ErrorCode::Internal,
|
||||
},
|
||||
PodError::Provider(_) => ErrorCode::ProviderError,
|
||||
PodError::WorkflowResolve(_) => ErrorCode::InvalidRequest,
|
||||
_ => ErrorCode::Internal,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,14 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
&mut self,
|
||||
input: Vec<Segment>,
|
||||
) -> Result<PodRunResult, PodError> {
|
||||
// Validate before any side effects so a bad workflow slug does
|
||||
// not leave half-applied interrupt prep (orphan closure +
|
||||
// system note) in worker history. `Pod::run` validates again at
|
||||
// its own entry; the duplicate call is cheap (read-only) and
|
||||
// collapses naturally once `interrupt_and_run` folds into
|
||||
// `Pod::run` (see ticket pod-interrupt-prep-internalize).
|
||||
self.validate_workflow_invocations(&input)?;
|
||||
|
||||
let tool_result_summary = self
|
||||
.prompts()
|
||||
.interrupt_tool_result_summary()
|
||||
|
||||
+10
-3
@@ -1136,6 +1136,12 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
/// the Worker is aborted, history is compacted, and execution resumes
|
||||
/// automatically.
|
||||
pub async fn run(&mut self, input: Vec<Segment>) -> Result<PodRunResult, PodError> {
|
||||
// Validate workflow invocations up front so an invalid slug
|
||||
// never commits a UserInput entry, never triggers pre-run
|
||||
// compaction, and never half-applies interrupt prep when run
|
||||
// from `interrupt_and_run`. Read-only against `workflow_registry`.
|
||||
self.validate_workflow_invocations(&input)?;
|
||||
|
||||
self.prepare_for_run().await?;
|
||||
|
||||
// Persist the user input as typed segments before the worker
|
||||
@@ -1394,9 +1400,10 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
}
|
||||
|
||||
/// Validate explicit workflow invocations without reading dependency
|
||||
/// bodies. Used by the controller before broadcasting `UserMessage` so
|
||||
/// user-invocation errors are returned immediately and never reach the
|
||||
/// Worker or client history.
|
||||
/// bodies. Called from `Pod::run` / `Pod::interrupt_and_run` entry so
|
||||
/// an invalid slug aborts the turn before any session-log commit or
|
||||
/// interrupt-prep side effects; `pub` so completion / preview paths
|
||||
/// can also dry-check inputs.
|
||||
pub fn validate_workflow_invocations(
|
||||
&self,
|
||||
segments: &[Segment],
|
||||
|
||||
Reference in New Issue
Block a user