livekit_api/services/
agent_dispatch.rs

1// Copyright 2025 LiveKit, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use super::{twirp_client::TwirpClient, ServiceBase, ServiceResult, LIVEKIT_PACKAGE};
16use crate::{
17    access_token::{AccessTokenError, VideoGrants},
18    get_env_keys,
19};
20use http::header::HeaderMap;
21use livekit_protocol as proto;
22
23const SVC: &str = "AgentDispatchService";
24
25#[derive(Debug)]
26pub struct AgentDispatchClient {
27    base: ServiceBase,
28    client: TwirpClient,
29}
30
31impl AgentDispatchClient {
32    pub fn with_api_key(host: &str, api_key: &str, api_secret: &str) -> Self {
33        Self {
34            base: ServiceBase::with_api_key(api_key, api_secret),
35            client: TwirpClient::new(host, LIVEKIT_PACKAGE, None),
36        }
37    }
38
39    pub fn new(host: &str) -> ServiceResult<Self> {
40        let (api_key, api_secret) = get_env_keys()?;
41        Ok(Self::with_api_key(host, &api_key, &api_secret))
42    }
43
44    /// Creates an explicit dispatch for an agent to join a room.
45    ///
46    /// To use explicit dispatch, your agent must be registered with an `agent_name`.
47    ///
48    /// # Arguments
49    /// * `req` - Request containing dispatch creation parameters
50    ///
51    /// # Returns
52    /// The created agent dispatch object
53    ///
54    pub async fn create_dispatch(
55        &self,
56        req: proto::CreateAgentDispatchRequest,
57    ) -> ServiceResult<proto::AgentDispatch> {
58        const METHOD: &str = "CreateDispatch";
59        let headers = self.auth_headers(req.room.to_string())?;
60        Ok(self.client.request(SVC, METHOD, req, headers).await?)
61    }
62
63    /// Deletes an explicit dispatch for an agent in a room.
64    ///
65    /// # Arguments
66    /// * `dispatch_id` - ID of the dispatch to delete
67    /// * `room_name` - Name of the room containing the dispatch
68    ///
69    /// # Returns
70    /// The deleted agent dispatch object
71    ///
72    pub async fn delete_dispatch(
73        &self,
74        dispatch_id: impl Into<String>,
75        room_name: impl Into<String>,
76    ) -> ServiceResult<proto::AgentDispatch> {
77        const METHOD: &str = "DeleteDispatch";
78        let req = proto::DeleteAgentDispatchRequest {
79            dispatch_id: dispatch_id.into(),
80            room: room_name.into(),
81        };
82        let headers = self.auth_headers(req.room.to_string())?;
83        Ok(self.client.request(SVC, METHOD, req, headers).await?)
84    }
85
86    /// Lists all agent dispatches in a room.
87    ///
88    /// # Arguments
89    /// * `room_name` - Name of the room to list dispatches from
90    ///
91    /// # Returns
92    /// List of dispatch objects in the room
93    ///
94    pub async fn list_dispatch(
95        &self,
96        room_name: impl Into<String>,
97    ) -> ServiceResult<Vec<proto::AgentDispatch>> {
98        const METHOD: &str = "ListDispatch";
99        let req = proto::ListAgentDispatchRequest { room: room_name.into(), ..Default::default() };
100        let headers = self.auth_headers(req.room.to_string())?;
101        let res: proto::ListAgentDispatchResponse =
102            self.client.request(SVC, METHOD, req, headers).await?;
103        Ok(res.agent_dispatches)
104    }
105
106    /// Gets an agent dispatch by ID.
107    ///
108    /// # Arguments
109    /// * `dispatch_id` - ID of the dispatch to retrieve
110    /// * `room_name` - Name of the room containing the dispatch
111    ///
112    /// # Returns
113    /// Requested dispatch object if found, `None` otherwise
114    ///
115    pub async fn get_dispatch(
116        &self,
117        dispatch_id: impl Into<String>,
118        room_name: impl Into<String>,
119    ) -> ServiceResult<Option<proto::AgentDispatch>> {
120        const METHOD: &str = "ListDispatch";
121        let req = proto::ListAgentDispatchRequest {
122            room: room_name.into(),
123            dispatch_id: dispatch_id.into(),
124        };
125        let headers = self.auth_headers(req.room.to_string())?;
126        let mut res: proto::ListAgentDispatchResponse =
127            self.client.request(SVC, METHOD, req, headers).await?;
128        Ok(res.agent_dispatches.pop())
129    }
130}
131
132impl AgentDispatchClient {
133    /// Generates the auth header common to all dispatch request types.
134    fn auth_headers(&self, room: String) -> Result<HeaderMap, AccessTokenError> {
135        self.base.auth_header(VideoGrants { room, room_admin: true, ..Default::default() }, None)
136    }
137}