Fixed LLM bot support
This commit is contained in:
parent
792f12b925
commit
e1d059d72c
|
|
@ -1,17 +1,19 @@
|
|||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { LLM } = require('@themaximalist/llm.js'); // or wherever your LLM package lives
|
||||
const OpenAI = require('openai');
|
||||
|
||||
// read optional config file
|
||||
const configPath = path.join(__dirname, '..', 'llm_config.json');
|
||||
const configPath = path.join(__dirname, 'llm_config.json');
|
||||
const llmOptions = fs.existsSync(configPath)
|
||||
? JSON.parse(fs.readFileSync(configPath, 'utf8'))
|
||||
: {};
|
||||
|
||||
console.log(`Read config from ${configPath}`);
|
||||
|
||||
class LLMBot {
|
||||
constructor() {
|
||||
// One instance keeps the whole conversation in memory
|
||||
this.llm = new LLM(llmOptions);
|
||||
// Instantiate the OpenAI client with the config options we just read
|
||||
this.llm = new OpenAI(llmOptions);
|
||||
|
||||
// Prompt fragments we’ll reuse
|
||||
this.writePrompt =
|
||||
|
|
@ -31,23 +33,39 @@ Keep it short, fun, and on-topic.`;
|
|||
|
||||
async Write(story_so_far) {
|
||||
const msg = `${this.writePrompt}\n\nStory so far:\n${story_so_far}`;
|
||||
const llm_response = await this.llm.chat(msg);
|
||||
console.log(`Received response from LLM: ${llm_response}`)
|
||||
return llm_response.trim();
|
||||
const response = await this.llm.chat.completions.create({
|
||||
model: llmOptions.model || 'meta-llama/Llama-3.2-3B-Instruct-Turbo',
|
||||
messages: [{ role: 'user', content: msg }],
|
||||
temperature: llmOptions.temperature ?? 0.8,
|
||||
max_tokens: llmOptions.max_tokens ?? 80,
|
||||
});
|
||||
console.log(`Received response from LLM: ${response.choices[0].message.content}`);
|
||||
return response.choices[0].message.content.trim();
|
||||
}
|
||||
|
||||
async Vote(story_so_far, choices) {
|
||||
const choicesBlock = choices.map((c, i) => `${i}: ${c}`).join('\n');
|
||||
const msg = `${this.votePrompt}\n\nStory:\n${story_so_far}\n\nChoices:\n${choicesBlock}`;
|
||||
const reply = (await this.llm.chat(msg)).trim();
|
||||
// extract first digit found, fallback to 0
|
||||
const response = await this.llm.chat.completions.create({
|
||||
model: llmOptions.model || 'meta-llama/Llama-3.2-3B-Instruct-Turbo',
|
||||
messages: [{ role: 'user', content: msg }],
|
||||
temperature: 0.2,
|
||||
max_tokens: 5,
|
||||
});
|
||||
const reply = response.choices[0].message.content.trim();
|
||||
const match = reply.match(/\d+/);
|
||||
return match ? parseInt(match[0], 10) : 0;
|
||||
}
|
||||
|
||||
async Banter(chat_so_far) {
|
||||
const msg = `${this.banterPrompt}\n\nChat so far:\n${chat_so_far}`;
|
||||
return (await this.llm.chat(msg)).trim();
|
||||
const response = await this.llm.chat.completions.create({
|
||||
model: llmOptions.model || 'meta-llama/Llama-3.2-3B-Instruct-Turbo',
|
||||
messages: [{ role: 'user', content: msg }],
|
||||
temperature: llmOptions.temperature ?? 0.8,
|
||||
max_tokens: llmOptions.max_tokens ?? 60,
|
||||
});
|
||||
return response.choices[0].message.content.trim();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
"cookie-parser": "^1.4.7",
|
||||
"express": "^5.2.1",
|
||||
"node-fetch": "^3.3.2",
|
||||
"openai": "^6.25.0",
|
||||
"socket.io": "^4.8.3",
|
||||
"uuid": "^13.0.0"
|
||||
}
|
||||
|
|
|
|||
41
server.js
41
server.js
|
|
@ -117,7 +117,7 @@ class YarnGame {
|
|||
console.log("Creating bots...")
|
||||
for (let i = 0; i < botCount; i++) {
|
||||
const bot_id = randomUUID()
|
||||
const bot = BotFactory.createBot("random");
|
||||
const bot = BotFactory.createBot("llm");
|
||||
this.bots.push({ bot_id, bot });
|
||||
const bot_player = {
|
||||
player_id: bot_id,
|
||||
|
|
@ -130,23 +130,24 @@ class YarnGame {
|
|||
}
|
||||
}
|
||||
|
||||
startGame(settings) {
|
||||
async startGame(settings) {
|
||||
this.gameSettings = { ...this.gameSettings, ...settings };
|
||||
this.startBots(this.gameSettings.botCount);
|
||||
this.inProgress = true;
|
||||
this.yarnStory = [{ player: "Narrator", str: this.gameSettings.startText }];
|
||||
this.players.forEach(p => p.score = 0);
|
||||
this.startWritingPhase();
|
||||
await this.startWritingPhase();
|
||||
}
|
||||
|
||||
processBotWriting() {
|
||||
async processBotWriting() {
|
||||
for (const botObj of this.bots) {
|
||||
const botText = botObj.bot.Write(this.getStoryText());
|
||||
const botText = await botObj.bot.Write(this.getStoryText());
|
||||
console.log(`Bot text: ${botText}`)
|
||||
this.submitEntry(botObj.bot_id, botText)
|
||||
}
|
||||
}
|
||||
|
||||
startWritingPhase() {
|
||||
async startWritingPhase() {
|
||||
this.currentPhase = 'writing';
|
||||
this.round_data = [];
|
||||
this.submittedPlayers = new Set();
|
||||
|
|
@ -176,18 +177,18 @@ class YarnGame {
|
|||
return this.submittedPlayers.size >= this.players.length;
|
||||
}
|
||||
|
||||
processBotVoting() {
|
||||
async processBotVoting() {
|
||||
for (const botObj of this.bots) {
|
||||
const botVote = botObj.bot.Vote(this.getStoryText(), this.round_data);
|
||||
const botVote = await botObj.bot.Vote(this.getStoryText(), this.round_data);
|
||||
this.submitVote(botObj.bot_id, botVote)
|
||||
}
|
||||
}
|
||||
|
||||
startVotingPhase() {
|
||||
async startVotingPhase() {
|
||||
this.currentPhase = 'voting';
|
||||
this.votedPlayers = new Set();
|
||||
this.timeRemaining = 30; // 30 seconds for voting
|
||||
this.processBotVoting()
|
||||
await this.processBotVoting()
|
||||
}
|
||||
|
||||
submitVote(playerId, entryIndex) {
|
||||
|
|
@ -390,7 +391,7 @@ io.on('connection', (socket) => {
|
|||
io.to(currentRoom).emit('settingsUpdate', game.gameSettings);
|
||||
});
|
||||
|
||||
socket.on('startGame', (settings) => {
|
||||
socket.on('startGame', async (settings) => {
|
||||
if (!currentRoom || !playerId) return;
|
||||
|
||||
const game = games[currentRoom];
|
||||
|
|
@ -401,13 +402,13 @@ io.on('connection', (socket) => {
|
|||
}
|
||||
|
||||
console.log(`Starting new game with settings ${JSON.stringify(settings)}`)
|
||||
game.startGame(settings);
|
||||
await game.startGame(settings);
|
||||
io.to(currentRoom).emit('gameStarted', game.getState());
|
||||
|
||||
startTimer(game);
|
||||
await startTimer(game);
|
||||
});
|
||||
|
||||
socket.on('submitEntry', (data) => {
|
||||
socket.on('submitEntry', async (data) => {
|
||||
if (!currentRoom || !playerId) return;
|
||||
|
||||
const game = games[currentRoom];
|
||||
|
|
@ -420,7 +421,7 @@ io.on('connection', (socket) => {
|
|||
|
||||
if (game.allPlayersSubmitted()) {
|
||||
clearInterval(game.timer);
|
||||
game.startVotingPhase();
|
||||
await game.startVotingPhase();
|
||||
io.to(currentRoom).emit('votingPhase', {
|
||||
entries: game.round_data.map((e, i) => ({
|
||||
index: i,
|
||||
|
|
@ -495,10 +496,10 @@ io.on('connection', (socket) => {
|
|||
});
|
||||
});
|
||||
|
||||
function startTimer(game) {
|
||||
async function startTimer(game) {
|
||||
if (game.timer) clearInterval(game.timer);
|
||||
|
||||
game.timer = setInterval(() => {
|
||||
game.timer = setInterval(async () => {
|
||||
game.timeRemaining--;
|
||||
io.to(game.roomName).emit('timerUpdate', game.timeRemaining);
|
||||
|
||||
|
|
@ -512,7 +513,7 @@ function startTimer(game) {
|
|||
}
|
||||
}
|
||||
|
||||
game.startVotingPhase();
|
||||
await game.startVotingPhase();
|
||||
io.to(game.roomName).emit('votingPhase', {
|
||||
entries: game.round_data.map((e, i) => ({
|
||||
index: i,
|
||||
|
|
@ -558,10 +559,10 @@ function endVotingPhase(game) {
|
|||
});
|
||||
} else {
|
||||
// Start next round after a short delay
|
||||
setTimeout(() => {
|
||||
setTimeout(async () => {
|
||||
game.startWritingPhase();
|
||||
io.to(game.roomName).emit('newRound', game.getState());
|
||||
startTimer(game);
|
||||
await startTimer(game);
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue