@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.
- package/LICENSE +21 -0
- package/README.md +436 -0
- package/dist/packages/interop/tsconfig.tsbuildinfo +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/typescript/index.d.ts +33 -0
- package/dist/typescript/index.d.ts.map +1 -0
- package/dist/typescript/index.js +121 -0
- package/dist/typescript/python-bridge.d.ts +80 -0
- package/dist/typescript/python-bridge.d.ts.map +1 -0
- package/dist/typescript/python-bridge.js +334 -0
- package/dist/typescript/types.d.ts +301 -0
- package/dist/typescript/types.d.ts.map +1 -0
- package/dist/typescript/types.js +10 -0
- package/dist/typescript/wasm-loader.d.ts +32 -0
- package/dist/typescript/wasm-loader.d.ts.map +1 -0
- package/dist/typescript/wasm-loader.js +269 -0
- package/package.json +43 -0
- package/python/__init__.py +50 -0
- package/python/__pycache__/__init__.cpython-313.pyc +0 -0
- package/python/__pycache__/rust_ffi.cpython-313.pyc +0 -0
- package/python/__pycache__/ts_bridge.cpython-313.pyc +0 -0
- package/python/__pycache__/wasm_loader.cpython-313.pyc +0 -0
- package/python/bridge_server.py +505 -0
- package/python/rust_ffi.py +418 -0
- package/python/tests/__init__.py +1 -0
- package/python/tests/__pycache__/__init__.cpython-313.pyc +0 -0
- package/python/tests/__pycache__/test_bridge_server.cpython-313-pytest-9.0.2.pyc +0 -0
- package/python/tests/__pycache__/test_interop.cpython-313-pytest-9.0.2.pyc +0 -0
- package/python/tests/__pycache__/test_rust_ffi.cpython-313-pytest-9.0.2.pyc +0 -0
- package/python/tests/__pycache__/test_rust_ffi_loader.cpython-313-pytest-9.0.2.pyc +0 -0
- package/python/tests/test_bridge_server.py +525 -0
- package/python/tests/test_interop.py +319 -0
- package/python/tests/test_rust_ffi.py +352 -0
- package/python/tests/test_rust_ffi_loader.py +71 -0
- package/python/ts_bridge.py +452 -0
- package/python/ts_bridge_runner.mjs +284 -0
- package/python/wasm_loader.py +517 -0
- package/rust/ffi_exports.rs +362 -0
- package/rust/mod.rs +21 -0
- package/rust/py_loader.rs +412 -0
- package/rust/ts_loader.rs +467 -0
- 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
|
+
|