| 1 | //! Provider descriptors over the existing built-in provider registry (#3084). |
| 2 | //! |
| 3 | //! A [`ProviderDescriptor`] is a thin, route-facing view over the static |
| 4 | //! [`provider::Provider`] trait objects already in [`crate::provider`]. It |
| 5 | //! surfaces only the transport facts route resolution needs (id, base URL, |
| 6 | //! default wire model, env vars, protocol) without duplicating the registry. |
| 7 | //! |
| 8 | //! Because a descriptor holds a `&'static dyn Provider`, it is intentionally |
| 9 | //! NOT `Serialize`/`PartialEq`-derivable. Never embed a [`ProviderDescriptor`] |
| 10 | //! inside a `Serialize` struct; serialize the resolved facts instead. |
| 11 | |
| 12 | use crate::ProviderKind; |
| 13 | use crate::provider::{self, Provider, WirePolicy}; |
| 14 | |
| 15 | use super::RequestProtocol; |
| 16 | use super::ids::{ProviderId, WireModelId}; |
| 17 | |
| 18 | /// Route-facing view of a built-in provider's transport facts. |
| 19 | /// |
| 20 | /// Holds a trait object, so it is deliberately not serializable/comparable. |
| 21 | #[derive(Clone, Copy)] |
| 22 | pub struct ProviderDescriptor { |
| 23 | /// The provider kind this descriptor describes. |
| 24 | pub kind: ProviderKind, |
| 25 | /// Backing static provider metadata entry. |
| 26 | pub inner: &'static dyn Provider, |
| 27 | } |
| 28 | |
| 29 | impl ProviderDescriptor { |
| 30 | /// Build a descriptor for a known provider kind. |
| 31 | #[must_use] |
| 32 | pub fn for_kind(kind: ProviderKind) -> Self { |
| 33 | Self { |
| 34 | kind, |
| 35 | inner: provider::provider_for_kind(kind), |
| 36 | } |
| 37 | } |
| 38 | |
| 39 | /// Canonical provider id. |
| 40 | #[must_use] |
| 41 | pub fn id(&self) -> ProviderId { |
| 42 | ProviderId::from(self.inner.id()) |
| 43 | } |
| 44 | |
| 45 | /// Default base URL when no override is present. |
| 46 | #[must_use] |
| 47 | pub fn default_base_url(&self) -> &'static str { |
| 48 | self.inner.default_base_url() |
| 49 | } |
| 50 | |
| 51 | /// Default wire model id when no model is selected. |
| 52 | #[must_use] |
| 53 | pub fn default_wire_model(&self) -> WireModelId { |
| 54 | WireModelId::from(self.inner.default_model()) |
| 55 | } |
| 56 | |
| 57 | /// Environment variable candidates for this provider's API key. |
| 58 | #[must_use] |
| 59 | pub fn env_vars(&self) -> &'static [&'static str] { |
| 60 | self.inner.env_vars() |
| 61 | } |
| 62 | |
| 63 | /// Policy used to select this provider's wire protocol. |
| 64 | #[must_use] |
| 65 | pub fn wire_policy(&self) -> WirePolicy { |
| 66 | self.inner.wire_policy() |
| 67 | } |
| 68 | |
| 69 | /// Resolve the concrete protocol for an offering endpoint key. |
| 70 | #[must_use] |
| 71 | pub fn protocol_for_endpoint(&self, endpoint_key: &str) -> Option<RequestProtocol> { |
| 72 | self.wire_policy().resolve(endpoint_key) |
| 73 | } |
| 74 | } |
| 75 | |
| 76 | impl std::fmt::Debug for ProviderDescriptor { |
| 77 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| 78 | f.debug_struct("ProviderDescriptor") |
| 79 | .field("kind", &self.kind) |
| 80 | .field("id", &self.inner.id()) |
| 81 | .field("wire_policy", &self.inner.wire_policy()) |
| 82 | .finish() |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | /// A concrete endpoint's transport facts. |
| 87 | /// |
| 88 | /// Unlike [`ProviderDescriptor`], this owns plain data and is safe to embed in |
| 89 | /// serializable route output (see [`super::candidate::ResolvedEndpoint`]). |
| 90 | #[derive(Debug, Clone)] |
| 91 | pub struct EndpointDescriptor { |
| 92 | /// Stable endpoint key (e.g. `"chat"`, `"responses"`). |
| 93 | pub endpoint_key: String, |
| 94 | /// Wire protocol spoken at this endpoint. |
| 95 | pub protocol: RequestProtocol, |
| 96 | /// Default base URL for this endpoint. |
| 97 | pub default_base_url: String, |
| 98 | /// Whether streaming is supported. |
| 99 | pub streaming: bool, |
| 100 | } |
| 101 |