@elizaos/interop 2.0.0-alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +436 -0
  3. package/dist/packages/interop/tsconfig.tsbuildinfo +1 -0
  4. package/dist/tsconfig.tsbuildinfo +1 -0
  5. package/dist/typescript/index.d.ts +33 -0
  6. package/dist/typescript/index.d.ts.map +1 -0
  7. package/dist/typescript/index.js +121 -0
  8. package/dist/typescript/python-bridge.d.ts +80 -0
  9. package/dist/typescript/python-bridge.d.ts.map +1 -0
  10. package/dist/typescript/python-bridge.js +334 -0
  11. package/dist/typescript/types.d.ts +301 -0
  12. package/dist/typescript/types.d.ts.map +1 -0
  13. package/dist/typescript/types.js +10 -0
  14. package/dist/typescript/wasm-loader.d.ts +32 -0
  15. package/dist/typescript/wasm-loader.d.ts.map +1 -0
  16. package/dist/typescript/wasm-loader.js +269 -0
  17. package/package.json +43 -0
  18. package/python/__init__.py +50 -0
  19. package/python/__pycache__/__init__.cpython-313.pyc +0 -0
  20. package/python/__pycache__/rust_ffi.cpython-313.pyc +0 -0
  21. package/python/__pycache__/ts_bridge.cpython-313.pyc +0 -0
  22. package/python/__pycache__/wasm_loader.cpython-313.pyc +0 -0
  23. package/python/bridge_server.py +505 -0
  24. package/python/rust_ffi.py +418 -0
  25. package/python/tests/__init__.py +1 -0
  26. package/python/tests/__pycache__/__init__.cpython-313.pyc +0 -0
  27. package/python/tests/__pycache__/test_bridge_server.cpython-313-pytest-9.0.2.pyc +0 -0
  28. package/python/tests/__pycache__/test_interop.cpython-313-pytest-9.0.2.pyc +0 -0
  29. package/python/tests/__pycache__/test_rust_ffi.cpython-313-pytest-9.0.2.pyc +0 -0
  30. package/python/tests/__pycache__/test_rust_ffi_loader.cpython-313-pytest-9.0.2.pyc +0 -0
  31. package/python/tests/test_bridge_server.py +525 -0
  32. package/python/tests/test_interop.py +319 -0
  33. package/python/tests/test_rust_ffi.py +352 -0
  34. package/python/tests/test_rust_ffi_loader.py +71 -0
  35. package/python/ts_bridge.py +452 -0
  36. package/python/ts_bridge_runner.mjs +284 -0
  37. package/python/wasm_loader.py +517 -0
  38. package/rust/ffi_exports.rs +362 -0
  39. package/rust/mod.rs +21 -0
  40. package/rust/py_loader.rs +412 -0
  41. package/rust/ts_loader.rs +467 -0
  42. package/rust/wasm_plugin.rs +320 -0
@@ -0,0 +1,320 @@
1
+ //! WASM Plugin Bindings for elizaOS
2
+ //!
3
+ //! This module provides WASM-specific bindings for creating plugins that can be
4
+ //! loaded by the TypeScript runtime.
5
+ //!
6
+ //! # Example
7
+ //!
8
+ //! ```rust,ignore
9
+ //! use elizaos::interop::wasm_plugin::*;
10
+ //! use elizaos::types::*;
11
+ //!
12
+ //! // Define your plugin
13
+ //! struct MyPlugin;
14
+ //!
15
+ //! impl WasmPlugin for MyPlugin {
16
+ //! fn manifest(&self) -> PluginManifest {
17
+ //! PluginManifest {
18
+ //! name: "my-plugin".to_string(),
19
+ //! description: "A WASM plugin".to_string(),
20
+ //! ..Default::default()
21
+ //! }
22
+ //! }
23
+ //!
24
+ //! fn init(&mut self, config: &str) -> Result<(), String> {
25
+ //! Ok(())
26
+ //! }
27
+ //!
28
+ //! // ... implement other methods ...
29
+ //! }
30
+ //!
31
+ //! // Export the plugin
32
+ //! elizaos_wasm_plugin!(MyPlugin::new());
33
+ //! ```
34
+
35
+ #![cfg(feature = "wasm")]
36
+
37
+ use serde::{Deserialize, Serialize};
38
+ use std::collections::HashMap;
39
+ use std::sync::Mutex;
40
+ use wasm_bindgen::prelude::*;
41
+
42
+ /// Plugin manifest for WASM export
43
+ #[derive(Clone, Debug, Default, Serialize, Deserialize)]
44
+ #[serde(rename_all = "camelCase")]
45
+ pub struct PluginManifest {
46
+ pub name: String,
47
+ pub description: String,
48
+ #[serde(skip_serializing_if = "Option::is_none")]
49
+ pub version: Option<String>,
50
+ #[serde(skip_serializing_if = "Option::is_none")]
51
+ pub config: Option<HashMap<String, serde_json::Value>>,
52
+ #[serde(skip_serializing_if = "Option::is_none")]
53
+ pub dependencies: Option<Vec<String>>,
54
+ #[serde(skip_serializing_if = "Option::is_none")]
55
+ pub actions: Option<Vec<ActionManifest>>,
56
+ #[serde(skip_serializing_if = "Option::is_none")]
57
+ pub providers: Option<Vec<ProviderManifest>>,
58
+ #[serde(skip_serializing_if = "Option::is_none")]
59
+ pub evaluators: Option<Vec<EvaluatorManifest>>,
60
+ }
61
+
62
+ #[derive(Clone, Debug, Serialize, Deserialize)]
63
+ #[serde(rename_all = "camelCase")]
64
+ pub struct ActionManifest {
65
+ pub name: String,
66
+ pub description: String,
67
+ #[serde(skip_serializing_if = "Option::is_none")]
68
+ pub similes: Option<Vec<String>>,
69
+ }
70
+
71
+ #[derive(Clone, Debug, Serialize, Deserialize)]
72
+ #[serde(rename_all = "camelCase")]
73
+ pub struct ProviderManifest {
74
+ pub name: String,
75
+ #[serde(skip_serializing_if = "Option::is_none")]
76
+ pub description: Option<String>,
77
+ #[serde(skip_serializing_if = "Option::is_none")]
78
+ pub dynamic: Option<bool>,
79
+ #[serde(skip_serializing_if = "Option::is_none")]
80
+ pub position: Option<i32>,
81
+ #[serde(skip_serializing_if = "Option::is_none")]
82
+ pub private: Option<bool>,
83
+ }
84
+
85
+ #[derive(Clone, Debug, Serialize, Deserialize)]
86
+ #[serde(rename_all = "camelCase")]
87
+ pub struct EvaluatorManifest {
88
+ pub name: String,
89
+ pub description: String,
90
+ #[serde(skip_serializing_if = "Option::is_none")]
91
+ pub always_run: Option<bool>,
92
+ #[serde(skip_serializing_if = "Option::is_none")]
93
+ pub similes: Option<Vec<String>>,
94
+ }
95
+
96
+ /// Action result for WASM
97
+ #[derive(Clone, Debug, Default, Serialize, Deserialize)]
98
+ #[serde(rename_all = "camelCase")]
99
+ pub struct ActionResult {
100
+ pub success: bool,
101
+ #[serde(skip_serializing_if = "Option::is_none")]
102
+ pub text: Option<String>,
103
+ #[serde(skip_serializing_if = "Option::is_none")]
104
+ pub error: Option<String>,
105
+ #[serde(skip_serializing_if = "Option::is_none")]
106
+ pub data: Option<HashMap<String, serde_json::Value>>,
107
+ #[serde(skip_serializing_if = "Option::is_none")]
108
+ pub values: Option<HashMap<String, serde_json::Value>>,
109
+ }
110
+
111
+ impl ActionResult {
112
+ pub fn success() -> Self {
113
+ ActionResult {
114
+ success: true,
115
+ ..Default::default()
116
+ }
117
+ }
118
+
119
+ pub fn success_with_text(text: &str) -> Self {
120
+ ActionResult {
121
+ success: true,
122
+ text: Some(text.to_string()),
123
+ ..Default::default()
124
+ }
125
+ }
126
+
127
+ pub fn failure(error: &str) -> Self {
128
+ ActionResult {
129
+ success: false,
130
+ error: Some(error.to_string()),
131
+ ..Default::default()
132
+ }
133
+ }
134
+ }
135
+
136
+ /// Provider result for WASM
137
+ #[derive(Clone, Debug, Default, Serialize, Deserialize)]
138
+ #[serde(rename_all = "camelCase")]
139
+ pub struct ProviderResult {
140
+ #[serde(skip_serializing_if = "Option::is_none")]
141
+ pub text: Option<String>,
142
+ #[serde(skip_serializing_if = "Option::is_none")]
143
+ pub values: Option<HashMap<String, serde_json::Value>>,
144
+ #[serde(skip_serializing_if = "Option::is_none")]
145
+ pub data: Option<HashMap<String, serde_json::Value>>,
146
+ }
147
+
148
+ /// Trait for WASM-exportable plugins
149
+ pub trait WasmPlugin: Send + Sync {
150
+ /// Get the plugin manifest
151
+ fn manifest(&self) -> PluginManifest;
152
+
153
+ /// Initialize the plugin with configuration
154
+ fn init(&mut self, config_json: &str) -> Result<(), String>;
155
+
156
+ /// Validate an action
157
+ fn validate_action(&self, name: &str, memory_json: &str, state_json: &str) -> bool;
158
+
159
+ /// Invoke an action
160
+ fn invoke_action(
161
+ &self,
162
+ name: &str,
163
+ memory_json: &str,
164
+ state_json: &str,
165
+ options_json: &str,
166
+ ) -> ActionResult;
167
+
168
+ /// Get provider data
169
+ fn get_provider(&self, name: &str, memory_json: &str, state_json: &str) -> ProviderResult;
170
+
171
+ /// Validate an evaluator
172
+ fn validate_evaluator(&self, name: &str, memory_json: &str, state_json: &str) -> bool;
173
+
174
+ /// Invoke an evaluator
175
+ fn invoke_evaluator(
176
+ &self,
177
+ name: &str,
178
+ memory_json: &str,
179
+ state_json: &str,
180
+ ) -> Option<ActionResult>;
181
+ }
182
+
183
+ /// Global plugin instance for WASM
184
+ static WASM_PLUGIN: Mutex<Option<Box<dyn WasmPlugin>>> = Mutex::new(None);
185
+
186
+ /// Register a plugin for WASM export
187
+ pub fn register_wasm_plugin<P: WasmPlugin + 'static>(plugin: P) {
188
+ let mut instance = WASM_PLUGIN.lock().unwrap();
189
+ *instance = Some(Box::new(plugin));
190
+ }
191
+
192
+ // ============================================================================
193
+ // WASM Export Functions
194
+ // ============================================================================
195
+
196
+ /// Get the plugin manifest as JSON
197
+ #[wasm_bindgen]
198
+ pub fn get_manifest() -> String {
199
+ let instance = WASM_PLUGIN.lock().unwrap();
200
+ match &*instance {
201
+ Some(plugin) => serde_json::to_string(&plugin.manifest()).unwrap_or_else(|e| {
202
+ format!(r#"{{"error": "{}"}}"#, e)
203
+ }),
204
+ None => r#"{"error": "No plugin registered"}"#.to_string(),
205
+ }
206
+ }
207
+
208
+ /// Initialize the plugin
209
+ #[wasm_bindgen]
210
+ pub fn init(config_json: &str) {
211
+ let mut instance = WASM_PLUGIN.lock().unwrap();
212
+ if let Some(plugin) = instance.as_mut() {
213
+ let _ = plugin.init(config_json);
214
+ }
215
+ }
216
+
217
+ /// Validate an action
218
+ #[wasm_bindgen]
219
+ pub fn validate_action(name: &str, memory_json: &str, state_json: &str) -> bool {
220
+ let instance = WASM_PLUGIN.lock().unwrap();
221
+ match &*instance {
222
+ Some(plugin) => plugin.validate_action(name, memory_json, state_json),
223
+ None => false,
224
+ }
225
+ }
226
+
227
+ /// Invoke an action
228
+ #[wasm_bindgen]
229
+ pub fn invoke_action(
230
+ name: &str,
231
+ memory_json: &str,
232
+ state_json: &str,
233
+ options_json: &str,
234
+ ) -> String {
235
+ let instance = WASM_PLUGIN.lock().unwrap();
236
+ match &*instance {
237
+ Some(plugin) => {
238
+ let result = plugin.invoke_action(name, memory_json, state_json, options_json);
239
+ serde_json::to_string(&result).unwrap_or_else(|e| {
240
+ format!(r#"{{"success": false, "error": "{}"}}"#, e)
241
+ })
242
+ }
243
+ None => r#"{"success": false, "error": "No plugin registered"}"#.to_string(),
244
+ }
245
+ }
246
+
247
+ /// Get provider data
248
+ #[wasm_bindgen]
249
+ pub fn get_provider(name: &str, memory_json: &str, state_json: &str) -> String {
250
+ let instance = WASM_PLUGIN.lock().unwrap();
251
+ match &*instance {
252
+ Some(plugin) => {
253
+ let result = plugin.get_provider(name, memory_json, state_json);
254
+ serde_json::to_string(&result).unwrap_or_else(|_| {
255
+ r#"{"text": null, "values": null, "data": null}"#.to_string()
256
+ })
257
+ }
258
+ None => r#"{"text": null, "values": null, "data": null}"#.to_string(),
259
+ }
260
+ }
261
+
262
+ /// Validate an evaluator
263
+ #[wasm_bindgen]
264
+ pub fn validate_evaluator(name: &str, memory_json: &str, state_json: &str) -> bool {
265
+ let instance = WASM_PLUGIN.lock().unwrap();
266
+ match &*instance {
267
+ Some(plugin) => plugin.validate_evaluator(name, memory_json, state_json),
268
+ None => false,
269
+ }
270
+ }
271
+
272
+ /// Invoke an evaluator
273
+ #[wasm_bindgen]
274
+ pub fn invoke_evaluator(name: &str, memory_json: &str, state_json: &str) -> String {
275
+ let instance = WASM_PLUGIN.lock().unwrap();
276
+ match &*instance {
277
+ Some(plugin) => match plugin.invoke_evaluator(name, memory_json, state_json) {
278
+ Some(result) => serde_json::to_string(&result).unwrap_or_else(|_| "null".to_string()),
279
+ None => "null".to_string(),
280
+ },
281
+ None => "null".to_string(),
282
+ }
283
+ }
284
+
285
+ // ============================================================================
286
+ // Memory Allocation for String Passing
287
+ // ============================================================================
288
+
289
+ /// Allocate memory for string passing
290
+ #[wasm_bindgen]
291
+ pub fn alloc(size: usize) -> *mut u8 {
292
+ let mut buf = Vec::with_capacity(size);
293
+ let ptr = buf.as_mut_ptr();
294
+ std::mem::forget(buf);
295
+ ptr
296
+ }
297
+
298
+ /// Deallocate memory
299
+ #[wasm_bindgen]
300
+ pub fn dealloc(ptr: *mut u8, size: usize) {
301
+ unsafe {
302
+ let _ = Vec::from_raw_parts(ptr, 0, size);
303
+ }
304
+ }
305
+
306
+ /// Macro to export a WASM plugin
307
+ ///
308
+ /// This macro should be called in your plugin's lib.rs to make it loadable
309
+ /// from the TypeScript runtime via WASM.
310
+ #[macro_export]
311
+ macro_rules! elizaos_wasm_plugin {
312
+ ($plugin:expr) => {
313
+ #[wasm_bindgen(start)]
314
+ pub fn wasm_plugin_init() {
315
+ console_error_panic_hook::set_once();
316
+ $crate::interop::wasm_plugin::register_wasm_plugin($plugin);
317
+ }
318
+ };
319
+ }
320
+