Spaces:
Sleeping
Sleeping
| import { Id, TableNames } from './_generated/dataModel'; | |
| import { internal } from './_generated/api'; | |
| import { | |
| DatabaseReader, | |
| internalAction, | |
| internalMutation, | |
| mutation, | |
| query, | |
| } from './_generated/server'; | |
| import { v } from 'convex/values'; | |
| import schema from './schema'; | |
| import { DELETE_BATCH_SIZE } from './constants'; | |
| import { kickEngine, startEngine, stopEngine } from './aiTown/main'; | |
| import { insertInput } from './aiTown/insertInput'; | |
| import { fetchEmbedding, LLM_CONFIG } from './util/llm'; | |
| import { chatCompletion } from './util/llm'; | |
| import { startConversationMessage } from './agent/conversation'; | |
| import { GameId } from './aiTown/ids'; | |
| // Clear all of the tables except for the embeddings cache. | |
| const excludedTables: Array<TableNames> = ['embeddingsCache']; | |
| export const wipeAllTables = internalMutation({ | |
| handler: async (ctx) => { | |
| for (const tableName of Object.keys(schema.tables)) { | |
| if (excludedTables.includes(tableName as TableNames)) { | |
| continue; | |
| } | |
| await ctx.scheduler.runAfter(0, internal.testing.deletePage, { tableName, cursor: null }); | |
| } | |
| }, | |
| }); | |
| export const deletePage = internalMutation({ | |
| args: { | |
| tableName: v.string(), | |
| cursor: v.union(v.string(), v.null()), | |
| }, | |
| handler: async (ctx, args) => { | |
| const results = await ctx.db | |
| .query(args.tableName as TableNames) | |
| .paginate({ cursor: args.cursor, numItems: DELETE_BATCH_SIZE }); | |
| for (const row of results.page) { | |
| await ctx.db.delete(row._id); | |
| } | |
| if (!results.isDone) { | |
| await ctx.scheduler.runAfter(0, internal.testing.deletePage, { | |
| tableName: args.tableName, | |
| cursor: results.continueCursor, | |
| }); | |
| } | |
| }, | |
| }); | |
| export const kick = internalMutation({ | |
| handler: async (ctx) => { | |
| const { worldStatus } = await getDefaultWorld(ctx.db); | |
| await kickEngine(ctx, worldStatus.worldId); | |
| }, | |
| }); | |
| export const stopAllowed = query({ | |
| handler: async () => { | |
| return !process.env.STOP_NOT_ALLOWED; | |
| }, | |
| }); | |
| export const stop = mutation({ | |
| handler: async (ctx) => { | |
| if (process.env.STOP_NOT_ALLOWED) throw new Error('Stop not allowed'); | |
| const { worldStatus, engine } = await getDefaultWorld(ctx.db); | |
| if (worldStatus.status === 'inactive' || worldStatus.status === 'stoppedByDeveloper') { | |
| if (engine.running) { | |
| throw new Error(`Engine ${engine._id} isn't stopped?`); | |
| } | |
| console.debug(`World ${worldStatus.worldId} is already inactive`); | |
| return; | |
| } | |
| console.log(`Stopping engine ${engine._id}...`); | |
| await ctx.db.patch(worldStatus._id, { status: 'stoppedByDeveloper' }); | |
| await stopEngine(ctx, worldStatus.worldId); | |
| }, | |
| }); | |
| export const resume = mutation({ | |
| handler: async (ctx) => { | |
| const { worldStatus, engine } = await getDefaultWorld(ctx.db); | |
| if (worldStatus.status === 'running') { | |
| if (!engine.running) { | |
| throw new Error(`Engine ${engine._id} isn't running?`); | |
| } | |
| console.debug(`World ${worldStatus.worldId} is already running`); | |
| return; | |
| } | |
| console.log( | |
| `Resuming engine ${engine._id} for world ${worldStatus.worldId} (state: ${worldStatus.status})...`, | |
| ); | |
| await ctx.db.patch(worldStatus._id, { status: 'running' }); | |
| await startEngine(ctx, worldStatus.worldId); | |
| }, | |
| }); | |
| export const archive = internalMutation({ | |
| handler: async (ctx) => { | |
| const { worldStatus, engine } = await getDefaultWorld(ctx.db); | |
| if (engine.running) { | |
| throw new Error(`Engine ${engine._id} is still running!`); | |
| } | |
| console.log(`Archiving world ${worldStatus.worldId}...`); | |
| await ctx.db.patch(worldStatus._id, { isDefault: false }); | |
| }, | |
| }); | |
| async function getDefaultWorld(db: DatabaseReader) { | |
| const worldStatus = await db | |
| .query('worldStatus') | |
| .filter((q) => q.eq(q.field('isDefault'), true)) | |
| .first(); | |
| if (!worldStatus) { | |
| throw new Error('No default world found'); | |
| } | |
| const engine = await db.get(worldStatus.engineId); | |
| if (!engine) { | |
| throw new Error(`Engine ${worldStatus.engineId} not found`); | |
| } | |
| return { worldStatus, engine }; | |
| } | |
| export const debugCreatePlayers = internalMutation({ | |
| args: { | |
| numPlayers: v.number(), | |
| }, | |
| handler: async (ctx, args) => { | |
| const { worldStatus } = await getDefaultWorld(ctx.db); | |
| for (let i = 0; i < args.numPlayers; i++) { | |
| const inputId = await insertInput(ctx, worldStatus.worldId, 'join', { | |
| name: `Robot${i}`, | |
| description: `This player is a robot.`, | |
| character: `f${1 + (i % 8)}`, | |
| type: 'villager', | |
| }); | |
| } | |
| }, | |
| }); | |
| export const randomPositions = internalMutation({ | |
| handler: async (ctx) => { | |
| const { worldStatus } = await getDefaultWorld(ctx.db); | |
| const map = await ctx.db | |
| .query('maps') | |
| .withIndex('worldId', (q) => q.eq('worldId', worldStatus.worldId)) | |
| .unique(); | |
| if (!map) { | |
| throw new Error(`No map for world ${worldStatus.worldId}`); | |
| } | |
| const world = await ctx.db.get(worldStatus.worldId); | |
| if (!world) { | |
| throw new Error(`No world for world ${worldStatus.worldId}`); | |
| } | |
| for (const player of world.players) { | |
| await insertInput(ctx, world._id, 'moveTo', { | |
| playerId: player.id, | |
| destination: { | |
| x: 1 + Math.floor(Math.random() * (map.width - 2)), | |
| y: 1 + Math.floor(Math.random() * (map.height - 2)), | |
| }, | |
| }); | |
| } | |
| }, | |
| }); | |
| export const testEmbedding = internalAction({ | |
| args: { input: v.string() }, | |
| handler: async (_ctx, args) => { | |
| return await fetchEmbedding(args.input); | |
| }, | |
| }); | |
| export const testCompletion = internalAction({ | |
| args: {}, | |
| handler: async (ctx, args) => { | |
| return await chatCompletion({ | |
| messages: [ | |
| { content: 'You are helpful', role: 'system' }, | |
| { content: 'Where is pizza?', role: 'user' }, | |
| ], | |
| }); | |
| }, | |
| }); | |
| export const testConvo = internalAction({ | |
| args: {}, | |
| handler: async (ctx, args) => { | |
| const a: any = (await startConversationMessage( | |
| ctx, | |
| 'm1707m46wmefpejw1k50rqz7856qw3ew' as Id<'worlds'>, | |
| 'c:115' as GameId<'conversations'>, | |
| 'p:0' as GameId<'players'>, | |
| 'p:6' as GameId<'players'>, | |
| )) as any; | |
| return await a.readAll(); | |
| }, | |
| }); | |