Process a request to completion, returning a stream of [`Step`]s. The consumer must fulfill every [`Step::CallLlm`] before the algorithm can continue. The bounded step channel applies backpressure when the consumer is not polling. A successful run ends with [`Step::ReturnToAgent`]; a failure is emitted as an `Err` item. Dropping the stream aborts the spawned algorithm task. Every invocation owns
(
self: Arc<Self>,
ctx: Context,
request: Request,
observer: Option<RunObserver>,
)
| 648 | /// Every invocation owns a separate [`Driver`]. `observer`, when present, receives |
| 649 | /// each completed model call and, after a successful routed run, its routing overhead. |
| 650 | fn run_stream( |
| 651 | self: Arc<Self>, |
| 652 | ctx: Context, |
| 653 | request: Request, |
| 654 | observer: Option<RunObserver>, |
| 655 | ) -> StepStream { |
| 656 | // Stamp the algorithm's telemetry label into the request context; the |
| 657 | // context rides on every driver call, so its telemetry is attributed. |
| 658 | let mut ctx = ctx; |
| 659 | ctx.values.insert( |
| 660 | observability::ALGORITHM_KEY.to_string(), |
| 661 | self.name().to_string(), |
| 662 | ); |
| 663 | let driver = Driver::with_observer(observer); |
| 664 | let task_driver = driver.clone(); |
| 665 | let task_ctx = ctx.clone(); |
| 666 | let stream = task_driver.stream(); |
| 667 | // One `libsy.run` span covers the whole algorithm task; the driver's |
| 668 | // `libsy.llm_call` spans and decision logs nest inside it via `tracing`'s |
| 669 | // contextual parenting. |
| 670 | let span = observability::run_span(self.name(), &request); |
| 671 | let observed_driver = task_driver.clone(); |
| 672 | let handle = tokio::spawn( |
| 673 | async move { |
| 674 | observability::observe_run( |
| 675 | task_ctx.clone(), |
| 676 | observed_driver, |
| 677 | self.create_run_task(task_ctx, task_driver, request), |
| 678 | ) |
| 679 | .await |
| 680 | } |
| 681 | .instrument(span), |
| 682 | ); |
| 683 | // Dropping the stream aborts the algorithm task when its consumer goes away. |
| 684 | let abort_guard = AbortOnDrop(handle.abort_handle()); |
| 685 | |
| 686 | let finish_driver = driver.clone(); |
| 687 | let finish_ctx = ctx; |
| 688 | let tail: StepStream = Box::pin( |
| 689 | futures::stream::once(async move { |
| 690 | let result = match handle.await { |
| 691 | Ok(response) => response, |
| 692 | Err(source) => Err(LibsyError::AlgorithmTask { source }), |
| 693 | }; |
| 694 | finish_driver.finish(finish_ctx, result).await |
| 695 | }) |
| 696 | .filter_map(|finish_result| async move { finish_result.err().map(Err) }), |
| 697 | ); |
| 698 | |
| 699 | let stream: StepStream = Box::pin(stream); |
| 700 | Box::pin(futures::stream::select(stream, tail).map(move |step| { |
| 701 | // link abort guard to stream |
| 702 | let _keep_alive = &abort_guard; |
| 703 | step |
| 704 | })) |
| 705 | } |
| 706 | |
| 707 | /// Process a request to completion, returning the final [`Response`] and the trace of |