diff --git a/README.md b/README.md index 9a58ac2..37624e9 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ Coming soon... We've created some examples using populat agent frameworks you can use as inspiration (feel free to contribute): - [Vercel AI SDK](https://github.com/superagent-ai/poker-eval/tree/main/examples/ai-sdk) -- [OpeaAI]() Coming soon +- [OpenAI](https://github.com/superagent-ai/poker-eval/tree/main/examples/openai) - [Mastra]() Coming soon - [LlamaIndex]() Coming soon - [Langchain]() Coming soon diff --git a/examples/ai-sdk/agent.ts b/examples/ai-sdk/agent.ts index dea07e9..e5c6ca3 100644 --- a/examples/ai-sdk/agent.ts +++ b/examples/ai-sdk/agent.ts @@ -2,8 +2,7 @@ import { config } from "dotenv"; import { generateObject } from "ai"; import { openai } from "@ai-sdk/openai"; import { z } from "zod"; - -import { PlayerAction, TableState } from "../../src/types"; +import { PlayerAction, TableState } from "@superagent-ai/poker-eval/dist/types"; config(); diff --git a/examples/openai/.env.example b/examples/openai/.env.example new file mode 100644 index 0000000..9847a1d --- /dev/null +++ b/examples/openai/.env.example @@ -0,0 +1 @@ +OPENAI_API_KEY= \ No newline at end of file diff --git a/examples/openai/.gitignore b/examples/openai/.gitignore new file mode 100644 index 0000000..1dcef2d --- /dev/null +++ b/examples/openai/.gitignore @@ -0,0 +1,2 @@ +node_modules +.env \ No newline at end of file diff --git a/examples/openai/README.md b/examples/openai/README.md new file mode 100644 index 0000000..2aea2b6 --- /dev/null +++ b/examples/openai/README.md @@ -0,0 +1,15 @@ +# PokerEval and Vercel AI SDK + +This examples shows how to integrate an Agent built with the [Vercel AI SDK](https://github.com/vercel/ai) and PokerEval. + +## How to use +1. Install dependencies + ``` + npm install + ``` +2. Update environment variables and rename `.env.example` to `.env` + +3. Run evaluation + ``` + ts-node index.ts + ``` \ No newline at end of file diff --git a/examples/openai/agent.ts b/examples/openai/agent.ts new file mode 100644 index 0000000..b214b02 --- /dev/null +++ b/examples/openai/agent.ts @@ -0,0 +1,71 @@ +import { config } from "dotenv"; +import OpenAI from "openai"; +import { z } from "zod"; +import { zodResponseFormat } from "openai/helpers/zod"; +import { PlayerAction, TableState } from "@superagent-ai/poker-eval/dist/types"; + +config(); + +const openai = new OpenAI({ + apiKey: process.env.OPENAI_API_KEY, +}); + +const ActionEnum = z + .enum(["bet", "fold", "call", "check", "raise"]) + .describe("The action to take"); + +const responseSchema = z.object({ + action: ActionEnum, + bet: z + .number() + .describe("The amount to bet, call, raise, or check") + .optional(), +}); + +export const generateAction = async ( + state: TableState +): Promise => { + const ActionEnum = z + .enum(["bet", "fold", "call", "check", "raise"] as const) + .describe("The action to take"); + + const prompt = ` +You are a poker assistant named ${state.playerName}. +You are playing at a table with ${state.tableInfo.length} players. +Act based on the information below. + +Players: +${state.tableInfo + .map( + (player) => + `${player.name}${player.hasButton ? " (Button)" : ""}, Stack: $${ + player.stack + }, Bet size: $${player.betSize}` + ) + .join("\n")} + +Game stage: ${state.gameStage} +Your cards: ${state.playerCards} +Cards on the table: ${state.tableCards} +Legal actions: ${state.legalActions.actions.join(", ")} +Min bet: $${state.minRaise} +Max bet: $${state.maxRaise} + +Considering the hand strength, potential opponent hands, and optimal strategy, what action should the player take? Explain your reasoning. + +Make sure to return the correct amount when calling, raising or bettings. + `; + + const completion = await openai.beta.chat.completions.parse({ + model: "gpt-4o", + messages: [{ role: "user", content: prompt }], + response_format: zodResponseFormat(responseSchema, "action"), + }); + + const { action, bet } = completion.choices[0].message.parsed; + + return { + action, + bet: bet ?? 0, + } as PlayerAction; +}; diff --git a/examples/openai/index.ts b/examples/openai/index.ts new file mode 100644 index 0000000..dcaf574 --- /dev/null +++ b/examples/openai/index.ts @@ -0,0 +1,40 @@ +import { generateAction } from "./agent"; + +import { PokerGame } from "@superagent-ai/poker-eval"; +import { Player, PlayerAction } from "@superagent-ai/poker-eval/dist/types"; + +async function executeGameSimulation(numHands: number): Promise { + // Setup AI players + const players: Player[] = [ + { + name: "GPT 1", + action: async (state): Promise => { + const action = await generateAction(state); + return action; + }, + }, + { + name: "GPT 2", + action: async (state): Promise => { + const action = await generateAction(state); + return action; + }, + }, + ]; + + // Setup a game + const game = new PokerGame(players, { + defaultChipSize: 1000, + smallBlind: 1, + bigBlind: 2, + }); + + // Set the output director for stats collection + const results = await game.runSimulation(numHands, { outputPath: "./stats" }); + + console.log(`Simulation completed for ${numHands} hands.`); + console.log("Results:", results); +} + +// Execute the function with ts-node index.ts +executeGameSimulation(5).catch(console.error); diff --git a/examples/openai/package-lock.json b/examples/openai/package-lock.json new file mode 100644 index 0000000..d612a72 --- /dev/null +++ b/examples/openai/package-lock.json @@ -0,0 +1,305 @@ +{ + "name": "pokereval-openai", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "pokereval-openai", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@superagent-ai/poker-eval": "^1.0.1", + "dotenv": "^16.4.5", + "openai": "^4.73.1", + "zod": "^3.23.8" + } + }, + "node_modules/@superagent-ai/poker-eval": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@superagent-ai/poker-eval/-/poker-eval-1.0.1.tgz", + "integrity": "sha512-mGI+54IA6FdFgaFz+vH9CuXpjcHBlJ2SZZCvdMvUjj7Q0XWNlXyjRtxz/lZAbI3PUId1fy5ZlAwJaZ6Q2wCquA==", + "license": "MIT", + "dependencies": { + "poker-ts": "^1.0.0" + } + }, + "node_modules/@types/node": { + "version": "18.19.66", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.66.tgz", + "integrity": "sha512-14HmtUdGxFUalGRfLLn9Gc1oNWvWh5zNbsyOLo5JV6WARSeN1QcEBKRnZm9QqNfrutgsl/hY4eJW63aZ44aBCg==", + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/node-fetch": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.0" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/agentkeepalive": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", + "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", + "license": "MIT", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data-encoder": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", + "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==", + "license": "MIT" + }, + "node_modules/formdata-node": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", + "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", + "license": "MIT", + "dependencies": { + "node-domexception": "1.0.0", + "web-streams-polyfill": "4.0.0-beta.3" + }, + "engines": { + "node": ">= 12.20" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/openai": { + "version": "4.73.1", + "resolved": "https://registry.npmjs.org/openai/-/openai-4.73.1.tgz", + "integrity": "sha512-nWImDJBcUsqrhy7yJScXB4+iqjzbUEgzfA3un/6UnHFdwWhjX24oztj69Ped/njABfOdLcO/F7CeWTI5dt8Xmg==", + "license": "Apache-2.0", + "dependencies": { + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7" + }, + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "node_modules/poker-ts": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/poker-ts/-/poker-ts-1.4.0.tgz", + "integrity": "sha512-WMLM+jbSEIinr2o8b9rrK38iCmahvGg/DlVmTNAOqH/vmlsziOQfsSmlngtyIV8J2CwVZPZDDvSLQD8Ou1m7LQ==", + "license": "MIT" + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "license": "MIT" + }, + "node_modules/web-streams-polyfill": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", + "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/zod": { + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/examples/openai/package.json b/examples/openai/package.json new file mode 100644 index 0000000..11828f3 --- /dev/null +++ b/examples/openai/package.json @@ -0,0 +1,17 @@ +{ + "name": "pokereval-openai", + "version": "1.0.0", + "description": "Poker agent evals with OpenAI", + "main": "index.ts", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "@superagent-ai/poker-eval": "^1.0.1", + "dotenv": "^16.4.5", + "openai": "^4.73.1", + "zod": "^3.23.8" + } +} diff --git a/examples/openai/tsconfig.json b/examples/openai/tsconfig.json new file mode 100644 index 0000000..bcbdc2d --- /dev/null +++ b/examples/openai/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "module": "CommonJS", + "esModuleInterop": true, + "target": "ES2020", + "moduleResolution": "node", + "allowJs": true + }, + "ts-node": { + "esm": false + } +} \ No newline at end of file