@statelyai/agent 2.0.0-next.1 → 2.0.0-next.3

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 (51) hide show
  1. package/.changeset/grumpy-dolphins-think.md +17 -0
  2. package/.changeset/old-teachers-tap.md +5 -0
  3. package/.changeset/pink-eagles-deliver.md +13 -0
  4. package/.changeset/pre.json +9 -1
  5. package/.changeset/quiet-turtles-do.md +7 -0
  6. package/.changeset/smart-yaks-pull.md +23 -0
  7. package/.changeset/sweet-clouds-mix.md +16 -0
  8. package/.changeset/swift-mangos-rush.md +5 -0
  9. package/.changeset/tough-ways-rhyme.md +5 -0
  10. package/CHANGELOG.md +79 -0
  11. package/dist/index.d.mts +116 -100
  12. package/dist/index.d.ts +116 -100
  13. package/dist/index.js +102 -72
  14. package/dist/index.mjs +105 -75
  15. package/examples/chatbot.ts +2 -2
  16. package/examples/cot.ts +21 -73
  17. package/examples/customer-service-sim.ts +3 -3
  18. package/examples/email.ts +3 -5
  19. package/examples/example.ts +2 -2
  20. package/examples/goal.ts +2 -2
  21. package/examples/joke.ts +12 -12
  22. package/examples/jugs.ts +4 -7
  23. package/examples/learn-from-feedback.ts +123 -0
  24. package/examples/number.ts +2 -2
  25. package/examples/raffle.ts +2 -2
  26. package/examples/river-crossing.ts +4 -7
  27. package/examples/simple.ts +13 -10
  28. package/examples/summary.ts +2 -5
  29. package/examples/support.ts +38 -38
  30. package/examples/ticTacToe.ts +46 -4
  31. package/examples/todo.ts +3 -3
  32. package/examples/tutor.ts +2 -2
  33. package/examples/verify.ts +2 -2
  34. package/examples/weather-agent.ts +139 -0
  35. package/examples/weather.ts +26 -23
  36. package/examples/word.ts +8 -6
  37. package/package.json +2 -1
  38. package/src/agent.test.ts +37 -52
  39. package/src/agent.ts +93 -60
  40. package/src/decide.test.ts +56 -8
  41. package/src/decide.ts +42 -32
  42. package/src/strategies/chainOfThought.ts +50 -0
  43. package/src/{planners → strategies}/shortestPath.test.ts +4 -7
  44. package/src/strategies/shortestPath.ts +178 -0
  45. package/src/{planners → strategies}/simple.ts +25 -26
  46. package/src/templates/defaultText.ts +3 -0
  47. package/src/text.ts +13 -13
  48. package/src/types.ts +124 -83
  49. package/src/utils.ts +13 -1
  50. package/src/planners/shortestPath.ts +0 -177
  51. package/src/strategies/chain-of-note.ts +0 -106
package/dist/index.js CHANGED
@@ -40,7 +40,7 @@ module.exports = __toCommonJS(src_exports);
40
40
  // src/agent.ts
41
41
  var import_xstate4 = require("xstate");
42
42
 
43
- // src/planners/simple.ts
43
+ // src/strategies/simple.ts
44
44
  var import_ai3 = require("ai");
45
45
 
46
46
  // src/utils.ts
@@ -79,6 +79,15 @@ function getAllMachineTransitions(stateNode) {
79
79
  function wrapInXml(tagName, content) {
80
80
  return `<${tagName}>${content}</${tagName}>`;
81
81
  }
82
+ function convertToXml(obj) {
83
+ return Object.entries(obj).map(([key, value]) => {
84
+ if (typeof value === "object" && value !== null) {
85
+ return wrapInXml(key, convertToXml(value));
86
+ } else {
87
+ return wrapInXml(key, value);
88
+ }
89
+ }).join("");
90
+ }
82
91
  function randomId(prefix) {
83
92
  const timestamp = Date.now().toString(36);
84
93
  const random = Math.random().toString(36).substring(2, 9);
@@ -110,12 +119,16 @@ function isMachineActor(actor) {
110
119
  return "src" in actor && typeof actor.src === "object" && actor.src !== null && "definition" in actor.src;
111
120
  }
112
121
 
113
- // src/planners/simple.ts
122
+ // src/strategies/simple.ts
114
123
  var import_xstate3 = require("xstate");
115
124
 
125
+ // src/text.ts
126
+ var import_ai = require("ai");
127
+
116
128
  // src/templates/defaultText.ts
117
129
  var defaultTextTemplate = (data) => {
118
130
  const preamble = [
131
+ data.stateValue ? wrapInXml("stateValue", JSON.stringify(data.stateValue)) : void 0,
119
132
  data.context ? wrapInXml("context", JSON.stringify(data.context)) : void 0
120
133
  ].filter(Boolean).join("\n");
121
134
  return `
@@ -126,7 +139,6 @@ ${data.goal}
126
139
  };
127
140
 
128
141
  // src/text.ts
129
- var import_ai = require("ai");
130
142
  var import_xstate = require("xstate");
131
143
  async function getMessages(agent, prompt, options) {
132
144
  let messages = [];
@@ -215,35 +227,46 @@ async function agentDecide(agent, options) {
215
227
  ...options
216
228
  };
217
229
  const {
218
- planner = simplePlanner,
230
+ strategy = agent.strategy,
219
231
  goal,
232
+ allowedEvents,
220
233
  events = agent.events,
221
234
  state,
222
235
  machine,
223
236
  model = agent.model,
224
237
  messages,
225
- ...otherPlanInput
238
+ ...otherDecideInput
226
239
  } = resolvedOptions;
240
+ const filteredEventSchemas = allowedEvents ? Object.fromEntries(
241
+ Object.entries(events).filter(([key]) => {
242
+ return allowedEvents.includes(key);
243
+ })
244
+ ) : events;
227
245
  let attempts = 0;
228
246
  const maxAttempts = resolvedOptions.maxAttempts ?? 2;
229
- let plan;
247
+ let decision;
248
+ const minimalState = {
249
+ value: state.value,
250
+ context: state.context
251
+ };
230
252
  while (attempts++ < maxAttempts) {
231
- plan = await planner(agent, {
253
+ decision = await strategy(agent, {
232
254
  model,
233
255
  goal,
234
- events,
235
- state,
256
+ events: filteredEventSchemas,
257
+ state: minimalState,
236
258
  machine,
237
259
  messages,
238
260
  // TODO: fix UIMessage thing
239
- ...otherPlanInput
261
+ ...otherDecideInput
240
262
  });
241
- if (plan?.nextEvent) {
242
- agent.addPlan(plan);
243
- await resolvedOptions.execute?.(plan.nextEvent);
263
+ if (decision?.nextEvent) {
264
+ agent.addDecision(decision);
265
+ await resolvedOptions.execute?.(decision.nextEvent);
266
+ break;
244
267
  }
245
268
  }
246
- return plan;
269
+ return decision;
247
270
  }
248
271
  function fromDecision(agent, defaultInput) {
249
272
  return (0, import_xstate2.fromPromise)(async ({ input, self }) => {
@@ -257,19 +280,17 @@ function fromDecision(agent, defaultInput) {
257
280
  ...defaultInput,
258
281
  ...inputObject
259
282
  };
260
- const state = {
261
- value: snapshot.value,
262
- context: resolvedInput.context
263
- };
264
- const plan = await agentDecide(agent, {
283
+ const decision = await agentDecide(agent, {
265
284
  machine: parentRef.logic,
266
- state,
285
+ state: snapshot,
267
286
  execute: async (event) => {
268
287
  parentRef.send(event);
269
288
  },
270
- ...resolvedInput
289
+ ...resolvedInput,
290
+ // @ts-ignore
291
+ messages: resolvedInput.messages
271
292
  });
272
- return plan;
293
+ return decision;
273
294
  });
274
295
  }
275
296
  function getToolMap(_agent, input) {
@@ -315,37 +336,30 @@ function getToolMap(_agent, input) {
315
336
  return toolMap;
316
337
  }
317
338
 
318
- // src/planners/simple.ts
319
- var simplePlannerPromptTemplate = (data) => {
339
+ // src/strategies/simple.ts
340
+ var simpleStrategyPromptTemplate = (data) => {
320
341
  return `
321
- ${defaultTextTemplate(data)}
342
+ ${convertToXml(data)}
322
343
 
323
344
  Make at most one tool call to achieve the above goal. If the goal cannot be achieved with any tool calls, do not make any tool call.
324
345
  `.trim();
325
346
  };
326
- async function simplePlanner(agent, input) {
347
+ async function simpleStrategy(agent, input) {
327
348
  const toolMap = getToolMap(agent, input);
328
349
  if (!toolMap) {
329
350
  return void 0;
330
351
  }
331
- const prompt = simplePlannerPromptTemplate({
332
- context: input.state.context,
352
+ const prompt = simpleStrategyPromptTemplate({
353
+ stateValue: input.state.value,
354
+ context: input.context ?? input.state.context,
333
355
  goal: input.goal
334
356
  });
335
357
  const messages = await getMessages(agent, prompt, input);
336
358
  const model = input.model ? agent.wrap(input.model) : agent.model;
337
- const {
338
- state,
339
- machine,
340
- previousPlan,
341
- events,
342
- goal,
343
- model: _,
344
- ...rest
345
- } = input;
346
- const machineState = input.machine ? input.machine.resolveState({
359
+ const { state, machine, events, goal, model: _, ...rest } = input;
360
+ const machineState = input.machine && input.state ? input.machine.resolveState({
347
361
  ...input.state,
348
- context: input.state.context
362
+ context: input.state.context ?? {}
349
363
  }) : void 0;
350
364
  const result = await (0, import_ai3.generateText)({
351
365
  ...rest,
@@ -369,7 +383,8 @@ async function simplePlanner(agent, input) {
369
383
  return void 0;
370
384
  }
371
385
  return {
372
- planner: "simple",
386
+ id: randomId(),
387
+ strategy: "simple",
373
388
  goal: input.goal,
374
389
  goalState: input.state,
375
390
  nextEvent: singleResult.result,
@@ -486,12 +501,11 @@ var agentLogic = (0, import_xstate4.fromTransition)(
486
501
  });
487
502
  break;
488
503
  }
489
- case "agent.plan": {
490
- state.plans.push(event.plan);
504
+ case "agent.decision": {
505
+ state.decisions.push(event.decision);
491
506
  emit({
492
- type: "plan",
493
- // @ts-ignore TODO: fix types in XState
494
- plan: event.plan
507
+ type: "decision",
508
+ decision: event.decision
495
509
  });
496
510
  break;
497
511
  }
@@ -506,7 +520,7 @@ var agentLogic = (0, import_xstate4.fromTransition)(
506
520
  feedback: [],
507
521
  messages: [],
508
522
  observations: [],
509
- plans: []
523
+ decisions: []
510
524
  })
511
525
  );
512
526
  function createAgent({
@@ -515,7 +529,8 @@ function createAgent({
515
529
  model,
516
530
  events,
517
531
  context,
518
- planner = simplePlanner,
532
+ episodeId,
533
+ strategy = simpleStrategy,
519
534
  logic = agentLogic
520
535
  }) {
521
536
  return new Agent({
@@ -523,9 +538,10 @@ function createAgent({
523
538
  context,
524
539
  events,
525
540
  description,
526
- planner,
541
+ strategy,
527
542
  model,
528
- logic
543
+ logic,
544
+ episodeId
529
545
  });
530
546
  }
531
547
  var Agent = class extends import_xstate4.Actor {
@@ -538,17 +554,18 @@ var Agent = class extends import_xstate4.Actor {
538
554
  model,
539
555
  events,
540
556
  context,
541
- planner = simplePlanner
557
+ episodeId,
558
+ strategy = simpleStrategy
542
559
  }) {
543
560
  super(logic);
544
561
  this.model = model;
545
- this.episodeId = id ?? randomId();
562
+ this.episodeId = episodeId ?? randomId("episode-");
546
563
  this.name = name;
547
564
  this.description = description;
548
565
  this.events = events;
549
566
  this.context = context;
550
- this.planner = planner;
551
- this.types = {};
567
+ this.strategy = strategy;
568
+ this.id = id ?? randomId();
552
569
  this.start();
553
570
  }
554
571
  /**
@@ -557,6 +574,12 @@ var Agent = class extends import_xstate4.Actor {
557
574
  onMessage(fn) {
558
575
  return this.on("message", (ev) => fn(ev.message));
559
576
  }
577
+ /**
578
+ * Called whenever the agent (LLM assistant) receives some feedback.
579
+ */
580
+ onFeedback(fn) {
581
+ return this.on("feedback", (ev) => fn(ev.feedback));
582
+ }
560
583
  /**
561
584
  * Retrieves messages from the agent's short-term (local) memory.
562
585
  */
@@ -579,8 +602,8 @@ var Agent = class extends import_xstate4.Actor {
579
602
  addFeedback(feedbackInput) {
580
603
  const feedback = {
581
604
  ...feedbackInput,
605
+ comment: feedbackInput.comment ?? void 0,
582
606
  attributes: { ...feedbackInput.attributes },
583
- reward: feedbackInput.reward ?? 0,
584
607
  timestamp: feedbackInput.timestamp ?? Date.now(),
585
608
  episodeId: this.episodeId
586
609
  };
@@ -619,17 +642,17 @@ var Agent = class extends import_xstate4.Actor {
619
642
  getObservations() {
620
643
  return this.getSnapshot().context.observations;
621
644
  }
622
- addPlan(plan) {
645
+ addDecision(decision) {
623
646
  this.send({
624
- type: "agent.plan",
625
- plan
647
+ type: "agent.decision",
648
+ decision
626
649
  });
627
650
  }
628
651
  /**
629
652
  * Retrieves strategies from the agent's short-term (local) memory.
630
653
  */
631
- getPlans() {
632
- return this.getSnapshot().context.plans;
654
+ getDecisions() {
655
+ return this.getSnapshot().context.decisions;
633
656
  }
634
657
  interact(actorRef, getInput) {
635
658
  const actorRefCheck = isActorRef(actorRef) && actorRef.src;
@@ -639,15 +662,16 @@ var Agent = class extends import_xstate4.Actor {
639
662
  const agent = this;
640
663
  async function handleObservation(observationInput) {
641
664
  const observation = agent.addObservation(observationInput);
642
- const input = getInput?.(observation);
643
- if (input) {
644
- const res = await agentDecide(agent, {
665
+ const interactInput = getInput?.(observation);
666
+ if (interactInput) {
667
+ const decision = await agentDecide(agent, {
645
668
  machine,
646
669
  state: observation.state,
647
- ...input
670
+ ...interactInput
648
671
  });
649
- if (res?.nextEvent) {
650
- actorRef.send(res.nextEvent);
672
+ if (decision?.nextEvent) {
673
+ decision.nextEvent["_decision"] = decision.id;
674
+ actorRef.send(decision.nextEvent);
651
675
  }
652
676
  }
653
677
  prevState = observationInput.state;
@@ -657,11 +681,14 @@ var Agent = class extends import_xstate4.Actor {
657
681
  if (!subscribed || inspEvent.actorRef !== actorRef || inspEvent.type !== "@xstate.snapshot") {
658
682
  return;
659
683
  }
684
+ const decisionId = inspEvent.event["_decision"];
685
+ const decision = decisionId ? agent.getDecisions().find((d) => d.id === decisionId) : void 0;
660
686
  const observationInput = {
661
687
  event: inspEvent.event,
662
688
  prevState,
663
689
  state: inspEvent.snapshot,
664
- machine: actorRef.src
690
+ machine: actorRef.src,
691
+ goal: decision?.goal
665
692
  };
666
693
  await handleObservation(observationInput);
667
694
  }
@@ -669,10 +696,10 @@ var Agent = class extends import_xstate4.Actor {
669
696
  if (actorRef._processingStatus === 1) {
670
697
  handleObservation({
671
698
  prevState: void 0,
672
- event: { type: "" },
673
- // TODO: unknown events?
699
+ event: void 0,
674
700
  state: actorRef.getSnapshot(),
675
- machine: actorRef.src
701
+ machine: actorRef.src,
702
+ goal: void 0
676
703
  });
677
704
  }
678
705
  return {
@@ -690,11 +717,14 @@ var Agent = class extends import_xstate4.Actor {
690
717
  if (inspEvent.actorRef !== actorRef || inspEvent.type !== "@xstate.snapshot") {
691
718
  return;
692
719
  }
720
+ const decisionId = inspEvent.event["_decision"];
721
+ const decision = decisionId ? this.getDecisions().find((d) => d.id === decisionId) : void 0;
693
722
  const observationInput = {
694
723
  event: inspEvent.event,
695
724
  prevState,
696
725
  state: inspEvent.snapshot,
697
- machine: actorRef.src
726
+ machine: actorRef.src,
727
+ goal: decision?.goal
698
728
  };
699
729
  prevState = observationInput.state;
700
730
  this.addObservation(observationInput);
@@ -710,14 +740,14 @@ var Agent = class extends import_xstate4.Actor {
710
740
  });
711
741
  }
712
742
  /**
713
- * Resolves with an `AgentPlan` based on the information provided in the `options`, including:
743
+ * Resolves with an `AgentDecision` based on the information provided in the `options`, including:
714
744
  *
715
745
  * - The `goal` for the agent to achieve
716
746
  * - The observed current `state`
717
747
  * - The `machine` (e.g. a state machine) that specifies what can happen next
718
748
  * - Additional `context`
719
749
  */
720
- decide(opts) {
750
+ async decide(opts) {
721
751
  return agentDecide(this, opts);
722
752
  }
723
753
  };