use std::fmt;
use std::sync::Arc;
use std::time::Duration;
use async_trait::async_trait;
use talon_container::{ContainerHandle, ContainerRunner, RunResult, RunSpec};
use talon_core::protocol::{ContainerOutput, LlmEndpoint, OutputStatus, SessionUsage};
use talon_core::types::{ContainerConfig, VllmConfig};
use talon_llm::GatewayHandle;
use talon_scheduler::RunningContainer;
use tokio::sync::mpsc;
pub type StartHook = Box<dyn FnOnce(Arc<dyn RunningContainer>) + Send>;
#[async_trait]
pub trait ContainerLauncher: Send + Sync {
async fn launch(
&self,
spec: RunSpec,
outputs: mpsc::Sender<ContainerOutput>,
on_start: StartHook,
) -> RunResult;
fn hard_timeout(&self, config: &ContainerConfig) -> Duration;
}
pub trait LlmAccess: Send + Sync {
fn issue(&self, label: &str, ttl: Duration, vllm: &VllmConfig) -> (String, LlmEndpoint);
fn revoke(&self, token: &str);
fn extend(&self, _token: &str, _ttl: Duration) -> bool {
false
}
}
pub struct QueueContainer(pub ContainerHandle);
impl fmt::Debug for QueueContainer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("QueueContainer").field(&self.0).finish()
}
}
#[async_trait]
impl RunningContainer for QueueContainer {
fn name(&self) -> &str {
self.0.name()
}
async fn stop(&self) -> talon_core::Result<()> {
self.0.stop().await;
Ok(())
}
}
pub struct RunnerLauncher {
runner: ContainerRunner,
}
impl fmt::Debug for RunnerLauncher {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RunnerLauncher")
.field("runner", &self.runner)
.finish()
}
}
impl RunnerLauncher {
pub fn new(runner: ContainerRunner) -> Self {
Self { runner }
}
}
#[async_trait]
impl ContainerLauncher for RunnerLauncher {
async fn launch(
&self,
spec: RunSpec,
outputs: mpsc::Sender<ContainerOutput>,
on_start: StartHook,
) -> RunResult {
let handler = move |output: ContainerOutput| {
let outputs = outputs.clone();
async move {
let _ = outputs.send(output).await;
}
};
self.runner
.run(spec, handler, move |handle: ContainerHandle| {
on_start(Arc::new(QueueContainer(handle)));
})
.await
}
fn hard_timeout(&self, config: &ContainerConfig) -> Duration {
self.runner.config().hard_timeout(config)
}
}
#[derive(Debug, Clone)]
pub struct GatewayLlm {
gateway: GatewayHandle,
container_host: String,
}
impl GatewayLlm {
pub fn new(gateway: GatewayHandle, container_host: impl Into<String>) -> Self {
Self {
gateway,
container_host: container_host.into(),
}
}
}
impl LlmAccess for GatewayLlm {
fn issue(&self, label: &str, ttl: Duration, vllm: &VllmConfig) -> (String, LlmEndpoint) {
let token = self.gateway.issue_token(label, ttl);
let endpoint = self
.gateway
.endpoint(token.clone(), &self.container_host, vllm);
(token, endpoint)
}
fn revoke(&self, token: &str) {
self.gateway.revoke(token);
}
fn extend(&self, token: &str, ttl: Duration) -> bool {
self.gateway.extend(token, ttl)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AgentOutcome {
pub status: OutputStatus,
pub error: Option<String>,
pub last_result: Option<String>,
pub new_session_id: Option<String>,
pub usage: Option<SessionUsage>,
pub launched: bool,
}
impl AgentOutcome {
pub fn not_started(error: impl Into<String>) -> Self {
Self {
status: OutputStatus::Error,
error: Some(error.into()),
last_result: None,
new_session_id: None,
usage: None,
launched: false,
}
}
pub fn is_success(&self) -> bool {
self.status == OutputStatus::Success
}
}