Skip to content

Commit 13549e3

Browse files
Merge pull request #99 from restackio/agentTodo
Add agent todo
2 parents 0e57c01 + c7743c1 commit 13549e3

23 files changed

+567
-0
lines changed

agent-todo/.env.example

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
# For inference
3+
RESTACK_API_KEY=
4+
5+
# For Restack Cloud deployment
6+
RESTACK_ENGINE_ID=
7+
RESTACK_ENGINE_ADDRESS=
8+
RESTACK_ENGINE_API_KEY=
9+
RESTACK_ENGINE_API_ADDRESS=
10+

agent-todo/chat_post.png

40.6 KB
Loading

agent-todo/chat_put.png

57.7 KB
Loading

agent-todo/chat_run.png

128 KB
Loading

agent-todo/eventAgent.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { client } from "./src/client";
2+
3+
export type EventInput = {
4+
agentId: string;
5+
runId: string;
6+
};
7+
8+
async function eventAgent(input: EventInput) {
9+
try {
10+
await client.sendAgentEvent({
11+
event: {
12+
name: "message",
13+
input: { content: "Sales on boots?" },
14+
},
15+
agent: {
16+
agentId: input.agentId,
17+
runId: input.runId,
18+
},
19+
});
20+
21+
await client.sendAgentEvent({
22+
event: {
23+
name: "end",
24+
},
25+
agent: {
26+
agentId: input.agentId,
27+
runId: input.runId,
28+
},
29+
});
30+
31+
process.exit(0); // Exit the process successfully
32+
} catch (error) {
33+
console.error("Error sending event to agent:", error);
34+
process.exit(1); // Exit the process with an error code
35+
}
36+
}
37+
38+
eventAgent({
39+
agentId: "your-agent-id",
40+
runId: "your-run-id",
41+
});

agent-todo/package.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"name": "agent-todo",
3+
"version": "0.0.1",
4+
"description": "Restack Agent managing todos",
5+
"scripts": {
6+
"dev": "open-cli http://localhost:5233 && tsx watch --include src src/services.ts",
7+
"build": "tsc --build",
8+
"clean": "rm -rf node_modules",
9+
"schedule": "tsx scheduleAgent.ts",
10+
"event": "tsx eventAgent.ts"
11+
},
12+
"dependencies": {
13+
"@restackio/ai": "^0.0.104",
14+
"@temporalio/workflow": "1.11.6",
15+
"openai": "^4.80.1",
16+
"zod": "^3.24.1"
17+
},
18+
"devDependencies": {
19+
"@types/node": "20.16.9",
20+
"dotenv-cli": "^7.4.2",
21+
"open-cli": "^8.0.0",
22+
"prettier": "3.3.3",
23+
"tsx": "4.19.2",
24+
"typescript": "^5.7.2"
25+
}
26+
}

agent-todo/readme.md

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# Restack Agent Todos example
2+
3+
A sample repository with an agent managing todos.
4+
5+
## Requirements
6+
7+
- **Node 20+**
8+
9+
## Start Restack
10+
11+
To start Restack, use the following Docker command:
12+
13+
```bash
14+
docker run -d --pull always --name restack -p 5233:5233 -p 6233:6233 -p 7233:7233 ghcr.io/restackio/restack:main
15+
```
16+
17+
## Install dependencies and start services
18+
19+
```bash
20+
npm install
21+
npm run dev
22+
```
23+
24+
This will start a Node.js app with Restack Services.
25+
Your code will be running and syncing with Restack to execute agents.
26+
27+
## Run agents
28+
29+
### from UI
30+
31+
You can run agents from the UI by clicking the "Run" button.
32+
33+
![Run agents from UI](./chat_post.png)
34+
35+
### from API
36+
37+
You can run agents from the API by using the generated endpoint:
38+
39+
`POST http://localhost:6233/api/agents/agentTodo`
40+
41+
### from any client
42+
43+
You can run agents with any client connected to Restack, for example:
44+
45+
```bash
46+
npm run schedule
47+
```
48+
49+
executes `scheduleAgent.ts` which will connect to Restack and execute the `agentTodo` agent.
50+
51+
## Send events to the Agent
52+
53+
### from UI
54+
55+
You can send events like message or end from the UI.
56+
57+
![Send events from UI](./chat_put.png)
58+
59+
And see the events in the run:
60+
61+
![See events in UI](./chat_run.png)
62+
63+
### from API
64+
65+
You can send events to the agent by using the following endpoint:
66+
67+
`PUT http://localhost:6233/api/agents/agentTodo/:agentId/:runId`
68+
69+
with the payload:
70+
71+
```json
72+
{
73+
"eventName": "message",
74+
"eventInput": { "content": "Create todo to send email to CEO" }
75+
}
76+
```
77+
78+
to send messages to the agent.
79+
80+
or
81+
82+
```json
83+
{
84+
"eventName": "end"
85+
}
86+
```
87+
88+
to end the conversation with the agent.
89+
90+
### from any client
91+
92+
You can send event to the agent with any client connected to Restack, for example:
93+
94+
Modify agentId and runId in eventAgent.ts and then run:
95+
96+
```bash
97+
npm run event
98+
```
99+
100+
It will connect to Restack and send 2 events to the agent, one to generate another agent and another one to end the conversation.
101+
102+
## Deploy on Restack Cloud
103+
104+
To deploy the application on Restack, you can create an account at [https://console.restack.io](https://console.restack.io)

agent-todo/scheduleAgent.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { client } from "./src/client";
2+
import { agentChatTool } from "./src/agents/agent";
3+
export type InputSchedule = {
4+
name: string;
5+
};
6+
7+
async function scheduleAgent(input: InputSchedule) {
8+
try {
9+
const agentId = `${Date.now()}-${agentChatTool.name}`;
10+
const runId = await client.scheduleAgent({
11+
agentName: agentChatTool.name,
12+
agentId,
13+
input,
14+
});
15+
16+
const result = await client.getAgentResult({ agentId, runId });
17+
18+
console.log("Agent result:", result);
19+
20+
process.exit(0); // Exit the process successfully
21+
} catch (error) {
22+
console.error("Error scheduling agent:", error);
23+
process.exit(1); // Exit the process with an error code
24+
}
25+
}
26+
27+
scheduleAgent({
28+
name: "test",
29+
});

agent-todo/src/agents/agent.ts

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import {
2+
defineEvent,
3+
onEvent,
4+
condition,
5+
log,
6+
step,
7+
} from "@restackio/ai/agent";
8+
import * as functions from "../functions";
9+
import { childExecute } from "@restackio/ai/workflow";
10+
import { executeTodoWorkflow } from "../workflows/executeTodo";
11+
12+
export type EndEvent = {
13+
end: boolean;
14+
};
15+
16+
export const messageEvent = defineEvent<functions.Message[]>("message");
17+
export const endEvent = defineEvent("end");
18+
19+
type agentTodoOutput = {
20+
messages: functions.Message[];
21+
};
22+
23+
export async function agentTodo(): Promise<agentTodoOutput> {
24+
let endReceived = false;
25+
let messages: functions.Message[] = [];
26+
27+
const tools = await step<typeof functions>({}).getTools();
28+
29+
onEvent(messageEvent, async ({ content }: functions.Message) => {
30+
messages.push({ role: "user", content: content?.toString() ?? "" });
31+
const result = await step<typeof functions>({}).llmChat({
32+
messages,
33+
tools,
34+
});
35+
36+
messages.push(result);
37+
38+
if (result.tool_calls) {
39+
log.info("result.tool_calls", { result });
40+
for (const toolCall of result.tool_calls) {
41+
switch (toolCall.function.name) {
42+
case "createTodo":
43+
log.info("createTodo", { toolCall });
44+
const toolResult = await step<typeof functions>({}).createTodo(
45+
JSON.parse(toolCall.function.arguments)
46+
);
47+
48+
messages.push({
49+
role: "tool",
50+
tool_call_id: toolCall.id,
51+
content: toolResult,
52+
});
53+
54+
const toolChatResult = await step<typeof functions>({}).llmChat({
55+
messages,
56+
tools,
57+
});
58+
59+
messages.push(toolChatResult);
60+
61+
break;
62+
case "executeTodoWorkflow":
63+
log.info("executeTodoWorkflow", { toolCall });
64+
const workflowId = `executeTodoWorkflow-${new Date().getTime()}`;
65+
const workflowResult = await childExecute({
66+
child: executeTodoWorkflow,
67+
childId: workflowId,
68+
input: JSON.parse(toolCall.function.arguments),
69+
});
70+
71+
messages.push({
72+
role: "tool",
73+
tool_call_id: toolCall.id,
74+
content: JSON.stringify(workflowResult),
75+
});
76+
77+
const toolWorkflowResult = await step<typeof functions>({}).llmChat(
78+
{
79+
messages,
80+
tools,
81+
}
82+
);
83+
84+
messages.push(toolWorkflowResult);
85+
86+
break;
87+
default:
88+
break;
89+
}
90+
}
91+
}
92+
return messages;
93+
});
94+
95+
onEvent(endEvent, async () => {
96+
endReceived = true;
97+
});
98+
99+
await condition(() => endReceived);
100+
101+
log.info("end condition met");
102+
return { messages };
103+
}

agent-todo/src/agents/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from "./agent";

0 commit comments

Comments
 (0)