Spaces:
Sleeping
Sleeping
import { ObjectType, v } from "convex/values"; | |
import { GameId, parseGameId, playerId } from "./ids"; | |
import { Player } from "./player"; | |
import { Game } from "./game"; | |
import { HfInference } from "@huggingface/inference"; | |
import { PlayerDescription } from "./playerDescription"; | |
export const VotesSchema = { | |
voter: playerId, | |
playerIds: v.array(playerId), | |
} | |
export type SerializedVotes = ObjectType<typeof VotesSchema>; | |
export class Votes { | |
voter: GameId<'players'>; | |
playerIds: GameId<'players'>[]; | |
constructor(serialized: SerializedVotes) { | |
const { voter, playerIds } = serialized; | |
this.voter = parseGameId('players', voter); | |
this.playerIds = playerIds.map((playerId) => parseGameId('players', playerId)); | |
} | |
serialize(): SerializedVotes { | |
const { voter, playerIds } = this; | |
return { | |
voter, | |
playerIds, | |
}; | |
} | |
} | |
export const llmVote = (game: Game, voter: GameId<'players'>, playerIds: GameId<'players'>[]) => { | |
// If the voter has already voted, remove their vote | |
let new_votes = game.world.llmVotes.filter((vote) => vote.voter !== voter); | |
new_votes.push(new Votes({ | |
voter, | |
playerIds, | |
})); | |
game.world.llmVotes = new_votes | |
} | |
export const gameVote = (game: Game, voter: GameId<'players'>, playerIds: GameId<'players'>[]) => { | |
// If the voter has already voted, remove their vote | |
let new_votes = game.world.gameVotes.filter((vote) => vote.voter !== voter); | |
new_votes.push(new Votes({ | |
voter, | |
playerIds, | |
})); | |
game.world.gameVotes = new_votes | |
} | |
export const processVotes = (votes: Votes[], players: Player[], k: number = 1) => { | |
// Select the players with the most votes | |
const voteCounts: Record<GameId<'players'>, number> = {}; | |
players.forEach(player => { | |
voteCounts[player.id] = 0; | |
}); | |
// Tally the votes | |
votes.forEach(vote => { | |
vote.playerIds.forEach(playerId => { | |
voteCounts[playerId] = (voteCounts[playerId] || 0) + 1; | |
}); | |
}); | |
// This can mean that warevolves can each other but whatever | |
const sortedVoteCounts = Object.entries(voteCounts).sort((a, b) => b[1] - a[1]); | |
const topKPlayers = sortedVoteCounts.slice(0, k).map((val) => { | |
return { | |
playerId: val[0] as GameId<'players'>, | |
voteCount: val[1], | |
}; | |
}); | |
return topKPlayers; | |
} | |
export function parseLLMVotingResult(voter: Player, log: any | null, game: Game) { | |
let votedPlayerId = '' | |
if (log?.arguments?.playerId) { | |
console.log('Successfully voted to eliminate villager: ', log.arguments.playerId); | |
votedPlayerId = log.arguments.playerId as string | |
} else { | |
const players = game.playerDescriptions.values(); | |
const playerIds = [...players].map(player => player.playerId); | |
votedPlayerId = playerIds[Math.floor(Math.random() * playerIds.length)]; | |
console.log('Voted to eliminate villager: ', votedPlayerId); | |
} | |
gameVote(game, voter.id, [votedPlayerId as GameId<'players'>]); | |
} | |
export async function LLmvotingCallWerewolf(werewolf: Player, villagers: PlayerDescription[]) { | |
// TODO: Use messages | |
// TODO: till fixed | |
return null | |
const inference = new HfInference(); | |
const params = { | |
model: "tgi", | |
messages: [ | |
{ | |
role: "system", | |
content: "You are a werewolf and shall eliminate someone based on a conversation. You shall eliminate villagers. Don't make assumptions about what values to plug into functions. You MUST call a tool", | |
}, | |
{ role: "user", content: `Who do you want to eliminate between the following player ? \n players : ${villagers} \n ` }, | |
], | |
max_tokens: 500, | |
tool_choice: "auto", | |
tools: [ | |
{ | |
type: "function", | |
function: { | |
name: "vote_player", | |
description: "A function to chose on who to kick out", | |
parameters: { | |
type: "object", | |
properties: { | |
playerId: { type: "string", description: "The character playerId of the player to eliminate. ( eg : p:1, p:2, p:3 etc ...)" }, | |
}, | |
required: ["playerId"], | |
}, | |
}, | |
}, | |
], | |
}; | |
// Streaming chat completion API | |
const llama3 = inference.endpoint( | |
"https://lr2bjyq40uegzvb5.us-east-1.aws.endpoints.huggingface.cloud" | |
); | |
const response = await llama3.chatCompletion(params); | |
return response?.choices[0]?.message?.tool_calls?.[0]?.function || null; | |
} | |
export async function LLmvotingCallAll(villagers: PlayerDescription[]) { | |
// TODO: till fixed | |
return null | |
const inference = new HfInference(); | |
const params = { | |
model: "tgi", | |
messages: [ | |
{ | |
role: "system", | |
content: "You are a villager and shall try to eliminate someone based on a conversation. You shall eliminate werewolves. Don't make assumptions about what values to plug into functions. You MUST call a tool", | |
}, | |
{ role: "user", content: `Who do you want to eliminate between the following player ? \n players : ${villagers} \n ` }, | |
], | |
max_tokens: 500, | |
tool_choice: "auto", | |
tools: [ | |
{ | |
type: "function", | |
function: { | |
name: "vote_player", | |
description: "A function to chose on who to kick out", | |
parameters: { | |
type: "object", | |
properties: { | |
playerId: { type: "string", description: "The character playerId of the player to eliminate. ( eg : p:1, p:2, p:3 etc ...)" }, | |
}, | |
required: ["playerId"], | |
}, | |
}, | |
}, | |
], | |
}; | |
// Streaming chat completion API | |
const llama3 = inference.endpoint( | |
"https://lr2bjyq40uegzvb5.us-east-1.aws.endpoints.huggingface.cloud" | |
); | |
const response = await llama3.chatCompletion(params); | |
return response?.choices[0]?.message?.tool_calls?.[0]?.function || null; | |
} | |