Skip to main content

soma_application/provider_registry/
python_operations.rs

1use super::*;
2
3impl ProviderRegistry {
4    /// Resolves one exact managed Python provider path without importing it.
5    pub fn resolve_python_provider_path(
6        &self,
7        provider_path: &Path,
8    ) -> Result<PathBuf, ProviderValidationError> {
9        let source = self.file_source.as_ref().ok_or_else(|| {
10            ProviderValidationError::new(
11                "provider_file_source_unavailable",
12                "Python environment operations require a file-backed provider registry",
13            )
14        })?;
15        source
16            .resolve_python_provider_path(provider_path)
17            .map_err(|error| {
18                ProviderValidationError::new("provider_candidate_path_invalid", error.to_string())
19            })
20    }
21
22    /// Applies active-registry policy without invoking a provider.
23    pub fn authorize_operator_action(
24        &self,
25        mut call: ProviderCall,
26    ) -> Result<ProviderCall, ProviderError> {
27        let state = self
28            .state
29            .read()
30            .expect("provider registry lock should not be poisoned");
31        let entry = state
32            .snapshot
33            .core_snapshot()
34            .tool(&call.action)
35            .ok_or_else(|| {
36                ProviderError::validation(
37                    "registry",
38                    call.action.clone(),
39                    "unknown_action",
40                    format!("unknown provider action `{}`", call.action),
41                )
42            })?;
43        let provider_name = entry.provider_id().as_str();
44        let provider_kind = state
45            .snapshot
46            .catalogs
47            .iter()
48            .find(|catalog| catalog.provider.name == provider_name)
49            .map(|catalog| catalog.provider.kind)
50            .ok_or_else(|| {
51                ProviderError::new(
52                    "provider_not_loaded",
53                    provider_name,
54                    Some(call.action.clone()),
55                    "provider is not loaded in the active registry",
56                    "Reload providers and retry.",
57                )
58            })?;
59        if !provider_tool_surface_enabled(entry.spec(), call.surface) {
60            return Err(ProviderError::validation(
61                provider_name,
62                call.action.clone(),
63                "surface_disabled",
64                "action is not enabled on this surface",
65            ));
66        }
67        call.provider = provider_name.to_owned();
68        call.snapshot_id = state.snapshot.id.clone();
69        enforce_pre_input(entry.spec(), &call, provider_kind)?;
70        Ok(call)
71    }
72
73    /// Inventories persistent worker state and bounded redacted logs.
74    pub fn python_worker_status(&self) -> Value {
75        let state = self
76            .state
77            .read()
78            .expect("provider registry lock should not be poisoned");
79        let workers = state
80            .providers
81            .iter()
82            .filter_map(|(name, provider)| {
83                provider.runtime_status().map(|status| {
84                    json!({
85                        "provider": name,
86                        "status": status,
87                    })
88                })
89            })
90            .collect::<Vec<_>>();
91        json!({
92            "snapshot_id": state.snapshot.id,
93            "workers": workers,
94        })
95    }
96
97    /// Cancels the active invocation for one persistent Python provider.
98    pub fn cancel_python_worker(&self, provider_name: &str) -> Result<bool, ProviderError> {
99        let state = self
100            .state
101            .read()
102            .expect("provider registry lock should not be poisoned");
103        let provider = state.providers.get(provider_name).ok_or_else(|| {
104            ProviderError::validation(
105                "registry",
106                "python_worker_cancel",
107                "provider_not_loaded",
108                format!("provider `{provider_name}` is not loaded"),
109            )
110        })?;
111        if provider.runtime_status().is_none() {
112            return Err(ProviderError::validation(
113                provider_name,
114                "python_worker_cancel",
115                "python_worker_unavailable",
116                "provider does not use a persistent Python worker",
117            ));
118        }
119        Ok(provider.cancel_active())
120    }
121
122    /// Clears one persistent worker's crash-loop quarantine.
123    pub async fn reset_python_worker_quarantine(
124        &self,
125        provider_name: &str,
126    ) -> Result<(), ProviderError> {
127        let provider = {
128            let state = self
129                .state
130                .read()
131                .expect("provider registry lock should not be poisoned");
132            let provider = state.providers.get(provider_name).cloned().ok_or_else(|| {
133                ProviderError::validation(
134                    "registry",
135                    "python_worker_reset",
136                    "provider_not_loaded",
137                    format!("provider `{provider_name}` is not loaded"),
138                )
139            })?;
140            if provider.runtime_status().is_none() {
141                return Err(ProviderError::validation(
142                    provider_name,
143                    "python_worker_reset",
144                    "python_worker_unavailable",
145                    "provider does not use a persistent Python worker",
146                ));
147            }
148            provider
149        };
150        provider.reset_quarantine().await;
151        Ok(())
152    }
153}