Skip to content

Commit 090fe2a

Browse files
committed
feat: ai-agent example
1 parent b0887f7 commit 090fe2a

File tree

12 files changed

+497
-0
lines changed

12 files changed

+497
-0
lines changed

examples/ai-agent/.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# OpenAI Configuration
2+
OPENAI_API_KEY=sk-xxxxxxxxxxxx
3+
API_NINJA_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # Optional [will give dummy weather if not (100 F)]

examples/ai-agent/.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
node_modules/
2+
build/
3+
.turbo/
4+
.env
5+
.env.local

examples/ai-agent/actors/app.ts

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { actor, setup } from "actor-core";
2+
import { generateText, jsonSchema, tool } from "ai";
3+
import { openai } from "@ai-sdk/openai";
4+
import { getWeather } from "../utils/weather";
5+
import dotenv from "dotenv";
6+
7+
dotenv.config();
8+
9+
export type Message = { role: "user" | "assistant"; content: string; timestamp: number; }
10+
11+
const aiAgent = actor({
12+
// State is automatically persisted
13+
state: {
14+
messages: [] as Message[]
15+
},
16+
17+
actions: {
18+
// Get conversation history
19+
getMessages: (c) => c.state.messages,
20+
21+
// Send a message to the AI and get a response
22+
sendMessage: async (c, userMessage: string) => {
23+
// Add user message to conversation
24+
const userMsg: Message = {
25+
role: "user",
26+
content: userMessage,
27+
timestamp: Date.now()
28+
};
29+
c.state.messages.push(userMsg);
30+
31+
// Generate AI response using Vercel AI SDK with tools
32+
const out = await generateText({
33+
model: openai("o3-mini"),
34+
messages: c.state.messages,
35+
tools: {
36+
weather: tool({
37+
description: 'Get the weather in a location',
38+
parameters: jsonSchema<{ coords: { longitude: number, latitude: number } }>({
39+
type: 'object',
40+
properties: {
41+
coords: {
42+
type: 'object',
43+
description: 'The location to get the weather for',
44+
properties: {
45+
longitude: {
46+
type: 'number',
47+
description: 'Longitude of the location'
48+
},
49+
latitude: {
50+
type: 'number',
51+
description: 'Latitude of the location'
52+
}
53+
},
54+
required: ['longitude', 'latitude'],
55+
additionalProperties: false
56+
}
57+
},
58+
required: ['coords'],
59+
additionalProperties: false
60+
}),
61+
execute: async ({ coords }) => {
62+
return await getWeather(coords);
63+
}
64+
}),
65+
},
66+
maxSteps: 2,
67+
});
68+
69+
const { text } = out;
70+
71+
// Add AI response to conversation
72+
const assistantMsg: Message = {
73+
role: "assistant",
74+
content: text,
75+
timestamp: Date.now()
76+
};
77+
c.state.messages.push(assistantMsg);
78+
79+
// Broadcast the new message to all connected clients
80+
c.broadcast("messageReceived", assistantMsg);
81+
82+
return assistantMsg;
83+
},
84+
}
85+
});
86+
87+
// Create and export the app
88+
export const app = setup({
89+
actors: { aiAgent },
90+
});
91+
92+
// Export type for client type checking
93+
export type App = typeof app;

examples/ai-agent/package.json

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
{
2+
"name": "ai-agent",
3+
"version": "0.8.0",
4+
"private": true,
5+
"type": "module",
6+
"main": "src/index.ts",
7+
"scripts": {
8+
"dev:actors": "npx @actor-core/cli@latest dev actors/app.ts",
9+
"dev:frontend": "react-scripts start",
10+
"build": "react-scripts build",
11+
"check-types": "tsc --noEmit",
12+
"test": "vitest run"
13+
},
14+
"dependencies": {
15+
"@actor-core/react": "workspace:*",
16+
"@ai-sdk/openai": "^1.3.16",
17+
"@types/react": "^19",
18+
"@types/react-dom": "^19",
19+
"actor-core": "workspace:*",
20+
"dotenv": "^16.5.0",
21+
"react": "^19",
22+
"react-dom": "^19",
23+
"react-scripts": "^5.0.1"
24+
},
25+
"devDependencies": {
26+
"@actor-core/cli": "workspace:*",
27+
"actor-core": "workspace:*",
28+
"typescript": "^5.5.2"
29+
},
30+
"example": {
31+
"platforms": [
32+
"*"
33+
]
34+
},
35+
"browserslist": {
36+
"production": [
37+
">0.2%",
38+
"not dead",
39+
"not op_mini all"
40+
],
41+
"development": [
42+
"last 1 chrome version",
43+
"last 1 firefox version",
44+
"last 1 safari version"
45+
]
46+
},
47+
"resolutions": {
48+
"react@^19": "^19.0.0",
49+
"react-dom@^19": "^19.0.0",
50+
"react@^18": "^18.3"
51+
}
52+
}

examples/ai-agent/public/github.css

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
body, html {
2+
margin: 0;
3+
padding: 0;
4+
padding-top: 16px;
5+
width: 100%;
6+
height: 100%;
7+
}
8+
#example--repo-ref {
9+
position: fixed;
10+
top: 0px;
11+
left: 0px;
12+
cursor: pointer;
13+
background-color: rgb(243, 243, 243);
14+
height: 24px;
15+
width: 100%;
16+
padding: 8px 8px;
17+
}
18+
#example--github-icon {
19+
height: 24px;
20+
float: left;
21+
}
22+
#example--repo-link {
23+
height: 24px;
24+
margin-left: 8px;
25+
color: rgb(45, 50, 55);
26+
font-weight: bold;
27+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
28+
font-size: 15px;
29+
vertical-align: middle;
30+
}
31+
32+
#example--repo-ref:hover #example--repo-link {
33+
color: black;
34+
}
35+
#example--repo-ref:hover svg {
36+
fill: black !important;
37+
}

examples/ai-agent/public/index.html

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
6+
<meta name="theme-color" content="#000000">
7+
<title>AI Agent</title>
8+
</head>
9+
<link rel="stylesheet" href="github.css">
10+
<body>
11+
<!-- Github Notch -->
12+
<div id="example--repo-ref">
13+
<a id="example--github-icon" href="https://github.com/rivet-gg/actor-core/tree/main/examples/ai-agent" target="_blank">
14+
<svg height="24" width="24" viewBox="0 0 16 16" style="fill: #24292e;">
15+
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path>
16+
</svg>
17+
</a>
18+
<a id="example--repo-link" href="https://github.com/rivet-gg/actor-core/tree/main/examples/ai-agent">@rivet-gg/actor-core</a>
19+
</div>
20+
<noscript>
21+
You need to enable JavaScript to run this app.
22+
</noscript>
23+
<div id="root"></div>
24+
25+
<link rel="stylesheet" href="./main.css">
26+
</body>
27+
</html>

examples/ai-agent/public/main.css

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
.app {
2+
max-width: 800px;
3+
margin: 0 auto;
4+
padding: 20px;
5+
font-family:system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif
6+
}
7+
8+
h1 {
9+
text-align: center;
10+
color: #333;
11+
}
12+
13+
.ai-chat {
14+
display: flex;
15+
flex-direction: column;
16+
height: 80vh;
17+
border: 1px solid #ddd;
18+
border-radius: 8px;
19+
overflow: hidden;
20+
}
21+
22+
.messages {
23+
flex: 1;
24+
overflow-y: auto;
25+
padding: 20px;
26+
}
27+
28+
.message {
29+
display: flex;
30+
margin-bottom: 20px;
31+
align-items: flex-start;
32+
}
33+
34+
.message .avatar {
35+
font-size: 24px;
36+
margin-right: 12px;
37+
}
38+
39+
.message .content {
40+
background: #f5f5f5;
41+
padding: 12px;
42+
border-radius: 8px;
43+
max-width: 70%;
44+
}
45+
46+
.message.user .content {
47+
background: #007bff;
48+
color: white;
49+
}
50+
51+
.message.assistant .content {
52+
background: #f8f9fa;
53+
}
54+
55+
.message.loading .content {
56+
font-style: italic;
57+
color: #666;
58+
}
59+
60+
.empty-message {
61+
text-align: center;
62+
color: #666;
63+
margin-top: 40px;
64+
}
65+
66+
.input-area {
67+
display: flex;
68+
padding: 20px;
69+
border-top: 1px solid #ddd;
70+
background: white;
71+
}
72+
73+
.input-area input {
74+
flex: 1;
75+
padding: 10px;
76+
border: 1px solid #ddd;
77+
border-radius: 4px;
78+
margin-right: 10px;
79+
}
80+
81+
.input-area button {
82+
padding: 10px 20px;
83+
background: #007bff;
84+
color: white;
85+
border: none;
86+
border-radius: 4px;
87+
cursor: pointer;
88+
}
89+
90+
.input-area button:disabled {
91+
background: #ccc;
92+
cursor: not-allowed;
93+
}

0 commit comments

Comments
 (0)