Skip to main content

livekit_token_source/
request.rs

1// Copyright 2026 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 std::collections::HashMap;
16
17/// Per-call overrides used to parameterize a token request.
18///
19/// Every option is optional: anything left unset is omitted from the request and the
20/// server picks a default. Set only the options you care about:
21///
22/// ```
23/// # use livekit_token_source::TokenSourceFetchOptions;
24/// let options = TokenSourceFetchOptions::new()
25///     .with_room_name("my-room")
26///     .with_participant_identity("user-123");
27/// ```
28#[derive(Default, Clone, Debug, PartialEq, Eq)]
29pub struct TokenSourceFetchOptions {
30    pub(crate) room_name: Option<String>,
31    pub(crate) participant_name: Option<String>,
32    pub(crate) participant_identity: Option<String>,
33    pub(crate) participant_metadata: Option<String>,
34    pub(crate) participant_attributes: Option<HashMap<String, String>>,
35    pub(crate) agent_name: Option<String>,
36    pub(crate) agent_metadata: Option<String>,
37    pub(crate) deployment: Option<String>,
38}
39
40impl TokenSourceFetchOptions {
41    /// Creates empty fetch options; the server picks a default for every field.
42    pub fn new() -> Self {
43        Self::default()
44    }
45
46    /// Sets the name of the room being requested when generating credentials.
47    pub fn with_room_name(mut self, value: impl Into<String>) -> Self {
48        self.room_name = Some(value.into());
49        self
50    }
51
52    /// Sets the name of the participant being requested when generating credentials.
53    pub fn with_participant_name(mut self, value: impl Into<String>) -> Self {
54        self.participant_name = Some(value.into());
55        self
56    }
57
58    /// Sets the identity of the participant being requested when generating credentials.
59    pub fn with_participant_identity(mut self, value: impl Into<String>) -> Self {
60        self.participant_identity = Some(value.into());
61        self
62    }
63
64    /// Sets the metadata of the participant being requested when generating credentials.
65    pub fn with_participant_metadata(mut self, value: impl Into<String>) -> Self {
66        self.participant_metadata = Some(value.into());
67        self
68    }
69
70    /// Adds the given attributes to the participant attributes, keeping any set previously.
71    /// A key that was already set is overwritten with its new value.
72    pub fn with_participant_attributes(
73        mut self,
74        value: impl IntoIterator<Item = (impl Into<String>, impl Into<String>)>,
75    ) -> Self {
76        self.participant_attributes
77            .get_or_insert_with(HashMap::new)
78            .extend(value.into_iter().map(|(k, v)| (k.into(), v.into())));
79        self
80    }
81
82    /// Adds a single attribute to the participant attributes, keeping any set previously.
83    pub fn with_participant_attribute(
84        mut self,
85        key: impl Into<String>,
86        value: impl Into<String>,
87    ) -> Self {
88        self.participant_attributes
89            .get_or_insert_with(HashMap::new)
90            .insert(key.into(), value.into());
91        self
92    }
93
94    /// Sets the name of the agent to dispatch into the room.
95    pub fn with_agent_name(mut self, value: impl Into<String>) -> Self {
96        self.agent_name = Some(value.into());
97        self
98    }
99
100    /// Sets the metadata to pass to the dispatched agent.
101    pub fn with_agent_metadata(mut self, value: impl Into<String>) -> Self {
102        self.agent_metadata = Some(value.into());
103        self
104    }
105
106    /// Sets the agent deployment to target. Leave unset to target the production deployment.
107    pub fn with_deployment(mut self, value: impl Into<String>) -> Self {
108        self.deployment = Some(value.into());
109        self
110    }
111}
112
113/// The JSON body posted to the token endpoint. Built from [`TokenSourceFetchOptions`];
114/// the flat agent fields get nested under `room_config.agents` to match the server's schema.
115#[derive(serde::Serialize)]
116pub(crate) struct TokenSourceRequest {
117    #[serde(skip_serializing_if = "Option::is_none")]
118    room_name: Option<String>,
119    #[serde(skip_serializing_if = "Option::is_none")]
120    participant_name: Option<String>,
121    #[serde(skip_serializing_if = "Option::is_none")]
122    participant_identity: Option<String>,
123    #[serde(skip_serializing_if = "Option::is_none")]
124    participant_metadata: Option<String>,
125    #[serde(skip_serializing_if = "Option::is_none")]
126    participant_attributes: Option<HashMap<String, String>>,
127    #[serde(skip_serializing_if = "Option::is_none")]
128    room_config: Option<RoomConfig>,
129}
130
131/// Non-exhaustive list of room config parameter, the full list is in livekit_room.proto
132#[derive(serde::Serialize)]
133struct RoomConfig {
134    agents: Vec<AgentDispatch>,
135}
136
137#[derive(serde::Serialize)]
138struct AgentDispatch {
139    #[serde(skip_serializing_if = "Option::is_none")]
140    agent_name: Option<String>,
141    #[serde(skip_serializing_if = "Option::is_none")]
142    metadata: Option<String>,
143    #[serde(skip_serializing_if = "Option::is_none")]
144    deployment: Option<String>,
145}
146
147impl From<&TokenSourceFetchOptions> for TokenSourceRequest {
148    fn from(options: &TokenSourceFetchOptions) -> TokenSourceRequest {
149        // Only include a room_config when at least one agent field is set.
150        let room_config = if options.agent_name.is_some()
151            || options.agent_metadata.is_some()
152            || options.deployment.is_some()
153        {
154            Some(RoomConfig {
155                agents: vec![AgentDispatch {
156                    agent_name: options.agent_name.clone(),
157                    metadata: options.agent_metadata.clone(),
158                    deployment: options.deployment.clone(),
159                }],
160            })
161        } else {
162            None
163        };
164
165        TokenSourceRequest {
166            room_name: options.room_name.clone(),
167            participant_name: options.participant_name.clone(),
168            participant_identity: options.participant_identity.clone(),
169            participant_metadata: options.participant_metadata.clone(),
170            participant_attributes: options.participant_attributes.clone(),
171            room_config,
172        }
173    }
174}