vv-agent 0.7.2

VectorVein agent runtime, SDK, CLI, tools, and workspace backends
Documentation
use std::collections::BTreeMap;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Mutex};
use std::time::Duration;

use serde_json::{json, Value};

use vv_agent::{
    build_default_registry, Agent, AgentStatus, ApprovalBroker, ApprovalDecision, ApprovalFuture,
    ApprovalProvider, ApprovalRequest, FunctionTool, LLMResponse, LlmRequest, MemorySession,
    Message, ModelRef, RunHandleStatus, Runner, ScriptStep, ScriptedModelProvider, Session,
    SessionItem, StaticTool, ToolCall, ToolOutput, ToolRegistry, ToolResultStatus,
};
use vv_agent::{
    InteractiveAgentClient, InteractiveSessionError, InteractiveSessionEvent,
    InteractiveSessionOptions,
};

struct DeferredApprovalProvider {
    requests: Arc<AtomicUsize>,
    request_ids: std::sync::mpsc::Sender<String>,
}

struct SynchronouslyResolvedApprovalProvider {
    broker: ApprovalBroker,
}

impl ApprovalProvider for SynchronouslyResolvedApprovalProvider {
    fn should_request(&self, request: &ApprovalRequest) -> bool {
        request.tool_name == "dangerous"
    }

    fn decide(&self, request: &ApprovalRequest) -> ApprovalFuture<Option<ApprovalDecision>> {
        let result = self
            .broker
            .resolve(&request.request_id, ApprovalDecision::allow());
        Box::pin(async move { result.map(|()| None) })
    }
}

impl ApprovalProvider for DeferredApprovalProvider {
    fn should_request(&self, request: &ApprovalRequest) -> bool {
        if request.tool_name != "dangerous" {
            return false;
        }
        self.requests.fetch_add(1, Ordering::SeqCst);
        true
    }

    fn decide(&self, request: &ApprovalRequest) -> ApprovalFuture<Option<ApprovalDecision>> {
        self.request_ids
            .send(request.request_id.clone())
            .expect("send approval request id");
        Box::pin(async { Ok(None) })
    }
}

include!("interactive_session/session_basics.rs");
include!("interactive_session/steering.rs");
include!("interactive_session/events_and_follow_ups.rs");
include!("interactive_session/lifecycle.rs");

fn scripted_runner(responses: Vec<LLMResponse>) -> Runner {
    Runner::builder()
        .model_provider(ScriptedModelProvider::new(
            "scripted",
            "demo-model",
            responses,
        ))
        .workspace(".")
        .build()
        .expect("runner")
}

fn scripted_agent() -> Agent {
    Agent::builder("assistant")
        .instructions("Finish with a concise answer.")
        .model(ModelRef::named("demo-model"))
        .build()
        .expect("agent")
}

fn finish_response(message: &str) -> LLMResponse {
    LLMResponse::with_tool_calls(
        "",
        vec![ToolCall::from_raw_arguments(
            "finish",
            "task_finish",
            json!({"message": message}),
        )],
    )
}

fn drain_events(
    receiver: &mut vv_agent::interactive::InteractiveSessionSubscription,
) -> Vec<InteractiveSessionEvent> {
    let mut events = Vec::new();
    while let Ok(event) = receiver.try_recv() {
        events.push(event);
    }
    events
}