soma_provider_adapters/wasm/
direct.rs1use super::*;
2
3#[derive(Clone)]
5pub struct PreparedComponentArtifact {
6 runtime: Arc<WasmRuntime>,
7 artifact: Arc<WasmArtifact>,
8 componentize: bool,
9}
10
11pub fn invoke_authorized_component_artifact(
13 path: &std::path::Path,
14 input: &Value,
15 capabilities: &soma_provider_core::HostCapabilities,
16 context: &soma_provider_core::ProviderInvocationContext,
17) -> Result<Value, String> {
18 if capabilities
19 .network
20 .as_ref()
21 .is_some_and(|network| network.enabled)
22 {
23 return Err(
24 "synchronous component conformance does not permit network host calls".to_owned(),
25 );
26 }
27 let artifact_bytes = read_artifact(path)?;
28 let componentize = is_componentize_artifact(&artifact_bytes);
29 let limits = conformance_limits(componentize);
30 let bytes = conformance_input(input, limits)?;
31 let runtime = shared_wasm_runtime()?;
32 let artifact = runtime.artifact(
33 &artifact_bytes,
34 Instant::now() + artifact_compile_timeout(&artifact_bytes),
35 )?;
36 let deadline = Instant::now() + Duration::from_millis(limits.timeout_ms);
37 let output = runtime.run_artifact(
38 artifact,
39 WasmInvocation {
40 input: &bytes,
41 limits,
42 capabilities,
43 context,
44 resolved_hosts: BTreeMap::new(),
45 deadline,
46 },
47 )?;
48 serde_json::from_slice(&output).map_err(|error| error.to_string())
49}
50
51pub async fn invoke_component_artifact_async(
54 path: &std::path::Path,
55 input: &Value,
56 capabilities: &soma_provider_core::HostCapabilities,
57 context: &soma_provider_core::ProviderInvocationContext,
58) -> Result<Value, String> {
59 let bytes = read_artifact(path)?;
60 let componentize = is_componentize_artifact(&bytes);
61 let prepared = prepare_component_artifact_bytes_before(
62 &bytes,
63 Instant::now() + artifact_compile_timeout(&bytes),
64 )?;
65 let limits = conformance_limits(componentize);
66 invoke_prepared_component_artifact_before_async(
67 &prepared,
68 input,
69 capabilities,
70 context,
71 Instant::now() + Duration::from_millis(limits.timeout_ms),
72 )
73 .await
74}
75
76pub async fn invoke_component_artifact_before_async(
78 path: &std::path::Path,
79 input: &Value,
80 capabilities: &soma_provider_core::HostCapabilities,
81 context: &soma_provider_core::ProviderInvocationContext,
82 deadline: Instant,
83) -> Result<Value, String> {
84 let prepared = prepare_component_artifact_before(path, deadline)?;
85 invoke_prepared_component_artifact_before_async(
86 &prepared,
87 input,
88 capabilities,
89 context,
90 deadline,
91 )
92 .await
93}
94
95pub fn prepare_component_artifact_before(
97 path: &std::path::Path,
98 deadline: Instant,
99) -> Result<PreparedComponentArtifact, String> {
100 let bytes = read_artifact(path)?;
101 prepare_component_artifact_bytes_before(&bytes, deadline)
102}
103
104fn prepare_component_artifact_bytes_before(
105 bytes: &[u8],
106 deadline: Instant,
107) -> Result<PreparedComponentArtifact, String> {
108 let componentize = is_componentize_artifact(bytes);
109 let runtime = shared_wasm_runtime()?;
110 let artifact = runtime.artifact(bytes, deadline)?;
111 if !matches!(artifact.as_ref(), WasmArtifact::Component(_)) {
112 return Err("artifact is core Wasm, not a component".to_owned());
113 }
114 Ok(PreparedComponentArtifact {
115 runtime,
116 artifact,
117 componentize,
118 })
119}
120
121pub async fn invoke_prepared_component_artifact_before_async(
123 prepared: &PreparedComponentArtifact,
124 input: &Value,
125 capabilities: &soma_provider_core::HostCapabilities,
126 context: &soma_provider_core::ProviderInvocationContext,
127 deadline: Instant,
128) -> Result<Value, String> {
129 let limits = conformance_limits(prepared.componentize);
130 let bytes = conformance_input(input, limits)?;
131 let resolved_hosts = resolve_component_hosts(capabilities, deadline).await?;
132 let runtime = prepared.runtime.clone();
133 let artifact = prepared.artifact.clone();
134 let capabilities = capabilities.clone();
135 let context = context.clone();
136 let task = tokio::task::spawn_blocking(move || {
137 runtime.run_artifact(
138 artifact,
139 WasmInvocation {
140 input: &bytes,
141 limits,
142 capabilities: &capabilities,
143 context: &context,
144 resolved_hosts,
145 deadline,
146 },
147 )
148 });
149 let remaining = deadline
150 .checked_duration_since(Instant::now())
151 .unwrap_or(Duration::ZERO);
152 let task_result = timeout(remaining, task)
153 .await
154 .map_err(|_| "component conformance invocation timed out".to_owned())?
155 .map_err(|error| format!("component conformance task failed: {error}"))?;
156 if Instant::now() >= deadline {
157 return Err("component conformance invocation timed out".to_owned());
158 }
159 let output = task_result?;
160 serde_json::from_slice(&output).map_err(|error| error.to_string())
161}
162
163pub fn verify_component_artifact(path: &std::path::Path) -> Result<(), String> {
165 let bytes = read_artifact(path)?;
166 let deadline = Instant::now() + artifact_compile_timeout(&bytes);
167 let prepared = prepare_component_artifact_bytes_before(&bytes, deadline)?;
168 prepared
169 .runtime
170 .verify_prepared_component(&prepared.artifact, prepared.componentize, deadline)
171}
172
173pub fn verify_component_artifact_before(
175 path: &std::path::Path,
176 deadline: Instant,
177) -> Result<(), String> {
178 let prepared = prepare_component_artifact_before(path, deadline)?;
179 prepared
180 .runtime
181 .verify_prepared_component(&prepared.artifact, prepared.componentize, deadline)
182}
183
184fn conformance_limits(componentize: bool) -> WasmRuntimeLimits {
185 WasmRuntimeLimits {
186 timeout_ms: 5_000,
187 max_input_bytes: 64 * 1024,
188 max_output_bytes: 256 * 1024,
189 fuel: 1_000_000,
190 max_memory_bytes: 64 * 1024 * 1024,
191 max_table_elements: 10_000,
192 max_instances: 16,
193 }
194 .with_componentize_minimums(componentize)
195}
196
197fn conformance_input(input: &Value, limits: WasmRuntimeLimits) -> Result<Vec<u8>, String> {
198 let bytes = serde_json::to_vec(input).map_err(|error| error.to_string())?;
199 if bytes.len() > limits.max_input_bytes {
200 return Err(format!(
201 "component conformance input exceeds {} bytes",
202 limits.max_input_bytes
203 ));
204 }
205 Ok(bytes)
206}