forked from a16z-infra/ai-town
-
Notifications
You must be signed in to change notification settings - Fork 0
/
testing.ts
239 lines (221 loc) · 7.44 KB
/
testing.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
import { v } from 'convex/values';
import { internal } from './_generated/api';
import { TableNames } from './_generated/dataModel';
import { internalAction, internalMutation, internalQuery } from './_generated/server';
import { getAllPlayers } from './players';
import { asyncMap, pruneNull } from './lib/utils';
import { EntryOfType, Motion, Position } from './schema';
import { clientMessageMapper } from './chat';
import { MemoryDB } from './lib/memory';
import { getPlayer, stop, walk } from './journal';
import { handleAgentInteraction } from './agent';
import schema from './schema';
import { findRoute } from './lib/routing';
export const converge = internalMutation({
args: {},
handler: async (ctx, args) => {
const world = await ctx.db.query('worlds').order('desc').first();
if (!world) throw new Error('No worlds exist yet: try running dbx convex run init');
const players = await getAllPlayers(ctx.db, world._id);
const target = players[0]._id;
await stop(ctx, { playerId: target });
for (const player of players.slice(1)) {
if (player.agentId) {
await walk(ctx, { agentId: player.agentId, ignore: [], target });
}
}
},
});
export const stopThinking = internalMutation({
args: {},
handler: async (ctx, args) => {
const world = (await ctx.db.query('worlds').order('desc').first())!;
const agents = await ctx.db
.query('agents')
.withIndex('by_worldId_thinking', (q) => q.eq('worldId', world._id).eq('thinking', true))
.collect();
for (const agent of agents) {
await ctx.db.patch(agent._id, { thinking: false });
}
},
});
export const testRouteFinding = internalQuery({
args: {},
handler: async (ctx, args) => {
const map = (await ctx.db.query('maps').order('desc').first())!;
const startMotion: Motion = {
type: 'stopped',
pose: { position: { x: 0, y: 1 }, orientation: 0 },
reason: 'idle',
};
const otherPlayerMotion: Motion[] = [
{
type: 'walking',
startTs: 0,
targetEndTs: 2,
route: [
{ x: 2, y: 2 },
{ x: 2, y: 1 },
],
ignore: [],
},
{ type: 'stopped', pose: { position: { x: 1, y: 3 }, orientation: 0 }, reason: 'idle' },
];
const end: Position = { x: 1, y: 3 };
return findRoute(map, startMotion, otherPlayerMotion, end, 0);
},
});
export const debugAgentSnapshotWithThinking = internalMutation({
args: { playerId: v.id('players') },
handler: async (ctx, { playerId }) => {
const playerDoc = await ctx.db.get(playerId);
const player = await getPlayer(ctx.db, playerDoc!);
await ctx.db.patch(player.agentId!, { thinking: true });
return { player };
},
});
export const agentState = internalQuery({
args: {},
handler: async (ctx, args) => {
const world = await ctx.db.query('worlds').order('desc').first();
if (!world) throw new Error('No worlds exist yet: try running dbx convex run init');
const agents = await ctx.db
.query('agents')
.withIndex('by_worldId_thinking', (q) => q.eq('worldId', world._id))
.collect();
return agents;
},
});
export const getDebugPlayers = internalQuery({
handler: async (ctx) => {
const world = await ctx.db.query('worlds').order('desc').first();
if (!world) throw new Error('No worlds exist yet: try running dbx convex run init');
const players = await asyncMap(await getAllPlayers(ctx.db, world._id), (p) =>
getPlayer(ctx.db, p),
);
return { players, world };
},
});
export const allPlayers = internalQuery({
args: {},
handler: async (ctx, args) => {
const players = await ctx.db.query('players').collect();
if (!players) return null;
return asyncMap(players, (p) => getPlayer(ctx.db, p));
},
});
export const latestPlayer = internalQuery({
args: {},
handler: async (ctx, args) => {
const player = await ctx.db.query('players').order('desc').first();
if (!player) return null;
return getPlayer(ctx.db, player);
},
});
export const debugPlayerIdSnapshot = internalQuery({
args: { playerId: v.id('players') },
handler: async (ctx, args) => {
return getPlayer(ctx.db, (await ctx.db.get(args.playerId))!);
},
});
export const listMessages = internalQuery({
args: {},
handler: async (ctx, args) => {
const world = await ctx.db.query('worlds').order('desc').first();
if (!world) return [];
const players = await getAllPlayers(ctx.db, world._id);
const playerIds = players.map((p) => p._id);
const messageEntries = await asyncMap(
playerIds,
(playerId) =>
ctx.db
.query('journal')
.withIndex('by_playerId_type', (q) =>
q.eq('playerId', playerId as any).eq('data.type', 'talking'),
)
.order('desc')
.take(10) as Promise<EntryOfType<'talking'>[]>,
);
return (
await asyncMap(
messageEntries.flatMap((a) => a),
clientMessageMapper(ctx.db),
)
).sort((a, b) => a.ts - b.ts);
},
});
export const setThinking = internalMutation({
args: { playerIds: v.array(v.id('players')) },
handler: async (ctx, args) => {
const players = pruneNull(await asyncMap(args.playerIds, ctx.db.get));
for (const player of players) {
await ctx.db.patch(player.agentId!, { thinking: true });
}
},
});
export const runAgentLoopClear = internalAction({
args: {
numberOfLoops: v.optional(v.number()),
},
handler: async (ctx, args) => {
await ctx.runAction(internal.init.resetFrozen);
await runAgentLoop(ctx, args);
},
});
export const runAgentLoop = internalAction({
args: {
numberOfLoops: v.optional(v.number()),
},
handler: async (ctx, args) => {
console.log('Looping', args.numberOfLoops || 100);
const { players, world } = await ctx.runQuery(internal.testing.getDebugPlayers);
const playerIds = players.map((p) => p.id);
let index = args.numberOfLoops || 100;
let randomX: number[] = [];
let displacement = 25;
for (let i = 0; i < playerIds.length; i++) {
randomX.push(displacement * i);
}
while (index-- != 0) {
await ctx.runMutation(internal.testing.setThinking, { playerIds });
await ctx.runAction(internal.agent.runAgentBatch, { playerIds, noSchedule: true });
}
},
});
// For making conversations happen without walking around, clear before conversation start.
export const runConversationClear = internalAction({
args: { maxMessages: v.optional(v.number()) },
handler: async (ctx, args) => {
await ctx.runAction(internal.init.resetFrozen);
await runConversation(ctx, args);
},
});
// For making conversations happen without walking around.
export const runConversation = internalAction({
args: {
maxMessages: v.optional(v.number()),
conversationCount: v.optional(v.number()),
},
handler: async (ctx, args) => {
const { players, world } = await ctx.runQuery(internal.testing.getDebugPlayers);
const memory = MemoryDB(ctx);
for (let i = 0; i < (args.conversationCount ?? 1); i++) {
await handleAgentInteraction(ctx, players, memory, async (agentId, activity) => {
console.log({ agentId, activity });
});
}
},
});
export const debugClearAll = internalMutation({
args: {},
handler: async (ctx, args) => {
for (const table in schema.tables) {
await ctx.scheduler.runAfter(0, internal.crons.vacuumOldEntries, {
table: table as TableNames,
age: -1_000, // Delete 1 second into the future
cursor: null,
soFar: 0,
});
}
},
});