Skip to main content

rpi_runtime/
lib.rs

1//! Host-independent model configuration, authentication and execution bindings.
2//!
3//! Hosts load their own configuration and credentials, supply a [`ValueSource`],
4//! and adapt any already-loaded extension providers through [`ProviderRegistry`].
5//! The library does not discover files, load plugins, or depend on a terminal.
6pub mod config;
7pub mod credentials;
8pub mod provider;
9mod runtime;
10pub mod thinking;
11pub mod values;
12
13pub use provider::{ResolveError, ResolvedModel, RuntimeFactories as ProviderRegistry};
14pub use runtime::{ModelIdentity, ModelSelection, Runtime, RuntimeSnapshot};
15pub use values::ValueSource;
16
17/// Provider metadata supplied by the host's existing extension registry.
18#[derive(Clone)]
19pub struct ProviderDeclaration {
20    pub id: String,
21    pub models: Vec<rpi_ai::Model>,
22    pub api_key_env: Option<String>,
23    pub requires_auth: bool,
24}
25
26/// Model defaults supplied by the host, independent of its storage format.
27#[derive(Clone, Default, serde::Deserialize)]
28#[serde(rename_all = "camelCase")]
29pub struct RuntimeSettings {
30    pub default_provider: Option<String>,
31    pub default_model: Option<String>,
32    pub default_thinking_level: Option<String>,
33    /// Opaque presentation hint, carried for existing CLI API compatibility.
34    pub theme: Option<String>,
35    pub http_idle_timeout_ms: Option<i64>,
36}
37
38/// An explicit selection, separate from defaults persisted by the host.
39#[derive(Default)]
40pub struct ModelRequest<'a> {
41    pub provider: Option<&'a str>,
42    /// Select a wire protocol when a provider exposes the same ID through several APIs.
43    pub api: Option<rpi_ai::Api>,
44    pub model: Option<&'a str>,
45    pub thinking: Option<rpi_ai::ThinkingLevel>,
46    pub api_key: Option<&'a str>,
47    pub base_url: Option<&'a str>,
48}
49
50/// Immutable configuration inputs read from any host-owned storage.
51/// Keep this object private: it contains credentials and unexpanded headers.
52#[derive(Clone, Default)]
53pub struct RuntimeConfig {
54    pub models: config::ModelsConfig,
55    pub credentials: config::AuthStore,
56    pub settings: RuntimeSettings,
57}
58
59impl RuntimeConfig {
60    /// Resolve each provider independently, expanding its headers only if needed.
61    pub fn auth_states(
62        &self,
63        providers: &ProviderRegistry,
64        values: &dyn ValueSource,
65    ) -> std::collections::BTreeMap<String, credentials::AuthState> {
66        let mut ids: std::collections::BTreeSet<String> = provider::builtin_catalog()
67            .into_iter()
68            .map(|model| model.provider)
69            .collect();
70        ids.extend(self.models.providers.keys().cloned());
71        ids.extend(self.credentials.keys().cloned());
72        ids.extend(
73            providers
74                .manifests
75                .iter()
76                .map(|manifest| manifest.id.clone()),
77        );
78        ids.into_iter()
79            .map(|id| {
80                let state = self.auth_status(&id, providers, values);
81                (id, state)
82            })
83            .collect()
84    }
85    /// Local, provider-scoped readiness using the same rules as execution.
86    pub fn auth_status(
87        &self,
88        provider: &str,
89        providers: &ProviderRegistry,
90        values: &dyn ValueSource,
91    ) -> credentials::AuthState {
92        credentials::state(
93            provider,
94            self.models.providers.get(provider),
95            &self.credentials,
96            providers
97                .manifests
98                .iter()
99                .find(|manifest| manifest.id == provider),
100            values,
101            || {
102                provider::provider_has_auth_headers(
103                    provider,
104                    &self.models,
105                    &providers.manifests,
106                    values,
107                )
108            },
109        )
110    }
111    /// Select and bind a runnable model. Existing results retain their providers
112    /// and loader leases even when the host later replaces this configuration.
113    pub fn resolve(
114        &self,
115        request: &ModelRequest<'_>,
116        providers: &ProviderRegistry,
117        values: &dyn ValueSource,
118    ) -> Result<ResolvedModel, ResolveError> {
119        provider::resolve_with_factories(request, self, providers, values)
120    }
121
122    /// All configured models, including those awaiting credentials. Descriptors
123    /// and diagnostics omit request headers and credential values.
124    pub fn catalog(
125        &self,
126        providers: &ProviderRegistry,
127        values: &dyn ValueSource,
128    ) -> (Vec<rpi_ai::Model>, Vec<String>) {
129        provider::catalog_with_diagnostics(self.models.clone(), &providers.manifests, values)
130    }
131}
132
133#[cfg(test)]
134mod test_host;