simple-agents-wasm 0.3.7 → 0.3.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "simple-agents-wasm",
3
- "version": "0.3.7",
3
+ "version": "0.3.9",
4
4
  "description": "Browser-compatible SimpleAgents client for OpenAI-compatible providers",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -38,6 +38,7 @@ export interface InitOutput {
38
38
  readonly wasmclient_streamEvents: (a: number, b: number, c: number, d: any, e: any, f: number) => any;
39
39
  readonly wasm_bindgen__convert__closures_____invoke__h9f53f643b01d7c8e: (a: number, b: number, c: any) => [number, number];
40
40
  readonly wasm_bindgen__convert__closures_____invoke__h05acb8c479b21d4b: (a: number, b: number, c: any, d: any) => void;
41
+ readonly wasm_bindgen__convert__closures_____invoke__hcbe4158cf6f0da5b: (a: number, b: number, c: any) => void;
41
42
  readonly __wbindgen_malloc: (a: number, b: number) => number;
42
43
  readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
43
44
  readonly __wbindgen_exn_store: (a: number) => void;
@@ -409,26 +409,31 @@ function __wbg_get_imports() {
409
409
  return ret;
410
410
  },
411
411
  __wbindgen_cast_0000000000000001: function(arg0, arg1) {
412
- // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 152, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
412
+ // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 157, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
413
413
  const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h9f53f643b01d7c8e);
414
414
  return ret;
415
415
  },
416
- __wbindgen_cast_0000000000000002: function(arg0) {
416
+ __wbindgen_cast_0000000000000002: function(arg0, arg1) {
417
+ // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 78, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
418
+ const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__hcbe4158cf6f0da5b);
419
+ return ret;
420
+ },
421
+ __wbindgen_cast_0000000000000003: function(arg0) {
417
422
  // Cast intrinsic for `F64 -> Externref`.
418
423
  const ret = arg0;
419
424
  return ret;
420
425
  },
421
- __wbindgen_cast_0000000000000003: function(arg0) {
426
+ __wbindgen_cast_0000000000000004: function(arg0) {
422
427
  // Cast intrinsic for `I64 -> Externref`.
423
428
  const ret = arg0;
424
429
  return ret;
425
430
  },
426
- __wbindgen_cast_0000000000000004: function(arg0, arg1) {
431
+ __wbindgen_cast_0000000000000005: function(arg0, arg1) {
427
432
  // Cast intrinsic for `Ref(String) -> Externref`.
428
433
  const ret = getStringFromWasm0(arg0, arg1);
429
434
  return ret;
430
435
  },
431
- __wbindgen_cast_0000000000000005: function(arg0) {
436
+ __wbindgen_cast_0000000000000006: function(arg0) {
432
437
  // Cast intrinsic for `U64 -> Externref`.
433
438
  const ret = BigInt.asUintN(64, arg0);
434
439
  return ret;
@@ -449,6 +454,10 @@ function __wbg_get_imports() {
449
454
  };
450
455
  }
451
456
 
457
+ function wasm_bindgen__convert__closures_____invoke__hcbe4158cf6f0da5b(arg0, arg1, arg2) {
458
+ wasm.wasm_bindgen__convert__closures_____invoke__hcbe4158cf6f0da5b(arg0, arg1, arg2);
459
+ }
460
+
452
461
  function wasm_bindgen__convert__closures_____invoke__h9f53f643b01d7c8e(arg0, arg1, arg2) {
453
462
  const ret = wasm.wasm_bindgen__convert__closures_____invoke__h9f53f643b01d7c8e(arg0, arg1, arg2);
454
463
  if (ret[1]) {
Binary file
@@ -11,6 +11,7 @@ export const wasmclient_runYamlString: (a: number, b: number, c: number, d: any,
11
11
  export const wasmclient_streamEvents: (a: number, b: number, c: number, d: any, e: any, f: number) => any;
12
12
  export const wasm_bindgen__convert__closures_____invoke__h9f53f643b01d7c8e: (a: number, b: number, c: any) => [number, number];
13
13
  export const wasm_bindgen__convert__closures_____invoke__h05acb8c479b21d4b: (a: number, b: number, c: any, d: any) => void;
14
+ export const wasm_bindgen__convert__closures_____invoke__hcbe4158cf6f0da5b: (a: number, b: number, c: any) => void;
14
15
  export const __wbindgen_malloc: (a: number, b: number) => number;
15
16
  export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
16
17
  export const __wbindgen_exn_store: (a: number) => void;
package/rust/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "simple-agents-wasm-rust"
3
- version = "0.3.7"
3
+ version = "0.3.9"
4
4
  edition = "2021"
5
5
  license = "MIT OR Apache-2.0"
6
6
 
package/rust/src/lib.rs CHANGED
@@ -3,6 +3,7 @@ use serde::{Deserialize, Serialize};
3
3
  use serde_json::{json, Map as JsonMap, Value as JsonValue};
4
4
  use std::cell::RefCell;
5
5
  use std::collections::HashMap;
6
+ use wasm_bindgen::closure::Closure;
6
7
  use wasm_bindgen::prelude::*;
7
8
  use wasm_bindgen::JsCast;
8
9
  use wasm_bindgen_futures::JsFuture;
@@ -87,6 +88,7 @@ struct WorkflowDoc {
87
88
 
88
89
  #[derive(Deserialize, Clone)]
89
90
  struct GraphWorkflowDoc {
91
+ id: Option<String>,
90
92
  model: Option<String>,
91
93
  entry_node: String,
92
94
  nodes: Vec<GraphWorkflowNode>,
@@ -126,6 +128,7 @@ struct GraphLlmCall {
126
128
  temperature: Option<f64>,
127
129
  messages_path: Option<String>,
128
130
  append_prompt_as_user: Option<bool>,
131
+ stream: Option<bool>,
129
132
  }
130
133
 
131
134
  #[derive(Deserialize, Clone)]
@@ -272,6 +275,8 @@ struct WorkflowRunOptions {
272
275
  trace: Option<JsonValue>,
273
276
  #[serde(skip)]
274
277
  functions_js: Option<JsValue>,
278
+ #[serde(skip)]
279
+ on_event_js: Option<Function>,
275
280
  /// Injected by JS as `__fetchImpl` so HTTP calls use the same fetch as tests (no global race).
276
281
  #[serde(skip)]
277
282
  fetch_js: Option<JsValue>,
@@ -319,6 +324,16 @@ fn json_value_to_js_plain(value: &JsonValue) -> Result<JsValue, JsValue> {
319
324
  js_sys::JSON::parse(&s).map_err(|_| js_error("failed to parse JSON for JS"))
320
325
  }
321
326
 
327
+ fn emit_workflow_event(on_event: &Option<Function>, event: JsonValue) -> Result<(), JsValue> {
328
+ if let Some(callback) = on_event {
329
+ let event_js = json_value_to_js_plain(&event)?;
330
+ callback
331
+ .call1(&JsValue::NULL, &event_js)
332
+ .map_err(|_| js_error("failed to call workflow stream callback"))?;
333
+ }
334
+ Ok(())
335
+ }
336
+
322
337
  fn now_millis() -> f64 {
323
338
  let global = js_sys::global();
324
339
  let performance = Reflect::get(&global, &JsValue::from_str("performance")).ok();
@@ -1439,6 +1454,7 @@ impl WasmClient {
1439
1454
  telemetry: None,
1440
1455
  trace: None,
1441
1456
  functions_js: None,
1457
+ on_event_js: None,
1442
1458
  fetch_js: None,
1443
1459
  };
1444
1460
  if let Some(options_js) = workflow_options {
@@ -1452,6 +1468,14 @@ impl WasmClient {
1452
1468
  Some(v)
1453
1469
  }
1454
1470
  });
1471
+ let on_event_value = Reflect::get(&options_js, &JsValue::from_str("onEvent")).ok();
1472
+ options.on_event_js = on_event_value.and_then(|v| {
1473
+ if v.is_undefined() || v.is_null() {
1474
+ None
1475
+ } else {
1476
+ v.dyn_into::<Function>().ok()
1477
+ }
1478
+ });
1455
1479
  let fetch_js = Reflect::get(&options_js, &JsValue::from_str("__fetchImpl")).ok();
1456
1480
  options.fetch_js = fetch_js;
1457
1481
  }
@@ -1499,6 +1523,14 @@ impl WasmClient {
1499
1523
  let total_reasoning_tokens: u64 = 0;
1500
1524
  let mut llm_nodes_without_usage: Vec<String> = Vec::new();
1501
1525
 
1526
+ emit_workflow_event(
1527
+ &options.on_event_js,
1528
+ json!({
1529
+ "event_type": "workflow_started",
1530
+ "workflow_id": graph_doc.id.clone().unwrap_or_else(|| "wasm_workflow".to_string())
1531
+ }),
1532
+ )?;
1533
+
1502
1534
  while !pointer.is_empty() {
1503
1535
  iterations += 1;
1504
1536
  if iterations > 1000 {
@@ -1582,13 +1614,65 @@ impl WasmClient {
1582
1614
  };
1583
1615
 
1584
1616
  let opts = json!({ "temperature": llm.temperature });
1585
- let completion_js = self
1586
- .complete(
1587
- model,
1588
- prompt_js,
1589
- Some(json_value_to_js_plain(&opts)?),
1590
- )
1591
- .await?;
1617
+ let completion_js = if llm.stream.unwrap_or(false) && options.on_event_js.is_some() {
1618
+ let node_id = node.id.clone();
1619
+ let step_id = node.id.clone();
1620
+ let workflow_on_event = options
1621
+ .on_event_js
1622
+ .clone()
1623
+ .ok_or_else(|| js_error("missing workflow stream callback"))?;
1624
+ let mapped_stream_cb = Closure::wrap(Box::new(move |stream_event_js: JsValue| {
1625
+ let stream_event: JsonValue =
1626
+ serde_wasm_bindgen::from_value(stream_event_js).unwrap_or(JsonValue::Null);
1627
+ let stream_event_type = stream_event
1628
+ .get("eventType")
1629
+ .and_then(JsonValue::as_str)
1630
+ .unwrap_or_default();
1631
+
1632
+ if stream_event_type == "delta" {
1633
+ if let Some(delta) = stream_event
1634
+ .get("delta")
1635
+ .and_then(|v| v.get("content"))
1636
+ .and_then(JsonValue::as_str)
1637
+ {
1638
+ let workflow_event = json!({
1639
+ "event_type": "node_stream_delta",
1640
+ "node_id": node_id.clone(),
1641
+ "step_id": step_id.clone(),
1642
+ "delta": delta
1643
+ });
1644
+ if let Ok(event_js) = json_value_to_js_plain(&workflow_event) {
1645
+ let _ = workflow_on_event.call1(&JsValue::NULL, &event_js);
1646
+ }
1647
+ }
1648
+ } else if stream_event_type == "done" {
1649
+ let workflow_event = json!({
1650
+ "event_type": "node_stream_snapshot",
1651
+ "node_id": node_id.clone(),
1652
+ "step_id": step_id.clone(),
1653
+ "metadata": { "is_complete": true }
1654
+ });
1655
+ if let Ok(event_js) = json_value_to_js_plain(&workflow_event) {
1656
+ let _ = workflow_on_event.call1(&JsValue::NULL, &event_js);
1657
+ }
1658
+ }
1659
+ }) as Box<dyn FnMut(JsValue)>);
1660
+ let stream_callback_fn: Function =
1661
+ mapped_stream_cb.as_ref().unchecked_ref::<Function>().clone();
1662
+ let result = self
1663
+ .stream_events(
1664
+ model,
1665
+ prompt_js,
1666
+ stream_callback_fn,
1667
+ Some(json_value_to_js_plain(&opts)?),
1668
+ )
1669
+ .await;
1670
+ drop(mapped_stream_cb);
1671
+ result?
1672
+ } else {
1673
+ self.complete(model, prompt_js, Some(json_value_to_js_plain(&opts)?))
1674
+ .await?
1675
+ };
1592
1676
  let completion: JsonValue = serde_wasm_bindgen::from_value(completion_js)
1593
1677
  .map_err(|_| js_error("failed to parse completion result"))?;
1594
1678
 
@@ -1797,6 +1881,17 @@ impl WasmClient {
1797
1881
  trace.push(node.id.clone());
1798
1882
  }
1799
1883
 
1884
+ emit_workflow_event(
1885
+ &options.on_event_js,
1886
+ json!({
1887
+ "event_type": "workflow_completed",
1888
+ "workflow_id": raw_doc
1889
+ .get("id")
1890
+ .and_then(JsonValue::as_str)
1891
+ .unwrap_or("wasm_workflow")
1892
+ }),
1893
+ )?;
1894
+
1800
1895
  let total_elapsed_ms = (now_millis() - workflow_started).max(0.0) as u64;
1801
1896
  let terminal_node = trace.last().cloned().unwrap_or_default();
1802
1897
  let workflow_id = raw_doc