Skip to main content

gemini_adk_rs/code_mode/
sandbox.rs

1//! Where programs run: the sandbox interface.
2
3use async_trait::async_trait;
4use serde_json::{Map, Value};
5
6use super::CodeLimits;
7
8/// Where a program has stopped: at a call to one of its functions, or at its
9/// end.
10#[derive(Debug, Clone, PartialEq)]
11pub enum Suspension {
12    /// The program called `function` and waits for its result.
13    Call {
14        /// The function's name.
15        function: String,
16        /// Positional arguments, in order.
17        positional: Vec<Value>,
18        /// Keyword arguments.
19        keywords: Map<String, Value>,
20    },
21    /// The program ended.
22    Finished {
23        /// The value of its last expression, or the error that ended it.
24        result: Result<Value, String>,
25        /// What it printed.
26        printed: String,
27    },
28}
29
30impl Suspension {
31    /// A program that ended with `error` and printed nothing.
32    pub fn failed(error: impl Into<String>) -> Self {
33        Self::Finished {
34            result: Err(error.into()),
35            printed: String::new(),
36        }
37    }
38}
39
40/// A program suspended at a call. Dropping it cancels the program.
41#[async_trait]
42pub trait Execution: Send {
43    /// Resume the program with the result of the call it waits on, or with
44    /// the reason the call failed (raised as `RuntimeError`), and run it to
45    /// its next suspension.
46    async fn resume(&mut self, answer: Result<Value, String>) -> Suspension;
47}
48
49/// Runs programs: Python source with the given functions defined as globals.
50#[async_trait]
51pub trait CodeSandbox: Send + Sync {
52    /// Start `program` with each of `functions` defined as a global that
53    /// suspends the program when called. Returns the execution and where it
54    /// first stopped.
55    async fn start(
56        &self,
57        program: &str,
58        functions: &[String],
59        limits: &CodeLimits,
60    ) -> (Box<dyn Execution>, Suspension);
61}
62
63/// A sandbox that refuses every program: the default when this crate is
64/// built without the `code-mode` feature.
65#[derive(Debug, Clone, Copy, Default)]
66pub struct Unavailable;
67
68struct Ended;
69
70/// An execution that has already ended.
71pub(super) fn ended() -> Box<dyn Execution> {
72    Box::new(Ended)
73}
74
75#[async_trait]
76impl Execution for Ended {
77    async fn resume(&mut self, _answer: Result<Value, String>) -> Suspension {
78        Suspension::failed("the program has ended")
79    }
80}
81
82#[async_trait]
83impl CodeSandbox for Unavailable {
84    async fn start(
85        &self,
86        _program: &str,
87        _functions: &[String],
88        _limits: &CodeLimits,
89    ) -> (Box<dyn Execution>, Suspension) {
90        (
91            Box::new(Ended),
92            Suspension::failed(
93                "code mode is unavailable: this build has no sandbox (enable the \
94                 `code-mode` feature)",
95            ),
96        )
97    }
98}