@statelyai/agent
Version:
Stateful agents that make decisions based on finite-state machine models
138 lines (132 loc) • 3.29 kB
text/typescript
import { assign, setup, assertEvent, createActor, createMachine } from 'xstate';
import { z } from 'zod';
import { createAgent, fromDecision } from '../src';
import { openai } from '@ai-sdk/openai';
import { getFromTerminal } from './helpers/helpers';
const agent = createAgent({
name: 'todo',
model: openai('gpt-4o'),
events: {
addTodo: z.object({
title: z.string().min(1).max(100).describe('The title of the todo'),
content: z.string().min(1).max(100).describe('The content of the todo'),
}),
deleteTodo: z.object({
index: z.number().describe('The index of the todo to delete'),
}),
toggleTodo: z
.object({
index: z.number().describe('The index of the todo to toggle'),
})
.describe('Toggle whether the todo item is done or not'),
doNothing: z.object({}).describe('Do nothing'),
},
});
interface Todo {
title: string;
content: string;
done: boolean;
}
const machine = setup({
types: {
context: {} as {
todos: Todo[];
command: string | null;
},
events: {} as
| typeof agent.types.events
| { type: 'assist'; command: string },
},
actors: { agent: fromDecision(agent), getFromTerminal },
}).createMachine({
context: {
command: null,
todos: [],
},
on: {
addTodo: {
actions: assign({
todos: ({ context, event }) => [
...context.todos,
{
title: event.title,
content: event.content,
done: false,
},
],
command: null,
}),
target: '.idle',
},
deleteTodo: {
actions: assign({
todos: ({ context, event }) => {
const todos = [...context.todos];
todos.splice(event.index, 1);
return todos;
},
command: null,
}),
target: '.idle',
},
toggleTodo: {
actions: assign({
todos: ({ context, event }) => {
const todos = context.todos.map((todo, i) => {
if (i === event.index) {
return {
...todo,
done: !todo.done,
};
}
return todo;
});
return todos;
},
command: null,
}),
target: '.idle',
},
doNothing: { target: '.idle' },
},
initial: 'idle',
states: {
idle: {
invoke: {
src: 'getFromTerminal',
input: '\nEnter a command:',
onDone: {
actions: assign({
command: ({ event }) => event.output,
}),
target: 'assisting',
},
},
on: {
assist: {
target: 'assisting',
actions: assign({
command: ({ event }) => event.command,
}),
},
},
},
assisting: {
invoke: {
src: 'agent',
input: (x) => ({
context: {
command: x.context.command,
todos: x.context.todos,
},
goal: 'Interpret the command as an action for this todo list; for example, "I need donuts" would add a todo item with the message "Get donuts".',
}),
},
},
},
});
const actor = createActor(machine);
actor.subscribe((s) => {
console.log(s.context.todos);
});
actor.start();