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(); | |
}, | |
}); | |