Skip to content

Commit 39083b2

Browse files
committed
feat(0.10.0): Phases 9 + 10 — Models listing and self-hosted credentials
Phase 9: GET /v1/models, /v1/models/{id}, and project variants. ModelInfo flattens the spec's oneOf [STT, TTS] into one struct with optional feature fields. Wired as Deepgram::models() under the existing `manage` feature. Phase 10: list/create/get/delete on /v1/projects/{id}/self-hosted/distribution/credentials. Scope is an 8-variant enum, Provider is a 1-variant enum (Quay). New `self_hosted` feature flag, off by default — the audience is self-hosted operators only. Both phases are purely additive — no breaking changes.
1 parent bf68cae commit 39083b2

12 files changed

Lines changed: 762 additions & 0 deletions

File tree

Cargo.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ listen = ["dep:tungstenite", "dep:tokio-tungstenite"]
6161
speak = ["dep:tungstenite", "dep:tokio-tungstenite"]
6262
agent = ["dep:tungstenite", "dep:tokio-tungstenite"]
6363
read = []
64+
self_hosted = []
6465

6566
[[example]]
6667
name = "grant_token"
@@ -195,3 +196,13 @@ required-features = ["agent"]
195196
name = "agent_variables"
196197
path = "examples/agent/rest/variables.rs"
197198
required-features = ["agent"]
199+
200+
[[example]]
201+
name = "manage_models"
202+
path = "examples/manage/models.rs"
203+
required-features = ["manage"]
204+
205+
[[example]]
206+
name = "self_hosted_distribution_credentials"
207+
path = "examples/self_hosted/distribution_credentials.rs"
208+
required-features = ["self_hosted"]

examples/manage/models.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
use deepgram::{manage::models::list_options, Deepgram, DeepgramError};
2+
use std::env;
3+
4+
#[tokio::main]
5+
async fn main() -> Result<(), DeepgramError> {
6+
let deepgram_api_key =
7+
env::var("DEEPGRAM_API_KEY").expect("DEEPGRAM_API_KEY environmental variable");
8+
9+
let project_id =
10+
env::var("DEEPGRAM_PROJECT_ID").expect("DEEPGRAM_PROJECT_ID environmental variable");
11+
12+
let dg_client = Deepgram::new(&deepgram_api_key)?;
13+
14+
let opts = list_options::Options::builder()
15+
.include_outdated(false)
16+
.build();
17+
18+
let public = dg_client.models().list(&opts).await?;
19+
println!("public: {} STT, {} TTS", public.stt.len(), public.tts.len());
20+
21+
let project = dg_client
22+
.models()
23+
.list_for_project(&project_id, &opts)
24+
.await?;
25+
println!(
26+
"project: {} STT, {} TTS",
27+
project.stt.len(),
28+
project.tts.len()
29+
);
30+
31+
if let Some(first) = public.stt.first() {
32+
let detail = dg_client.models().get(&first.uuid).await?;
33+
println!("{}: {}", detail.canonical_name, detail.version);
34+
}
35+
36+
Ok(())
37+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
use deepgram::{
2+
self_hosted::distribution_credentials::create_options::{Options, Provider, Scope},
3+
Deepgram, DeepgramError,
4+
};
5+
use std::env;
6+
7+
#[tokio::main]
8+
async fn main() -> Result<(), DeepgramError> {
9+
let deepgram_api_key =
10+
env::var("DEEPGRAM_API_KEY").expect("DEEPGRAM_API_KEY environmental variable");
11+
12+
let project_id =
13+
env::var("DEEPGRAM_PROJECT_ID").expect("DEEPGRAM_PROJECT_ID environmental variable");
14+
15+
let dg_client = Deepgram::new(&deepgram_api_key)?;
16+
17+
let existing = dg_client
18+
.distribution_credentials()
19+
.list(&project_id)
20+
.await?;
21+
println!(
22+
"existing credential sets: {}",
23+
existing.distribution_credentials.len()
24+
);
25+
26+
let opts = Options::builder()
27+
.scopes([Scope::ProductApi, Scope::ProductEngine])
28+
.provider(Provider::Quay)
29+
.comment("created by deepgram-rust-sdk example")
30+
.build();
31+
let created = dg_client
32+
.distribution_credentials()
33+
.create(&project_id, &opts)
34+
.await?;
35+
let credentials_id = created.distribution_credentials.distribution_credentials_id;
36+
println!("created: {credentials_id}");
37+
38+
let fetched = dg_client
39+
.distribution_credentials()
40+
.get(&project_id, &credentials_id)
41+
.await?;
42+
println!(
43+
"scopes on fetched: {:?}",
44+
fetched.distribution_credentials.scopes
45+
);
46+
47+
dg_client
48+
.distribution_credentials()
49+
.delete(&project_id, &credentials_id)
50+
.await?;
51+
println!("deleted: {credentials_id}");
52+
53+
Ok(())
54+
}

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ pub mod listen;
3535
pub mod manage;
3636
#[cfg(feature = "read")]
3737
pub mod read;
38+
#[cfg(feature = "self_hosted")]
39+
pub mod self_hosted;
3840
#[cfg(feature = "speak")]
3941
pub mod speak;
4042

src/manage/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ pub mod billing;
44
pub mod invitations;
55
pub mod keys;
66
pub mod members;
7+
pub mod models;
78
pub mod projects;
89
pub mod scopes;
910
pub mod usage;

src/manage/models.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
//! List and look up Deepgram models, both public and project-scoped.
2+
//!
3+
//! See the [Deepgram API Reference][api] for more info.
4+
//!
5+
//! [api]: https://developers.deepgram.com/reference/manage-api/get-models
6+
7+
use crate::{
8+
manage::models::response::{ListModelsResponse, ModelInfo},
9+
send_and_translate_response, Deepgram,
10+
};
11+
12+
pub mod list_options;
13+
pub mod response;
14+
15+
/// Sub-client for the model-listing endpoints. Constructed via
16+
/// [`Deepgram::models`].
17+
#[derive(Debug, Clone)]
18+
pub struct Models<'a>(&'a Deepgram);
19+
20+
impl Deepgram {
21+
/// Construct a new [`Models`] sub-client.
22+
pub fn models(&self) -> Models<'_> {
23+
self.into()
24+
}
25+
}
26+
27+
impl<'a> From<&'a Deepgram> for Models<'a> {
28+
fn from(deepgram: &'a Deepgram) -> Self {
29+
Self(deepgram)
30+
}
31+
}
32+
33+
impl Models<'_> {
34+
/// `GET /v1/models` — metadata for all latest public models.
35+
///
36+
/// To list project-scoped (including non-public) models, use
37+
/// [`Models::list_for_project`].
38+
pub async fn list(&self, options: &list_options::Options) -> crate::Result<ListModelsResponse> {
39+
let url = "https://api.deepgram.com/v1/models";
40+
let mut request = self.0.client.get(url);
41+
let pairs = options.query_pairs();
42+
if !pairs.is_empty() {
43+
request = request.query(&pairs);
44+
}
45+
send_and_translate_response(request).await
46+
}
47+
48+
/// `GET /v1/models/{model_id}` — metadata for one public model.
49+
pub async fn get(&self, model_id: &str) -> crate::Result<ModelInfo> {
50+
let url = format!("https://api.deepgram.com/v1/models/{model_id}");
51+
send_and_translate_response(self.0.client.get(url)).await
52+
}
53+
54+
/// `GET /v1/projects/{project_id}/models` — metadata for all
55+
/// models the project has access to, including non-public ones.
56+
pub async fn list_for_project(
57+
&self,
58+
project_id: &str,
59+
options: &list_options::Options,
60+
) -> crate::Result<ListModelsResponse> {
61+
let url = format!("https://api.deepgram.com/v1/projects/{project_id}/models");
62+
let mut request = self.0.client.get(url);
63+
let pairs = options.query_pairs();
64+
if !pairs.is_empty() {
65+
request = request.query(&pairs);
66+
}
67+
send_and_translate_response(request).await
68+
}
69+
70+
/// `GET /v1/projects/{project_id}/models/{model_id}` — metadata
71+
/// for a single project-scoped model.
72+
pub async fn get_for_project(
73+
&self,
74+
project_id: &str,
75+
model_id: &str,
76+
) -> crate::Result<ModelInfo> {
77+
let url = format!("https://api.deepgram.com/v1/projects/{project_id}/models/{model_id}");
78+
send_and_translate_response(self.0.client.get(url)).await
79+
}
80+
}

src/manage/models/list_options.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//! Options for the model-listing endpoints.
2+
//!
3+
//! Mirrors the `include_outdated` query param shared by
4+
//! `GET /v1/models` and `GET /v1/projects/{id}/models`.
5+
6+
/// Options for [`Models::list`](super::super::Models::list) and
7+
/// [`Models::list_for_project`](super::super::Models::list_for_project).
8+
#[derive(Debug, Default, PartialEq, Clone, Copy)]
9+
pub struct Options {
10+
include_outdated: Option<bool>,
11+
}
12+
13+
/// Builder for [`Options`].
14+
#[derive(Debug, Default, PartialEq, Clone, Copy)]
15+
pub struct OptionsBuilder(Options);
16+
17+
impl Options {
18+
/// Construct a new [`OptionsBuilder`].
19+
pub fn builder() -> OptionsBuilder {
20+
OptionsBuilder::default()
21+
}
22+
23+
pub(super) fn query_pairs(&self) -> Vec<(&'static str, &'static str)> {
24+
let mut pairs = Vec::new();
25+
if let Some(include_outdated) = self.include_outdated {
26+
pairs.push((
27+
"include_outdated",
28+
if include_outdated { "true" } else { "false" },
29+
));
30+
}
31+
pairs
32+
}
33+
}
34+
35+
impl OptionsBuilder {
36+
/// Construct a fresh empty builder.
37+
pub fn new() -> Self {
38+
Self(Options::default())
39+
}
40+
41+
/// When `true`, include non-latest model versions in the response.
42+
pub fn include_outdated(mut self, include_outdated: bool) -> Self {
43+
self.0.include_outdated = Some(include_outdated);
44+
self
45+
}
46+
47+
/// Finish building.
48+
pub fn build(self) -> Options {
49+
self.0
50+
}
51+
}

0 commit comments

Comments
 (0)