diff --git a/package-lock.json b/package-lock.json index 5ce1ea27..bf937eb1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,8 +15,8 @@ "@ai-sdk/google-vertex": "^0.0.42", "@ai-sdk/groq": "^0.0.3", "@ai-sdk/openai": "^0.0.70", + "@anthropic-ai/sdk": "^0.30.1", "@cliqz/adblocker-playwright": "1.34.0", - "@diffusionstudio/vits-web": "1.0.3", "@discordjs/opus": "github:discordjs/opus", "@discordjs/rest": "2.4.0", "@discordjs/voice": "0.17.0", @@ -57,7 +57,7 @@ "formdata-node": "6.0.3", "fs-extra": "11.2.0", "gaxios": "6.7.1", - "gif-frames": "1.0.1", + "gif-frames": "^0.4.1", "glob": "11.0.0", "graceful-fs": "4.2.11", "html-escaper": "3.0.3", @@ -77,6 +77,7 @@ "onnxruntime-node": "1.20.0", "openai": "4.69.0", "pdfjs-dist": "4.7.76", + "pg": "^8.13.1", "playwright": "1.48.2", "pm2": "5.4.2", "prism-media": "1.3.5", @@ -407,6 +408,43 @@ "node": ">=6.0.0" } }, + "node_modules/@anthropic-ai/sdk": { + "version": "0.30.1", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.30.1.tgz", + "integrity": "sha512-nuKvp7wOIz6BFei8WrTdhmSsx5mwnArYyJgh4+vYu3V4J0Ltb8Xm3odPm51n1aSI0XxNCrDl7O88cxCtUdAkaw==", + "license": "MIT", + "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" + } + }, + "node_modules/@anthropic-ai/sdk/node_modules/@types/node": { + "version": "18.19.63", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.63.tgz", + "integrity": "sha512-hcUB7THvrGmaEcPcvUZCZtQ2Z3C+UR/aOcraBLCvTsFMh916Gc1kCCYcfcMuB76HM2pSerxl1PoP3KnmHzd9Lw==", + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@anthropic-ai/sdk/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/@babel/code-frame": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", @@ -1092,23 +1130,6 @@ "node": ">=16.13" } }, - "node_modules/@cloudflare/workerd-darwin-64": { - "version": "1.20241022.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20241022.0.tgz", - "integrity": "sha512-1NNYun37myMTgCUiPQEJ0cMal4mKZVTpkD0b2tx9hV70xji+frVJcSK8YVLeUm1P+Rw1d/ct8DMgQuCpsz3Fsw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=16" - } - }, "node_modules/@cloudflare/workerd-darwin-arm64": { "version": "1.20241022.0", "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20241022.0.tgz", @@ -1126,57 +1147,6 @@ "node": ">=16" } }, - "node_modules/@cloudflare/workerd-linux-64": { - "version": "1.20241022.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20241022.0.tgz", - "integrity": "sha512-RsNc19BQJG9yd+ngnjuDeG9ywZG+7t1L4JeglgceyY5ViMNMKVO7Zpbsu69kXslU9h6xyQG+lrmclg3cBpnhYA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=16" - } - }, - "node_modules/@cloudflare/workerd-linux-arm64": { - "version": "1.20241022.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20241022.0.tgz", - "integrity": "sha512-x5mUXpKxfsosxcFmcq5DaqLs37PejHYVRsNz1cWI59ma7aC4y4Qn6Tf3i0r9MwQTF/MccP4SjVslMU6m4W7IaA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=16" - } - }, - "node_modules/@cloudflare/workerd-windows-64": { - "version": "1.20241022.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20241022.0.tgz", - "integrity": "sha512-eBCClx4szCOgKqOlxxbdNszMqQf3MRG1B9BRIqEM/diDfdR9IrZ8l3FaEm+l9gXgPmS6m1NBn40aWuGBl8UTSw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=16" - } - }, "node_modules/@cloudflare/workers-shared": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/@cloudflare/workers-shared/-/workers-shared-0.7.0.tgz", @@ -1320,15 +1290,6 @@ "node": ">=6.0.0" } }, - "node_modules/@diffusionstudio/vits-web": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@diffusionstudio/vits-web/-/vits-web-1.0.3.tgz", - "integrity": "sha512-xvOmc3CTWXGKbyiLbsrMczNArm7Z0Bi0h3pij0RTX6SytY/NSqd7h5DdPNUUhBt0nSjWGbo5RIrJLoCQTUQ0nQ==", - "license": "MIT", - "dependencies": { - "onnxruntime-web": "1.18.0" - } - }, "node_modules/@discordjs/builders": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.9.0.tgz", @@ -1524,7 +1485,7 @@ }, "node_modules/@discordjs/opus": { "version": "0.9.0", - "resolved": "git+ssh://git@github.com/discordjs/opus.git#8f52fa01b8229d92bca1027f1e89c5cf71b48133", + "resolved": "git+ssh://git@github.com/discordjs/opus.git#31da49d8d2cc6c5a2ab1bfd332033ff7d5f9fb02", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -2897,88 +2858,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@node-llama-cpp/linux-arm64": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@node-llama-cpp/linux-arm64/-/linux-arm64-3.1.1.tgz", - "integrity": "sha512-rrn1O9zmg8L47e16YlbGI3+Uw1Z8HCTNiBqnz+qcfH2H6HnHd1IenM1CgR9+PVODCnUXE7ErN2moto1XsOxifQ==", - "cpu": [ - "arm64", - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@node-llama-cpp/linux-armv7l": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@node-llama-cpp/linux-armv7l/-/linux-armv7l-3.1.1.tgz", - "integrity": "sha512-fM5dr/wmL4R3rADUOa0SnFRYYpyzsxG0akhg+qBgh0/b1jGwGM6jzBQ9AuhsgfW9tjKdpvpM2GyUDh4tHGHN5w==", - "cpu": [ - "arm", - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@node-llama-cpp/linux-x64": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@node-llama-cpp/linux-x64/-/linux-x64-3.1.1.tgz", - "integrity": "sha512-s3VsBTrVWJgBfV5HruhfkTrnh5ykbuaCXvm1xRMpmMpnkL2tMMOrJJFJJIvrTurtGTxEvbO45O+wLU4wrVlQOw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@node-llama-cpp/linux-x64-cuda": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@node-llama-cpp/linux-x64-cuda/-/linux-x64-cuda-3.1.1.tgz", - "integrity": "sha512-2435gpEI1M0gs8R0/EcpsXwkEtz1hu0waFJjQjck2KNE/Pz+DTw4T7JgWSkAS8uPS7XzzDGBXDuuK1er0ACq3w==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@node-llama-cpp/linux-x64-vulkan": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@node-llama-cpp/linux-x64-vulkan/-/linux-x64-vulkan-3.1.1.tgz", - "integrity": "sha512-iSuaLDsmypv/eASW5DD09FMCCFRKgumpxdB9DHiG8oOd9CLFZle+fxql1TJx3zwtYRrsR7YkfWinjhILYfSIZw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/@node-llama-cpp/mac-arm64-metal": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@node-llama-cpp/mac-arm64-metal/-/mac-arm64-metal-3.1.1.tgz", @@ -2996,87 +2875,6 @@ "node": ">=18.0.0" } }, - "node_modules/@node-llama-cpp/mac-x64": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@node-llama-cpp/mac-x64/-/mac-x64-3.1.1.tgz", - "integrity": "sha512-7UJDsoFpZW3ETsDG623KWZO/pyA1jfVsSPDTJjmotQN1rvXtVqt6cVN/AJ6OjHdoPdEW0u7QxD2nwxY24rRwaQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@node-llama-cpp/win-arm64": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@node-llama-cpp/win-arm64/-/win-arm64-3.1.1.tgz", - "integrity": "sha512-cflHtb0+E4HCm9nIeCGOn4TMAc9R+f2uhCwzZOV6ZMHIwbuVjt/L+3tBo3NULhKWLDSsklRdaU2qV/5elau3wg==", - "cpu": [ - "arm64", - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@node-llama-cpp/win-x64": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@node-llama-cpp/win-x64/-/win-x64-3.1.1.tgz", - "integrity": "sha512-/hK4+wyOe7Q3+UlM/eSmm2GkrS7FwXp+IXAo+id/PobOYEn7l5r1ntqaTgwh3xWefezD3UDSCH1OqkZ2EsVdig==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@node-llama-cpp/win-x64-cuda": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@node-llama-cpp/win-x64-cuda/-/win-x64-cuda-3.1.1.tgz", - "integrity": "sha512-OHk53PpJ6zfJwCUKCS/A+zFEh8JxguuYFnqqyteZoNdI9h3ggOk9QLrn1RQ1LH232Rvfu7AoqGiVgFSB8Jkz4Q==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@node-llama-cpp/win-x64-vulkan": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@node-llama-cpp/win-x64-vulkan/-/win-x64-vulkan-3.1.1.tgz", - "integrity": "sha512-IuKmcN1LUDiQfQAGkTVdAF4J55VzC87PYjYYQNthfojFxwG8GFxK/VnngmmGXybGd6pwK8Cvymun2bNJVQKVoA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -3941,118 +3739,6 @@ "node": ">= 10" } }, - "node_modules/@reflink/reflink-darwin-x64": { - "version": "0.1.16", - "resolved": "https://registry.npmjs.org/@reflink/reflink-darwin-x64/-/reflink-darwin-x64-0.1.16.tgz", - "integrity": "sha512-ssrJj3K0Euua2LAkA4ff5y693wGKUHfznrGeWWtMw2aoLZRAH+C9Ne5oQvmcPPEK6wa929nRhA0ABrvhUa9mvA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@reflink/reflink-linux-arm64-gnu": { - "version": "0.1.16", - "resolved": "https://registry.npmjs.org/@reflink/reflink-linux-arm64-gnu/-/reflink-linux-arm64-gnu-0.1.16.tgz", - "integrity": "sha512-I4PCAcsAKFRSfOSHdz+rck6ARg4jzo4PvVqcnS2odcXy1Inbehxk3IcKBpHnuuDbXRCUoWV6NP7wSx1wG7ZBuA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@reflink/reflink-linux-arm64-musl": { - "version": "0.1.16", - "resolved": "https://registry.npmjs.org/@reflink/reflink-linux-arm64-musl/-/reflink-linux-arm64-musl-0.1.16.tgz", - "integrity": "sha512-xzcdtfwTXWUzN5yHdJgCdyAZSBO0faSgTqGdT4QKDxGHmiokf7+tgVBd6bU2nT4sL26AiIFyIBwp8buXGQYyaw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@reflink/reflink-linux-x64-gnu": { - "version": "0.1.16", - "resolved": "https://registry.npmjs.org/@reflink/reflink-linux-x64-gnu/-/reflink-linux-x64-gnu-0.1.16.tgz", - "integrity": "sha512-4/jscn1A/hx6maOowUjcvIs7YBs0fj//1vxB16TdMYk3tH9FHNmMBv5Pvw8eeRDimAzHP9fQJ9/t4dR6HCf32w==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@reflink/reflink-linux-x64-musl": { - "version": "0.1.16", - "resolved": "https://registry.npmjs.org/@reflink/reflink-linux-x64-musl/-/reflink-linux-x64-musl-0.1.16.tgz", - "integrity": "sha512-03kRXoAXhS/ZKxU2TKax59mLyKP7mev0EoIs+yXejUQo6D4uU46j+Sc243xMp72jRTgbWV4hQykcov98KtXEKQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@reflink/reflink-win32-arm64-msvc": { - "version": "0.1.16", - "resolved": "https://registry.npmjs.org/@reflink/reflink-win32-arm64-msvc/-/reflink-win32-arm64-msvc-0.1.16.tgz", - "integrity": "sha512-N7r+6YB3vXijs7PF3eg306B5s82hGS2TzsMM4+B9DNN9sbvN2yV5HQw29zyCXHY9c9SLe5kEzERp0rsDtN+6TA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@reflink/reflink-win32-x64-msvc": { - "version": "0.1.16", - "resolved": "https://registry.npmjs.org/@reflink/reflink-win32-x64-msvc/-/reflink-win32-x64-msvc-0.1.16.tgz", - "integrity": "sha512-CaslGjfhpvtjHqr8Cw1MhkYZAkcLWFiL1pMXOPv4fwngtLC5/OlcL/Y4Rw2QEZwDvPG3gaeY7pjF1NYEGnDrZA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@remusao/guess-url-type": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@remusao/guess-url-type/-/guess-url-type-1.3.0.tgz", @@ -9655,20 +9341,6 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "license": "ISC" }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -9867,10 +9539,10 @@ "node": ">=8.0.0" } }, - "node_modules/get-pixels-frame-info-update": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/get-pixels-frame-info-update/-/get-pixels-frame-info-update-3.3.2.tgz", - "integrity": "sha512-LzVij57X/gK4Y6LpcDdqj+R9WCpD6Sv3ZH85GMA+S3xgPGCz81mHql4GiSnF4GijRjk7TE0ja2sDr8FFYKLe2g==", + "node_modules/get-pixels-jpeg-js-upgrade": { + "version": "3.3.0-jpeg-js-upgrade.0", + "resolved": "https://registry.npmjs.org/get-pixels-jpeg-js-upgrade/-/get-pixels-jpeg-js-upgrade-3.3.0-jpeg-js-upgrade.0.tgz", + "integrity": "sha512-3GQfE+K7GPp04Rbxh4GQhvGNPStlVYkW8b3hhsAD/3sDuBM5js1hnsNRptMIwyTrAjUoezEnUCFxhnQ0OLi3Sg==", "license": "MIT", "dependencies": { "data-uri-to-buffer": "0.0.3", @@ -9881,7 +9553,7 @@ "node-bitmap": "0.0.1", "omggif": "^1.0.5", "parse-data-uri": "^0.2.0", - "pngjs": "^3.3.3", + "pngjs": "^2.0.0", "request": "^2.44.0", "through": "^2.3.4" } @@ -9986,12 +9658,12 @@ "license": "MIT" }, "node_modules/gif-frames": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gif-frames/-/gif-frames-1.0.1.tgz", - "integrity": "sha512-9ddxnrEbAjdv0R6Ib8DHhd3TIsaulrm55qWpiH1dsVp4X/QPE8FxtK2YvuYrj+y+YhiBHRHRXo5Gei9GzdMS3g==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/gif-frames/-/gif-frames-0.4.1.tgz", + "integrity": "sha512-BSqFuIz4qeZsX7wKDlwyF6qkGyUAgoYNRFJs7v8P97qvBz1FmzyRFHA/EWi/81OMHb0xQdps1X8BYrTyI3e3Aw==", "license": "MIT", "dependencies": { - "get-pixels-frame-info-update": "^3.3.2", + "get-pixels-jpeg-js-upgrade": "^3.3.0-jpeg-js-upgrade.0", "multi-integer-range": "^3.0.0", "save-pixels-jpeg-js-upgrade": "^2.3.4-jpeg-js-upgrade.0" } @@ -14206,26 +13878,6 @@ "tar": "^7.0.1" } }, - "node_modules/onnxruntime-web": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/onnxruntime-web/-/onnxruntime-web-1.18.0.tgz", - "integrity": "sha512-o1UKj4ABIj1gmG7ae0RKJ3/GT+3yoF0RRpfDfeoe0huzRW4FDRLfbkDETmdFAvnJEXuYDE0YT+hhkia0352StQ==", - "license": "MIT", - "dependencies": { - "flatbuffers": "^1.12.0", - "guid-typescript": "^1.0.9", - "long": "^5.2.3", - "onnxruntime-common": "1.18.0", - "platform": "^1.3.6", - "protobufjs": "^7.2.4" - } - }, - "node_modules/onnxruntime-web/node_modules/onnxruntime-common": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/onnxruntime-common/-/onnxruntime-common-1.18.0.tgz", - "integrity": "sha512-lufrSzX6QdKrktAELG5x5VkBpapbCeS3dQwrXbN0eD9rHvU0yAWl7Ztju9FvgAKWvwd/teEKJNj3OwM6eTZh3Q==", - "license": "MIT" - }, "node_modules/openai": { "version": "4.69.0", "resolved": "https://registry.npmjs.org/openai/-/openai-4.69.0.tgz", @@ -14735,6 +14387,95 @@ "@types/estree": "*" } }, + "node_modules/pg": { + "version": "8.13.1", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.13.1.tgz", + "integrity": "sha512-OUir1A0rPNZlX//c7ksiu7crsGZTKSOXJPgtNiHGIlC9H0lO+NC6ZDYksSgBYY/thSWhnSRBv8w1lieNNGATNQ==", + "license": "MIT", + "dependencies": { + "pg-connection-string": "^2.7.0", + "pg-pool": "^3.7.0", + "pg-protocol": "^1.7.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + }, + "engines": { + "node": ">= 8.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.1.1" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", + "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", + "license": "MIT", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.7.0.tgz", + "integrity": "sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==", + "license": "MIT" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.7.0.tgz", + "integrity": "sha512-ZOBQForurqh4zZWjrgSwwAtzJ7QiRX0ovFkZr2klsen3Nm0aoh33Ls0fzfv3imeH/nw/O27cjdz5kzYJfeGp/g==", + "license": "MIT", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.7.0.tgz", + "integrity": "sha512-hTK/mE36i8fDDhgDFjy6xNOG+LCorxLG3WO17tku+ij6sVHXh1jQUJ8hYAnRhNla4QVD2H8er/FOjc/+EgC6yQ==", + "license": "MIT" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "license": "MIT", + "dependencies": { + "split2": "^4.1.0" + } + }, "node_modules/picocolors": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", @@ -15061,12 +14802,13 @@ } }, "node_modules/pngjs": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", - "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-2.3.1.tgz", + "integrity": "sha512-ITNPqvx+SSssNFOgHQzGG87HrqQ0g2nMSHc1jjU5Piq9xJEJ40fiFEPz0S5HSSXxBHrTnhaBHIayTO5aRfk2vw==", "license": "MIT", "engines": { - "node": ">=4.0.0" + "iojs": ">= 1.0.0", + "node": ">=0.10.0" } }, "node_modules/pngjs-nozlib": { @@ -15127,6 +14869,45 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/prebuild-install": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz", @@ -17190,6 +16971,15 @@ "dev": true, "license": "MIT" }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, "node_modules/sprintf-js": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", diff --git a/package.json b/package.json index 37f275bc..289a49cb 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "scripts": { "build": "tsc", "start": "node --loader ts-node/esm src/index.ts", + "start:arok": "node --loader ts-node/esm src/index.ts --characters=\"characters/arok.character.json\"", "start:service:ruby": "pm2 start npm --name=\"ruby\" --restart-delay=3000 --max-restarts=10 -- run start:ruby", "stop:service:ruby": "pm2 stop ruby", "start:ruby": "node --loader ts-node/esm src/index.ts --characters=\"characters/ruby.character.json\"", @@ -25,6 +26,7 @@ "start:tate": "node --loader ts-node/esm src/index.ts --characters=\"characters/tate.character.json\"", "watch": "tsc --watch", "dev": "nodemon", + "build:docs": "cd docs && npm run build", "postinstall": "npx playwright install-deps && npx playwright install", "test": "NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" jest --runInBand --watch -f", "test:sqlite": "NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" TEST_DATABASE_CLIENT=sqlite jest --runInBand --watch -f", @@ -37,6 +39,7 @@ "@rollup/plugin-json": "6.1.0", "@rollup/plugin-node-resolve": "15.3.0", "@rollup/plugin-replace": "5.0.7", + "@rollup/plugin-terser": "0.1.0", "@rollup/plugin-typescript": "11.1.6", "@types/better-sqlite3": "7.6.11", "@types/fluent-ffmpeg": "2.1.27", @@ -58,7 +61,6 @@ "prettier": "3.3.3", "rimraf": "6.0.1", "rollup": "2.79.2", - "@rollup/plugin-terser": "0.1.0", "ts-jest": "29.2.5", "ts-node": "10.9.2", "tslib": "2.8.0", @@ -71,9 +73,7 @@ "@ai-sdk/google-vertex": "^0.0.42", "@ai-sdk/groq": "^0.0.3", "@ai-sdk/openai": "^0.0.70", - "ai": "^3.4.23", - "anthropic-vertex-ai": "^1.0.0", - "together-ai": "^0.7.0", + "@anthropic-ai/sdk": "^0.30.1", "@cliqz/adblocker-playwright": "1.34.0", "@discordjs/opus": "github:discordjs/opus", "@discordjs/rest": "2.4.0", @@ -91,8 +91,10 @@ "@types/cors": "2.8.17", "@types/express": "5.0.0", "agent-twitter-client": "0.0.13", + "ai": "^3.4.23", "alawmulaw": "6.0.0", "ansi-colors": "4.1.3", + "anthropic-vertex-ai": "^1.0.0", "better-sqlite3": "11.5.0", "bignumber": "1.1.0", "bignumber.js": "9.1.2", @@ -113,7 +115,7 @@ "formdata-node": "6.0.3", "fs-extra": "11.2.0", "gaxios": "6.7.1", - "gif-frames": "1.0.1", + "gif-frames": "^0.4.1", "glob": "11.0.0", "graceful-fs": "4.2.11", "html-escaper": "3.0.3", @@ -133,6 +135,7 @@ "onnxruntime-node": "1.20.0", "openai": "4.69.0", "pdfjs-dist": "4.7.76", + "pg": "^8.13.1", "playwright": "1.48.2", "pm2": "5.4.2", "prism-media": "1.3.5", @@ -147,6 +150,7 @@ "telegraf": "4.16.3", "tiktoken": "1.0.17", "tinyld": "1.3.4", + "together-ai": "^0.7.0", "unique-names-generator": "4.7.1", "uuid": "11.0.2", "uuidv4": "6.2.13", diff --git a/src/adapters/postgres.ts b/src/adapters/postgres.ts new file mode 100644 index 00000000..28c99979 --- /dev/null +++ b/src/adapters/postgres.ts @@ -0,0 +1,845 @@ +import { v4 } from "uuid"; +import pg from "pg"; +import { + Account, + Actor, + GoalStatus, + type Goal, + type Memory, + type Relationship, + type UUID, + Participant +} from "../core/types.ts"; +import { DatabaseAdapter } from "../core/database.ts"; +const { Pool } = pg; + +export class PostgresDatabaseAdapter extends DatabaseAdapter { + private pool: typeof Pool; + + constructor(connectionConfig: any) { + super(); + + this.pool = new Pool({ + ...connectionConfig, + max: 20, + idleTimeoutMillis: 30000, + connectionTimeoutMillis: 2000 + }); + + // Register error handler for pool + this.pool.on("error", (err) => { + console.error("Unexpected error on idle client", err); + }); + + this.testConnection(); + } + + async testConnection(): Promise { + let client; + try { + // Attempt to get a client from the pool + client = await this.pool.connect(); + + // Test the connection with a simple query + const result = await client.query("SELECT NOW()"); + console.log("Database connection test successful:", result.rows[0]); + + return true; + } catch (error) { + console.error("Database connection test failed:", error); + throw new Error(`Failed to connect to database: ${error.message}`); + } finally { + // Make sure to release the client back to the pool + if (client) { + client.release(); + } + } + } + + async getRoom(roomId: UUID): Promise { + const client = await this.pool.connect(); + try { + const { rows } = await client.query( + "SELECT id FROM rooms WHERE id = $1", + [roomId] + ); + return rows.length > 0 ? (rows[0].id as UUID) : null; + } finally { + client.release(); + } + } + + async getParticipantsForAccount(userId: UUID): Promise { + const client = await this.pool.connect(); + try { + const { rows } = await client.query( + `SELECT id, "userId", "roomId", last_message_read + FROM participants + WHERE "userId" = $1`, + [userId] + ); + return rows as Participant[]; + } finally { + client.release(); + } + } + + async getParticipantUserState( + roomId: UUID, + userId: UUID + ): Promise<"FOLLOWED" | "MUTED" | null> { + const client = await this.pool.connect(); + try { + const { rows } = await client.query( + `SELECT userState FROM participants WHERE "roomId" = $1 AND userId = $2`, + [roomId, userId] + ); + return rows.length > 0 ? rows[0].userState : null; + } finally { + client.release(); + } + } + + async getMemoriesByRoomIds(params: { + roomIds: UUID[]; + tableName: string; + }): Promise { + const client = await this.pool.connect(); + try { + const placeholders = params.roomIds.map((_, i) => `$${i + 2}`).join(", "); + const { rows } = await client.query( + `SELECT * FROM memories + WHERE type = $1 AND "roomId" IN (${placeholders})`, + [params.tableName, ...params.roomIds] + ); + return rows.map((row) => ({ + ...row, + content: JSON.parse(row.content) + })); + } finally { + client.release(); + } + } + + async setParticipantUserState( + roomId: UUID, + userId: UUID, + state: "FOLLOWED" | "MUTED" | null + ): Promise { + const client = await this.pool.connect(); + try { + await client.query( + `UPDATE participants SET "userState" = $1 WHERE "roomId" = $2 AND "userId" = $3`, + [state, roomId, userId] + ); + } finally { + client.release(); + } + } + + async getParticipantsForRoom(roomId: UUID): Promise { + const client = await this.pool.connect(); + try { + const { rows } = await client.query( + 'SELECT "userId" FROM participants WHERE "roomId" = $1', + [roomId] + ); + return rows.map((row) => row.userId); + } finally { + client.release(); + } + } + + async getAccountById(userId: UUID): Promise { + const client = await this.pool.connect(); + try { + const { rows } = await client.query( + "SELECT * FROM accounts WHERE id = $1", + [userId] + ); + if (rows.length === 0) return null; + + const account = rows[0]; + + console.log("account", account); + return { + ...account, + details: + typeof account.details === "string" + ? JSON.parse(account.details) + : account.details + }; + } finally { + client.release(); + } + } + + async createAccount(account: Account): Promise { + const client = await this.pool.connect(); + try { + await client.query( + `INSERT INTO accounts (id, name, username, email, "avatarUrl", details) + VALUES ($1, $2, $3, $4, $5, $6)`, + [ + account.id ?? v4(), + account.name, + account.username || "", + account.email || "", + account.avatarUrl || "", + JSON.stringify(account.details) + ] + ); + return true; + } catch (error) { + console.log("Error creating account", error); + return false; + } finally { + client.release(); + } + } + + async getActorById(params: { roomId: UUID }): Promise { + const client = await this.pool.connect(); + try { + const { rows } = await client.query( + `SELECT a.id, a.name, a.username, a.details + FROM participants p + LEFT JOIN accounts a ON p."userId" = a.id + WHERE p."roomId" = $1`, + [params.roomId] + ); + return rows.map((row) => ({ + ...row, + details: + typeof row.details === "string" + ? JSON.parse(row.details) + : row.details + })); + } finally { + client.release(); + } + } + + async getMemoryById(id: UUID): Promise { + const client = await this.pool.connect(); + try { + const { rows } = await client.query( + "SELECT * FROM memories WHERE id = $1", + [id] + ); + if (rows.length === 0) return null; + + return { + ...rows[0], + content: + typeof rows[0].content === "string" + ? JSON.parse(rows[0].content) + : rows[0].content + }; + } finally { + client.release(); + } + } + + async createMemory(memory: Memory, tableName: string): Promise { + const client = await this.pool.connect(); + try { + let isUnique = true; + if (memory.embedding) { + const similarMemories = await this.searchMemoriesByEmbedding( + memory.embedding, + { + tableName, + roomId: memory.roomId, + match_threshold: 0.95, + count: 1 + } + ); + isUnique = similarMemories.length === 0; + } + + await client.query( + `INSERT INTO memories ( + id, type, content, embedding, "userId", "roomId", "unique", "createdAt" + ) VALUES ($1, $2, $3, $4::vector, $5::uuid, $6::uuid, $7, to_timestamp($8/1000.0))`, + [ + memory.id ?? v4(), + tableName, + JSON.stringify(memory.content), + `[${memory.embedding.join(",")}]`, + memory.userId, + memory.roomId, + memory.unique ?? true, + Date.now() + ] + ); + } finally { + client.release(); + } + } + + async searchMemories(params: { + tableName: string; + roomId: UUID; + embedding: number[]; + match_threshold: number; + match_count: number; + unique: boolean; + }): Promise { + const client = await this.pool.connect(); + try { + let sql = ` + SELECT *, + 1 - (embedding <-> $3) as similarity + FROM memories + WHERE type = $1 AND "roomId" = $2 + `; + + if (params.unique) { + sql += " AND unique = true"; + } + + sql += ` AND 1 - (embedding <-> $3) >= $4 + ORDER BY embedding <-> $3 + LIMIT $5`; + + const { rows } = await client.query(sql, [ + params.tableName, + params.roomId, + params.embedding, + params.match_threshold, + params.match_count + ]); + + return rows.map((row) => ({ + ...row, + content: JSON.parse(row.content), + similarity: row.similarity + })); + } finally { + client.release(); + } + } + + async getMemories(params: { + roomId: UUID; + count?: number; + unique?: boolean; + tableName: string; + userIds?: UUID[]; + start?: number; + end?: number; + }): Promise { + if (!params.tableName) throw new Error("tableName is required"); + if (!params.roomId) throw new Error("roomId is required"); + + const client = await this.pool.connect(); + try { + let sql = `SELECT * FROM memories WHERE type = $1 AND "roomId" = $2`; + const values: any[] = [params.tableName, params.roomId]; + let paramCount = 2; + + if (params.start) { + paramCount++; + sql += ` AND "createdAt" >= to_timestamp($${paramCount / 1000})`; + values.push(params.start); + } + + if (params.end) { + paramCount++; + sql += ` AND "createdAt" <= to_timestamp($${paramCount / 1000})`; + values.push(params.end); + } + + if (params.unique) { + sql += " AND unique = true"; + } + + if (params.userIds?.length) { + const userPlaceholders = params.userIds + .map((_, i) => `$${paramCount + 1 + i}`) + .join(","); + sql += ` AND "userId" IN (${userPlaceholders})`; + values.push(...params.userIds); + paramCount += params.userIds.length; + } + + sql += ' ORDER BY "createdAt" DESC'; + + if (params.count) { + paramCount++; + sql += ` LIMIT $${paramCount}`; + values.push(params.count); + } + + console.log("sql", sql, values); + + const { rows } = await client.query(sql, values); + return rows.map((row) => ({ + ...row, + content: + typeof rows.content === "string" + ? JSON.parse(rows.content) + : rows.content + })); + } finally { + client.release(); + } + } + + async getGoals(params: { + roomId: UUID; + userId?: UUID | null; + onlyInProgress?: boolean; + count?: number; + }): Promise { + const client = await this.pool.connect(); + try { + let sql = `SELECT * FROM goals WHERE "roomId" = $1`; + const values: any[] = [params.roomId]; + let paramCount = 1; + + if (params.userId) { + paramCount++; + sql += ` AND "userId" = $${paramCount}`; + values.push(params.userId); + } + + if (params.onlyInProgress) { + sql += " AND status = 'IN_PROGRESS'"; + } + + if (params.count) { + paramCount++; + sql += ` LIMIT $${paramCount}`; + values.push(params.count); + } + + const { rows } = await client.query(sql, values); + return rows.map((row) => ({ + ...row, + objectives: + typeof row.objectives === "string" + ? JSON.parse(row.objectives) + : row.objectives + })); + } finally { + client.release(); + } + } + + async updateGoal(goal: Goal): Promise { + const client = await this.pool.connect(); + try { + await client.query( + "UPDATE goals SET name = $1, status = $2, objectives = $3 WHERE id = $4", + [goal.name, goal.status, JSON.stringify(goal.objectives), goal.id] + ); + } finally { + client.release(); + } + } + + async createGoal(goal: Goal): Promise { + const client = await this.pool.connect(); + try { + await client.query( + `INSERT INTO goals (id, "roomId", "userId", name, status, objectives) + VALUES ($1, $2, $3, $4, $5, $6)`, + [ + goal.id ?? v4(), + goal.roomId, + goal.userId, + goal.name, + goal.status, + JSON.stringify(goal.objectives) + ] + ); + } finally { + client.release(); + } + } + + async removeGoal(goalId: UUID): Promise { + const client = await this.pool.connect(); + try { + await client.query("DELETE FROM goals WHERE id = $1", [goalId]); + } finally { + client.release(); + } + } + + async createRoom(roomId?: UUID): Promise { + const client = await this.pool.connect(); + try { + const newRoomId = roomId || v4(); + await client.query("INSERT INTO rooms (id) VALUES ($1)", [newRoomId]); + return newRoomId as UUID; + } finally { + client.release(); + } + } + + async removeRoom(roomId: UUID): Promise { + const client = await this.pool.connect(); + try { + await client.query("DELETE FROM rooms WHERE id = $1", [roomId]); + } finally { + client.release(); + } + } + + async createRelationship(params: { + userA: UUID; + userB: UUID; + }): Promise { + if (!params.userA || !params.userB) { + throw new Error("userA and userB are required"); + } + + const client = await this.pool.connect(); + try { + await client.query( + `INSERT INTO relationships (id, "userA", "userB", "userId") + VALUES ($1, $2, $3, $4)`, + [v4(), params.userA, params.userB, params.userA] + ); + return true; + } catch (error) { + console.log("Error creating relationship", error); + return false; + } finally { + client.release(); + } + } + + async getRelationship(params: { + userA: UUID; + userB: UUID; + }): Promise { + const client = await this.pool.connect(); + try { + const { rows } = await client.query( + `SELECT * FROM relationships + WHERE ("userA" = $1 AND "userB" = $2) OR ("userA" = $2 AND "userB" = $1)`, + [params.userA, params.userB] + ); + return rows.length > 0 ? rows[0] : null; + } finally { + client.release(); + } + } + + async getRelationships(params: { userId: UUID }): Promise { + const client = await this.pool.connect(); + try { + const { rows } = await client.query( + `SELECT * FROM relationships WHERE "userA" = $1 OR "userB" = $1`, + [params.userId] + ); + return rows; + } finally { + client.release(); + } + } + + async getCachedEmbeddings(opts: { + query_table_name: string; + query_threshold: number; + query_input: string; + query_field_name: string; + query_field_sub_name: string; + query_match_count: number; + }): Promise<{ embedding: number[]; levenshtein_score: number }[]> { + const client = await this.pool.connect(); + try { + const sql = ` + SELECT embedding, + levenshtein($1, content->$2->$3) as levenshtein_score + FROM memories + WHERE type = $4 + ORDER BY levenshtein_score + LIMIT $5 + `; + + const { rows } = await client.query(sql, [ + opts.query_input, + opts.query_field_name, + opts.query_field_sub_name, + opts.query_table_name, + opts.query_match_count + ]); + + return rows.map((row) => ({ + embedding: row.embedding, + levenshtein_score: row.levenshtein_score + })); + } finally { + client.release(); + } + } + + async log(params: { + body: { [key: string]: unknown }; + userId: UUID; + roomId: UUID; + type: string; + }): Promise { + const client = await this.pool.connect(); + try { + await client.query( + 'INSERT INTO logs (body, "userId", "roomId", type) VALUES ($1, $2, $3, $4)', + [params.body, params.userId, params.roomId, params.type] + ); + } finally { + client.release(); + } + } + + async searchMemoriesByEmbedding( + embedding: number[], + params: { + match_threshold?: number; + count?: number; + roomId?: UUID; + unique?: boolean; + tableName: string; + } + ): Promise { + const client = await this.pool.connect(); + try { + // Format the embedding array as a proper vector string + const vectorStr = `[${embedding.join(",")}]`; + + let sql = ` + SELECT *, + 1 - (embedding <-> $1::vector) as similarity + FROM memories + WHERE type = $2 + `; + + const values: any[] = [vectorStr, params.tableName]; + let paramCount = 2; + + if (params.unique) { + sql += ` AND "unique" = true`; + } + + if (params.roomId) { + paramCount++; + sql += ` AND "roomId" = $${paramCount}::uuid`; + values.push(params.roomId); + } + + if (params.match_threshold) { + paramCount++; + sql += ` AND 1 - (embedding <-> $1::vector) >= $${paramCount}`; + values.push(params.match_threshold); + } + + sql += ` ORDER BY embedding <-> $1::vector`; + + if (params.count) { + paramCount++; + sql += ` LIMIT $${paramCount}`; + values.push(params.count); + } + + const { rows } = await client.query(sql, values); + return rows.map((row) => ({ + ...row, + content: + typeof row.content === "string" + ? JSON.parse(row.content) + : row.content, + similarity: row.similarity + })); + } finally { + client.release(); + } + } + + async addParticipant(userId: UUID, roomId: UUID): Promise { + const client = await this.pool.connect(); + try { + await client.query( + 'INSERT INTO participants (id, "userId", "roomId") VALUES ($1, $2, $3)', + [v4(), userId, roomId] + ); + return true; + } catch (error) { + console.log("Error adding participant", error); + return false; + } finally { + client.release(); + } + } + + async removeParticipant(userId: UUID, roomId: UUID): Promise { + const client = await this.pool.connect(); + try { + await client.query( + 'DELETE FROM participants WHERE "userId" = $1 AND "roomId" = $2', + [userId, roomId] + ); + return true; + } catch (error) { + console.log("Error removing participant", error); + return false; + } finally { + client.release(); + } + } + + async updateGoalStatus(params: { + goalId: UUID; + status: GoalStatus; + }): Promise { + const client = await this.pool.connect(); + try { + await client.query("UPDATE goals SET status = $1 WHERE id = $2", [ + params.status, + params.goalId + ]); + } finally { + client.release(); + } + } + + async removeMemory(memoryId: UUID, tableName: string): Promise { + const client = await this.pool.connect(); + try { + await client.query("DELETE FROM memories WHERE type = $1 AND id = $2", [ + tableName, + memoryId + ]); + } finally { + client.release(); + } + } + + async removeAllMemories(roomId: UUID, tableName: string): Promise { + const client = await this.pool.connect(); + try { + await client.query( + "DELETE FROM memories WHERE type = $1 AND roomId = $2", + [tableName, roomId] + ); + } finally { + client.release(); + } + } + + async countMemories( + roomId: UUID, + unique = true, + tableName = "" + ): Promise { + if (!tableName) throw new Error("tableName is required"); + + const client = await this.pool.connect(); + try { + let sql = `SELECT COUNT(*) as count FROM memories WHERE type = $1 AND "roomId" = $2`; + if (unique) { + sql += " AND unique = true"; + } + + const { rows } = await client.query(sql, [tableName, roomId]); + return parseInt(rows[0].count); + } finally { + client.release(); + } + } + + async removeAllGoals(roomId: UUID): Promise { + const client = await this.pool.connect(); + try { + await client.query(`DELETE FROM goals WHERE "roomId" = $1`, [roomId]); + } finally { + client.release(); + } + } + + async getRoomsForParticipant(userId: UUID): Promise { + const client = await this.pool.connect(); + try { + const { rows } = await client.query( + `SELECT "roomId" FROM participants WHERE "userId" = $1`, + [userId] + ); + return rows.map((row) => row.roomId); + } finally { + client.release(); + } + } + + async getRoomsForParticipants(userIds: UUID[]): Promise { + const client = await this.pool.connect(); + try { + const placeholders = userIds.map((_, i) => `${i + 1}`).join(", "); + const { rows } = await client.query( + `SELECT DISTINCT "roomId" FROM participants WHERE "userId" IN (${placeholders})`, + userIds + ); + return rows.map((row) => row.roomId); + } finally { + client.release(); + } + } + async getActorDetails(params: { roomId: string }): Promise { + const sql = ` + SELECT + a.id, + a.name, + a.username, + COALESCE(a.details::jsonb, '{}'::jsonb) as details + FROM participants p + LEFT JOIN accounts a ON p.userId = a.id + WHERE p.roomId = $1 + `; + + try { + const result = await this.pool.query(sql, [params.roomId]); + + return result.rows.map((row) => ({ + ...row, + details: row.details // PostgreSQL automatically handles JSON parsing + })); + } catch (error) { + console.error("Error fetching actor details:", error); + throw new Error("Failed to fetch actor details"); + } + } +} + +export function createLoggingDatabaseAdapter( + adapter: DatabaseAdapter +): DatabaseAdapter { + return new Proxy(adapter, { + get(target, prop, receiver) { + const value = Reflect.get(target, prop, receiver); + + if (typeof value === "function") { + return async function (...args: any[]) { + const methodName = prop.toString(); + console.log(`Calling method: ${methodName}`, { + arguments: args.map((arg) => + typeof arg === "object" ? JSON.stringify(arg) : arg + ) + }); + + try { + const result = await value.apply(this, args); + console.log(`Method ${methodName} completed successfully`); + return result; + } catch (error) { + console.error(`Method ${methodName} failed:`, error); + throw error; + } + }; + } + + return value; + } + }); +} diff --git a/src/adapters/sqlite.ts b/src/adapters/sqlite.ts index 628d2a62..ace51b5f 100644 --- a/src/adapters/sqlite.ts +++ b/src/adapters/sqlite.ts @@ -10,7 +10,7 @@ import { type Memory, type Relationship, type UUID, - Participant, + Participant } from "../core/types.ts"; import { sqliteTables } from "./sqlite/sqliteTables.ts"; @@ -135,7 +135,7 @@ export class SqliteDatabaseAdapter extends DatabaseAdapter { details: typeof row.details === "string" ? JSON.parse(row.details) - : row.details, + : row.details }; }) .filter((row): row is Actor => row !== null); @@ -159,7 +159,7 @@ export class SqliteDatabaseAdapter extends DatabaseAdapter { rows.forEach((row) => { memories.push({ ...row, - content: JSON.parse(row.content), + content: JSON.parse(row.content) }); }); @@ -175,7 +175,7 @@ export class SqliteDatabaseAdapter extends DatabaseAdapter { if (memory) { return { ...memory, - content: JSON.parse(memory.content as unknown as string), + content: JSON.parse(memory.content as unknown as string) }; } @@ -197,7 +197,7 @@ export class SqliteDatabaseAdapter extends DatabaseAdapter { tableName, roomId: memory.roomId, match_threshold: 0.95, // 5% similarity threshold - count: 1, + count: 1 } ); @@ -233,7 +233,7 @@ export class SqliteDatabaseAdapter extends DatabaseAdapter { new Float32Array(params.embedding), // Ensure embedding is Float32Array params.tableName, params.roomId, - params.match_count, + params.match_count ]; let sql = ` @@ -257,7 +257,7 @@ export class SqliteDatabaseAdapter extends DatabaseAdapter { typeof memory.createdAt === "string" ? Date.parse(memory.createdAt as string) : memory.createdAt, - content: JSON.parse(memory.content as unknown as string), + content: JSON.parse(memory.content as unknown as string) })); } @@ -274,7 +274,7 @@ export class SqliteDatabaseAdapter extends DatabaseAdapter { const queryParams = [ // JSON.stringify(embedding), new Float32Array(embedding), - params.tableName, + params.tableName ]; let sql = ` @@ -305,7 +305,7 @@ export class SqliteDatabaseAdapter extends DatabaseAdapter { typeof memory.createdAt === "string" ? Date.parse(memory.createdAt as string) : memory.createdAt, - content: JSON.parse(memory.content as unknown as string), + content: JSON.parse(memory.content as unknown as string) })); } @@ -341,7 +341,7 @@ export class SqliteDatabaseAdapter extends DatabaseAdapter { embedding: Array.from( new Float32Array(memory.embedding as unknown as Buffer) ), // Convert Buffer to number[] - levenshtein_score: 0, + levenshtein_score: 0 })); } @@ -424,7 +424,7 @@ export class SqliteDatabaseAdapter extends DatabaseAdapter { typeof memory.createdAt === "string" ? Date.parse(memory.createdAt as string) : memory.createdAt, - content: JSON.parse(memory.content as unknown as string), + content: JSON.parse(memory.content as unknown as string) })); } @@ -488,7 +488,7 @@ export class SqliteDatabaseAdapter extends DatabaseAdapter { objectives: typeof goal.objectives === "string" ? JSON.parse(goal.objectives) - : goal.objectives, + : goal.objectives })); } diff --git a/src/clients/twitter/search.ts b/src/clients/twitter/search.ts index e9d08e08..ec29f2de 100644 --- a/src/clients/twitter/search.ts +++ b/src/clients/twitter/search.ts @@ -7,14 +7,14 @@ import { Content, HandlerCallback, IAgentRuntime, - State, + State } from "../../core/types.ts"; import { stringToUuid } from "../../core/uuid.ts"; import { ClientBase } from "./base.ts"; import { buildConversationThread, sendTweetChunks, - wait, + wait } from "./utils.ts"; import { generateText, generateMessageResponse } from "../../core/generation.ts"; @@ -51,7 +51,7 @@ export class TwitterSearchClient extends ClientBase { constructor(runtime: IAgentRuntime) { // Initialize the client and pass an optional callback to be called when the client is ready super({ - runtime, + runtime }); } @@ -63,7 +63,7 @@ export class TwitterSearchClient extends ClientBase { this.engageWithSearchTerms(); setTimeout( () => this.engageWithSearchTermsLoop(), - (Math.floor(Math.random() * (120 - 60 + 1)) + 60) * 60 * 1000, + (Math.floor(Math.random() * (120 - 60 + 1)) + 60) * 60 * 1000 ); } @@ -80,13 +80,17 @@ export class TwitterSearchClient extends ClientBase { console.log("Fetching search tweets"); // TODO: we wait 5 seconds here to avoid getting rate limited on startup, but we should queue await new Promise((resolve) => setTimeout(resolve, 5000)); - const recentTweets = await this.fetchSearchTweets(searchTerm, 20, SearchMode.Top); + const recentTweets = await this.fetchSearchTweets( + searchTerm, + 20, + SearchMode.Top + ); console.log("Search tweets fetched"); const homeTimeline = await this.fetchHomeTimeline(50); fs.writeFileSync( "tweetcache/home_timeline.json", - JSON.stringify(homeTimeline, null, 2), + JSON.stringify(homeTimeline, null, 2) ); const formattedHomeTimeline = @@ -153,7 +157,7 @@ export class TwitterSearchClient extends ClientBase { const selectedTweet = slicedTweets.find( (tweet) => tweet.id.toString().includes(tweetId) || - tweetId.includes(tweet.id.toString()), + tweetId.includes(tweet.id.toString()) ); if (!selectedTweet) { @@ -180,19 +184,19 @@ export class TwitterSearchClient extends ClientBase { this.runtime.agentId, this.runtime.getSetting("TWITTER_USERNAME"), this.runtime.character.name, - "twitter", + "twitter" ), this.runtime.ensureUserExists( userIdUUID, selectedTweet.username, selectedTweet.name, - "twitter", - ), + "twitter" + ) ]); await Promise.all([ this.runtime.ensureParticipantInRoom(userIdUUID, roomId), - this.runtime.ensureParticipantInRoom(this.runtime.agentId, roomId), + this.runtime.ensureParticipantInRoom(this.runtime.agentId, roomId) ]); // crawl additional conversation tweets, if there are any @@ -205,12 +209,12 @@ export class TwitterSearchClient extends ClientBase { url: selectedTweet.permanentUrl, inReplyTo: selectedTweet.inReplyToStatusId ? stringToUuid(selectedTweet.inReplyToStatusId) - : undefined, + : undefined }, userId: userIdUUID, roomId, // Timestamps are in seconds, but we need them in milliseconds - createdAt: selectedTweet.timestamp * 1000, + createdAt: selectedTweet.timestamp * 1000 }; if (!message.content.text) { @@ -222,7 +226,7 @@ export class TwitterSearchClient extends ClientBase { const replyContext = replies .filter( (reply) => - reply.username !== this.runtime.getSetting("TWITTER_USERNAME"), + reply.username !== this.runtime.getSetting("TWITTER_USERNAME") ) .map((reply) => `@${reply.username}: ${reply.text}`) .join("\n"); @@ -230,7 +234,7 @@ export class TwitterSearchClient extends ClientBase { let tweetBackground = ""; if (selectedTweet.isRetweet) { const originalTweet = await this.requestQueue.add(() => - this.twitterClient.getTweet(selectedTweet.id), + this.twitterClient.getTweet(selectedTweet.id) ); tweetBackground = `Retweeting @${originalTweet.username}: ${originalTweet.text}`; } @@ -254,20 +258,20 @@ export class TwitterSearchClient extends ClientBase { ${selectedTweet.text}${replyContext.length > 0 && `\nReplies to original post:\n${replyContext}`} ${`Original post text: ${selectedTweet.text}`} ${selectedTweet.urls.length > 0 ? `URLs: ${selectedTweet.urls.join(", ")}\n` : ""}${imageDescriptions.length > 0 ? `\nImages in Post (Described): ${imageDescriptions.join(", ")}\n` : ""} - `, + ` }); await this.saveRequestMessage(message, state as State); const context = composeContext({ state, - template: messageHandlerTemplate, + template: messageHandlerTemplate }); // log context to file log_to_file( `${this.runtime.getSetting("TWITTER_USERNAME")}_${datestr}_search_context`, - context, + context ); const responseContent = await generateMessageResponse({ @@ -280,7 +284,7 @@ export class TwitterSearchClient extends ClientBase { log_to_file( `${this.runtime.getSetting("TWITTER_USERNAME")}_${datestr}_search_response`, - JSON.stringify(responseContent), + JSON.stringify(responseContent) ); const response = responseContent; @@ -291,7 +295,7 @@ export class TwitterSearchClient extends ClientBase { } console.log( - `Bot would respond to tweet ${selectedTweet.id} with: ${response.text}`, + `Bot would respond to tweet ${selectedTweet.id} with: ${response.text}` ); try { const callback: HandlerCallback = async (response: Content) => { diff --git a/src/core/generation.ts b/src/core/generation.ts index 48587c1b..2ea36ad3 100644 --- a/src/core/generation.ts +++ b/src/core/generation.ts @@ -72,8 +72,6 @@ export async function generateText({ case ModelProvider.LLAMACLOUD: const openai = createOpenAI({ apiKey }); - console.log("Context:\n", context); - const { text: openaiResponse } = await aiGenerateText({ model: openai.languageModel(model), prompt: context, @@ -84,7 +82,6 @@ export async function generateText({ }); response = openaiResponse; - console.log("OpenAI Response:\n", response); break; case ModelProvider.ANTHROPIC: @@ -118,7 +115,6 @@ export async function generateText({ break; case ModelProvider.LLAMALOCAL: - console.log("queueing text generateText"); response = await runtime.llamaService.queueTextCompletion( context, temperature, @@ -133,8 +129,6 @@ export async function generateText({ throw new Error(`Unsupported provider: ${provider}`); } - console.log(response) - return response; } catch (error) { console.error('Error in generateText:', error); diff --git a/src/core/types.ts b/src/core/types.ts index 6223df83..0149ca1e 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -56,7 +56,7 @@ export interface Objective { export enum GoalStatus { DONE = "DONE", FAILED = "FAILED", - IN_PROGRESS = "IN_PROGRESS", + IN_PROGRESS = "IN_PROGRESS" } /** @@ -151,6 +151,7 @@ export interface Memory { content: Content; // The content of the memory, which can be a structured object or a plain string. embedding?: number[]; // An optional embedding vector representing the semantic content of the memory. roomId: UUID; // The room or conversation ID associated with the memory. + unique?: boolean; // Whether the memory is unique or not } /** @@ -169,13 +170,13 @@ export type Handler = ( message: Memory, state?: State, options?: { [key: string]: unknown }, // additional options can be used for things like tests or state-passing on a chain - callback?: HandlerCallback, + callback?: HandlerCallback ) => Promise; // export type HandlerCallback = ( response: Content, - files?: any, + files?: any ) => Promise; /** @@ -184,7 +185,7 @@ export type HandlerCallback = ( export type Validator = ( runtime: IAgentRuntime, message: Memory, - state?: State, + state?: State ) => Promise; /** @@ -355,19 +356,19 @@ export interface IDatabaseAdapter { roomId?: UUID; unique?: boolean; tableName: string; - }, + } ): Promise; createMemory( memory: Memory, tableName: string, - unique?: boolean, + unique?: boolean ): Promise; removeMemory(memoryId: UUID, tableName: string): Promise; removeAllMemories(roomId: UUID, tableName: string): Promise; countMemories( roomId: UUID, unique?: boolean, - tableName?: string, + tableName?: string ): Promise; getGoals(params: { roomId: UUID; @@ -390,12 +391,12 @@ export interface IDatabaseAdapter { getParticipantsForRoom(roomId: UUID): Promise; getParticipantUserState( roomId: UUID, - userId: UUID, + userId: UUID ): Promise<"FOLLOWED" | "MUTED" | null>; setParticipantUserState( roomId: UUID, userId: UUID, - state: "FOLLOWED" | "MUTED" | null, + state: "FOLLOWED" | "MUTED" | null ): Promise; createRelationship(params: { userA: UUID; userB: UUID }): Promise; getRelationship(params: { @@ -421,7 +422,7 @@ export interface IMemoryManager { end?: number; }): Promise; getCachedEmbeddings( - content: string, + content: string ): Promise<{ embedding: number[]; levenshtein_score: number }[]>; getMemoryById(id: UUID): Promise; getMemoriesByRoomIds(params: { roomIds: UUID[] }): Promise; @@ -432,7 +433,7 @@ export interface IMemoryManager { count?: number; roomId: UUID; unique?: boolean; - }, + } ): Promise; createMemory(memory: Memory, unique?: boolean): Promise; removeMemory(memoryId: UUID): Promise; @@ -471,7 +472,7 @@ export interface IAgentRuntime { message: Memory, responses: Memory[], state?: State, - callback?: HandlerCallback, + callback?: HandlerCallback ): Promise; evaluate(message: Memory, state?: State): Promise; ensureParticipantExists(userId: UUID, roomId: UUID): Promise; @@ -479,14 +480,14 @@ export interface IAgentRuntime { userId: UUID, userName: string | null, name: string | null, - source: string | null, + source: string | null ): Promise; registerAction(action: Action): void; ensureParticipantInRoom(userId: UUID, roomId: UUID): Promise; ensureRoomExists(roomId: UUID): Promise; composeState( message: Memory, - additionalKeys?: { [key: string]: unknown }, + additionalKeys?: { [key: string]: unknown } ): Promise; updateRecentMessageState(state: State): Promise; } @@ -494,7 +495,7 @@ export interface IAgentRuntime { export interface IImageRecognitionService { initialize(modelId?: string | null, device?: string | null): Promise; describeImage( - imageUrl: string, + imageUrl: string ): Promise<{ title: string; description: string }>; } @@ -518,7 +519,7 @@ export interface ILlamaService { stop: string[], frequency_penalty: number, presence_penalty: number, - max_tokens: number, + max_tokens: number ): Promise; queueTextCompletion( context: string, @@ -526,7 +527,7 @@ export interface ILlamaService { stop: string[], frequency_penalty: number, presence_penalty: number, - max_tokens: number, + max_tokens: number ): Promise; getEmbeddingResponse(input: string): Promise; } @@ -535,7 +536,7 @@ export interface IBrowserService { initialize(): Promise; closeBrowser(): Promise; getPageContent( - url: string, + url: string ): Promise<{ title: string; description: string; bodyContent: string }>; } diff --git a/src/index.ts b/src/index.ts index 13e897ce..e52a8901 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,15 +3,22 @@ import fs from "fs"; import yargs from "yargs"; import askClaude from "./actions/ask_claude.ts"; import follow_room from "./actions/follow_room.ts"; +import imageGeneration from "./actions/imageGeneration.ts"; import mute_room from "./actions/mute_room.ts"; +import swap from "./actions/swap.ts"; import unfollow_room from "./actions/unfollow_room.ts"; import unmute_room from "./actions/unmute_room.ts"; -import imageGeneration from "./actions/imageGeneration.ts"; -import swap from "./actions/swap.ts"; +import { + PostgresDatabaseAdapter +} from "./adapters/postgres.ts"; import { SqliteDatabaseAdapter } from "./adapters/sqlite.ts"; -import { DiscordClient } from "./clients/discord/index.ts"; import DirectClient from "./clients/direct/index.ts"; +import { DiscordClient } from "./clients/discord/index.ts"; import { TelegramClient } from "./clients/telegram/src/index.ts"; // Added Telegram import +import { TwitterGenerationClient } from "./clients/twitter/generate.ts"; +import { TwitterInteractionClient } from "./clients/twitter/interactions.ts"; +import { TwitterSearchClient } from "./clients/twitter/search.ts"; +import { wait } from "./clients/twitter/utils.ts"; import { defaultActions } from "./core/actions.ts"; import defaultCharacter from "./core/defaultCharacter.ts"; import { AgentRuntime } from "./core/runtime.ts"; @@ -19,12 +26,10 @@ import settings from "./core/settings.ts"; import { Character, IAgentRuntime, ModelProvider } from "./core/types.ts"; // Added IAgentRuntime import boredomProvider from "./providers/boredom.ts"; import timeProvider from "./providers/time.ts"; -import { wait } from "./clients/twitter/utils.ts"; -import { TwitterSearchClient } from "./clients/twitter/search.ts"; -import { TwitterInteractionClient } from "./clients/twitter/interactions.ts"; -import { TwitterGenerationClient } from "./clients/twitter/generate.ts"; import walletProvider from "./providers/wallet.ts"; - +import readline from 'readline'; +import orderbook from "./providers/order_book.ts"; +import tokenProvider from "./providers/token.ts"; interface Arguments { character?: string; characters?: string; @@ -35,7 +40,7 @@ interface Arguments { let argv: Arguments = { character: "./src/agent/default_character.json", - characters: "", + characters: "" }; try { @@ -43,16 +48,16 @@ try { argv = yargs(process.argv.slice(2)) .option("character", { type: "string", - description: "Path to the character JSON file", + description: "Path to the character JSON file" }) .option("characters", { type: "string", - description: "Comma separated list of paths to character JSON files", + description: "Comma separated list of paths to character JSON files" }) .option("telegram", { type: "boolean", description: "Enable Telegram client", - default: false, + default: false }) .parseSync() as Arguments; } catch (error) { @@ -96,7 +101,18 @@ async function startAgent(character: Character) { console.log("Starting agent for character " + character.name); const token = getTokenForProvider(character.modelProvider, character); - const db = new SqliteDatabaseAdapter(new Database("./db.sqlite")) + let db; + if (process.env.POSTGRES_URL) { + // const db = new SqliteDatabaseAdapter(new Database("./db.sqlite")); + db = new PostgresDatabaseAdapter({ + connectionString: process.env.POSTGRES_URL + }); + } else { + db = new SqliteDatabaseAdapter(new Database("./db.sqlite")); + // Debug adapter + // const loggingDb = createLoggingDatabaseAdapter(db); + } + const runtime = new AgentRuntime({ databaseAdapter: db, token, @@ -144,39 +160,44 @@ async function startAgent(character: Character) { ); return null; } - + console.log("✅ Bot token found, initializing Telegram client..."); - + try { console.log("Creating new TelegramClient instance..."); const telegramClient = new TelegramClient(runtime, botToken); - + console.log("Calling start() on TelegramClient..."); await telegramClient.start(); - - console.log(`✅ Telegram client successfully started for character ${character.name}`); + + console.log( + `✅ Telegram client successfully started for character ${character.name}` + ); return telegramClient; } catch (error) { - console.error(`❌ Error creating/starting Telegram client for ${character.name}:`, error); + console.error( + `❌ Error creating/starting Telegram client for ${character.name}:`, + error + ); return null; } } async function startTwitter(runtime) { - console.log("Starting search client"); - const twitterSearchClient = new TwitterSearchClient(runtime); - await wait(); - console.log("Starting interaction client"); - const twitterInteractionClient = new TwitterInteractionClient(runtime); - await wait(); - console.log("Starting generation client"); - const twitterGenerationClient = new TwitterGenerationClient(runtime); - - return { - twitterInteractionClient, - twitterSearchClient, - twitterGenerationClient, - }; + console.log("Starting search client"); + const twitterSearchClient = new TwitterSearchClient(runtime); + await wait(); + console.log("Starting interaction client"); + const twitterInteractionClient = new TwitterInteractionClient(runtime); + await wait(); + console.log("Starting generation client"); + const twitterGenerationClient = new TwitterGenerationClient(runtime); + + return { + twitterInteractionClient, + twitterSearchClient, + twitterGenerationClient + }; } if (!character.clients) { @@ -192,7 +213,8 @@ async function startAgent(character: Character) { // Add Telegram client initialization if ( - (argv.telegram || character.clients.map((str) => str.toLowerCase()).includes("telegram")) + argv.telegram || + character.clients.map((str) => str.toLowerCase()).includes("telegram") ) { console.log("🔄 Telegram client enabled, starting initialization..."); const telegramClient = await startTelegram(runtime, character); @@ -205,14 +227,16 @@ async function startAgent(character: Character) { } if (character.clients.map((str) => str.toLowerCase()).includes("twitter")) { - const { - twitterInteractionClient, - twitterSearchClient, - twitterGenerationClient, - } = await startTwitter(runtime); - clients.push( - twitterInteractionClient, twitterSearchClient, twitterGenerationClient, - ); + const { + twitterInteractionClient, + twitterSearchClient, + twitterGenerationClient + } = await startTwitter(runtime); + clients.push( + twitterInteractionClient, + twitterSearchClient, + twitterGenerationClient + ); } directClient.registerAgent(directRuntime); @@ -232,33 +256,29 @@ const startAgents = async () => { startAgents(); -import readline from 'readline'; -import orderbook from "./providers/order_book.ts"; -import tokenProvider from "./providers/token.ts"; - const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); function chat() { - rl.question('You: ', async (input) => { - if (input.toLowerCase() === 'exit') { + rl.question("You: ", async (input) => { + if (input.toLowerCase() === "exit") { rl.close(); return; } const agentId = characters[0].name.toLowerCase(); // Assuming we're using the first character const response = await fetch(`http://localhost:3000/${agentId}/message`, { - method: 'POST', + method: "POST", headers: { - 'Content-Type': 'application/json', + "Content-Type": "application/json" }, body: JSON.stringify({ text: input, - userId: 'user', - userName: 'User', - }), + userId: "user", + userName: "User" + }) }); const data = await response.json(); @@ -268,4 +288,4 @@ function chat() { } console.log("Chat started. Type 'exit' to quit."); -chat(); \ No newline at end of file +chat(); diff --git a/src/providers/cache/dexScreenerData_2weMjPLLybRMMva1fM3U31goWWrCpF59CHWNhnCJ9Vyh.json b/src/providers/cache/dexScreenerData_2weMjPLLybRMMva1fM3U31goWWrCpF59CHWNhnCJ9Vyh.json new file mode 100644 index 00000000..afb7d0a6 --- /dev/null +++ b/src/providers/cache/dexScreenerData_2weMjPLLybRMMva1fM3U31goWWrCpF59CHWNhnCJ9Vyh.json @@ -0,0 +1 @@ +{"data":{"schemaVersion":"1.0.0","pairs":[{"chainId":"solana","dexId":"raydium","url":"https://dexscreener.com/solana/9p8pi37msc1dwbvilujf4i82sbdf64invec1kcr1x5vk","pairAddress":"9P8Pi37MsC1dWBViLUJF4i82sbDf64iNVec1KCr1X5vk","baseToken":{"address":"2weMjPLLybRMMva1fM3U31goWWrCpF59CHWNhnCJ9Vyh","name":"SHARPEI","symbol":"SHAR"},"quoteToken":{"address":"So11111111111111111111111111111111111111112","name":"Wrapped SOL","symbol":"SOL"},"priceNative":"0.000000000003380","priceUsd":"0.0000000005930","txns":{"m5":{"buys":0,"sells":0},"h1":{"buys":0,"sells":0},"h6":{"buys":0,"sells":0},"h24":{"buys":0,"sells":1}},"volume":{"h24":1.17,"h6":0,"h1":0,"m5":0},"priceChange":{"m5":0,"h1":0,"h6":0,"h24":0},"liquidity":{"usd":73.41,"base":62816039761,"quote":0.2061},"fdv":59,"marketCap":59,"pairCreatedAt":1729700095000,"info":{"imageUrl":"https://dd.dexscreener.com/ds-data/tokens/solana/2weMjPLLybRMMva1fM3U31goWWrCpF59CHWNhnCJ9Vyh.png","websites":[{"label":"Website","url":"https://www.sharpei.xyz/"}],"socials":[{"type":"twitter","url":"https://x.com/SolanaKol"},{"type":"telegram","url":"https://t.me/sharsol"}]}}]},"expiry":1730420206609} \ No newline at end of file diff --git a/src/providers/cache/holderList_2weMjPLLybRMMva1fM3U31goWWrCpF59CHWNhnCJ9Vyh.json b/src/providers/cache/holderList_2weMjPLLybRMMva1fM3U31goWWrCpF59CHWNhnCJ9Vyh.json new file mode 100644 index 00000000..b12c6d73 --- /dev/null +++ b/src/providers/cache/holderList_2weMjPLLybRMMva1fM3U31goWWrCpF59CHWNhnCJ9Vyh.json @@ -0,0 +1 @@ +{"data":[],"expiry":1730420207793} \ No newline at end of file diff --git a/src/providers/cache/tokenSecurity_2weMjPLLybRMMva1fM3U31goWWrCpF59CHWNhnCJ9Vyh.json b/src/providers/cache/tokenSecurity_2weMjPLLybRMMva1fM3U31goWWrCpF59CHWNhnCJ9Vyh.json new file mode 100644 index 00000000..43a57cd7 --- /dev/null +++ b/src/providers/cache/tokenSecurity_2weMjPLLybRMMva1fM3U31goWWrCpF59CHWNhnCJ9Vyh.json @@ -0,0 +1 @@ +{"data":{"ownerBalance":null,"creatorBalance":0,"ownerPercentage":null,"creatorPercentage":0,"top10HolderBalance":97984326430.19872,"top10HolderPercent":0.9922779616670393},"expiry":1730420206118} \ No newline at end of file diff --git a/src/providers/cache/tokenTradeData_2weMjPLLybRMMva1fM3U31goWWrCpF59CHWNhnCJ9Vyh.json b/src/providers/cache/tokenTradeData_2weMjPLLybRMMva1fM3U31goWWrCpF59CHWNhnCJ9Vyh.json new file mode 100644 index 00000000..8aaf0c8e --- /dev/null +++ b/src/providers/cache/tokenTradeData_2weMjPLLybRMMva1fM3U31goWWrCpF59CHWNhnCJ9Vyh.json @@ -0,0 +1 @@ +{"data":{"address":"2weMjPLLybRMMva1fM3U31goWWrCpF59CHWNhnCJ9Vyh","holder":1407,"market":1,"last_trade_unix_time":1730353661,"last_trade_human_time":"2024-10-31T05:47:41","price":5.92913712012338e-10,"history_30m_price":5.92913712012338e-10,"price_change_30m_percent":0,"history_1h_price":5.92913712012338e-10,"price_change_1h_percent":0,"history_2h_price":5.92913712012338e-10,"price_change_2h_percent":0,"history_4h_price":5.92913712012338e-10,"price_change_4h_percent":0,"history_6h_price":5.92913712012338e-10,"price_change_6h_percent":0,"history_8h_price":5.92913712012338e-10,"price_change_8h_percent":0,"history_12h_price":5.92913712012338e-10,"price_change_12h_percent":0,"history_24h_price":5.797865002251019e-10,"price_change_24h_percent":2.2641458161132384,"unique_wallet_30m":null,"unique_wallet_history_30m":null,"unique_wallet_30m_change_percent":null,"unique_wallet_1h":null,"unique_wallet_history_1h":null,"unique_wallet_1h_change_percent":null,"unique_wallet_2h":null,"unique_wallet_history_2h":null,"unique_wallet_2h_change_percent":null,"unique_wallet_4h":null,"unique_wallet_history_4h":null,"unique_wallet_4h_change_percent":null,"unique_wallet_8h":null,"unique_wallet_history_8h":null,"unique_wallet_8h_change_percent":null,"unique_wallet_24h":3,"unique_wallet_history_24h":3,"unique_wallet_24h_change_percent":0,"trade_30m":0,"trade_history_30m":0,"trade_30m_change_percent":null,"sell_30m":0,"sell_history_30m":0,"sell_30m_change_percent":null,"buy_30m":0,"buy_history_30m":0,"buy_30m_change_percent":null,"volume_30m":0,"volume_30m_usd":0,"volume_history_30m":0,"volume_history_30m_usd":0,"volume_30m_change_percent":null,"volume_buy_30m":null,"volume_buy_30m_usd":null,"volume_buy_history_30m":null,"volume_buy_history_30m_usd":null,"volume_buy_30m_change_percent":null,"volume_sell_30m":null,"volume_sell_30m_usd":null,"volume_sell_history_30m":null,"volume_sell_history_30m_usd":null,"volume_sell_30m_change_percent":null,"trade_1h":0,"trade_history_1h":0,"trade_1h_change_percent":null,"sell_1h":0,"sell_history_1h":0,"sell_1h_change_percent":null,"buy_1h":0,"buy_history_1h":0,"buy_1h_change_percent":null,"volume_1h":0,"volume_1h_usd":0,"volume_history_1h":0,"volume_history_1h_usd":0,"volume_1h_change_percent":null,"volume_buy_1h":null,"volume_buy_1h_usd":null,"volume_buy_history_1h":null,"volume_buy_history_1h_usd":null,"volume_buy_1h_change_percent":null,"volume_sell_1h":null,"volume_sell_1h_usd":null,"volume_sell_history_1h":null,"volume_sell_history_1h_usd":null,"volume_sell_1h_change_percent":null,"trade_2h":0,"trade_history_2h":0,"trade_2h_change_percent":null,"sell_2h":0,"sell_history_2h":0,"sell_2h_change_percent":null,"buy_2h":0,"buy_history_2h":0,"buy_2h_change_percent":null,"volume_2h":0,"volume_2h_usd":0,"volume_history_2h":0,"volume_history_2h_usd":0,"volume_2h_change_percent":null,"volume_buy_2h":null,"volume_buy_2h_usd":null,"volume_buy_history_2h":null,"volume_buy_history_2h_usd":null,"volume_buy_2h_change_percent":null,"volume_sell_2h":null,"volume_sell_2h_usd":null,"volume_sell_history_2h":null,"volume_sell_history_2h_usd":null,"volume_sell_2h_change_percent":null,"trade_4h":0,"trade_history_4h":0,"trade_4h_change_percent":null,"sell_4h":0,"sell_history_4h":0,"sell_4h_change_percent":null,"buy_4h":0,"buy_history_4h":0,"buy_4h_change_percent":null,"volume_4h":0,"volume_4h_usd":0,"volume_history_4h":0,"volume_history_4h_usd":0,"volume_4h_change_percent":null,"volume_buy_4h":null,"volume_buy_4h_usd":null,"volume_buy_history_4h":null,"volume_buy_history_4h_usd":null,"volume_buy_4h_change_percent":null,"volume_sell_4h":null,"volume_sell_4h_usd":null,"volume_sell_history_4h":null,"volume_sell_history_4h_usd":null,"volume_sell_4h_change_percent":null,"trade_8h":0,"trade_history_8h":0,"trade_8h_change_percent":null,"sell_8h":0,"sell_history_8h":0,"sell_8h_change_percent":null,"buy_8h":0,"buy_history_8h":0,"buy_8h_change_percent":null,"volume_8h":0,"volume_8h_usd":0,"volume_history_8h":0,"volume_history_8h_usd":0,"volume_8h_change_percent":null,"volume_buy_8h":null,"volume_buy_8h_usd":null,"volume_buy_history_8h":null,"volume_buy_history_8h_usd":null,"volume_buy_8h_change_percent":null,"volume_sell_8h":null,"volume_sell_8h_usd":null,"volume_sell_history_8h":null,"volume_sell_history_8h_usd":null,"volume_sell_8h_change_percent":null,"trade_24h":1,"trade_history_24h":2,"trade_24h_change_percent":-50,"sell_24h":1,"sell_history_24h":1,"sell_24h_change_percent":0,"buy_24h":0,"buy_history_24h":1,"buy_24h_change_percent":-100,"volume_24h":1988689816.32415,"volume_24h_usd":1.179121460595915,"volume_history_24h":6255934028.790945,"volume_history_24h_usd":3.6796057811224863,"volume_24h_change_percent":-68.21114469603039,"volume_buy_24h":0.00019359588623046875,"volume_buy_24h_usd":3.49102080576813e-10,"volume_buy_history_24h":3169272907.791343,"volume_buy_history_24h_usd":1.8375016478157893,"volume_buy_24h_change_percent":-99.99999999999389,"volume_sell_24h":1988689816.3239565,"volume_sell_24h_usd":1.179121460246813,"volume_sell_history_24h":3086661120.999602,"volume_sell_history_24h_usd":1.8421041333066972,"volume_sell_24h_change_percent":-35.57148846712632},"expiry":1730420206358} \ No newline at end of file diff --git a/src/providers/wallet.ts b/src/providers/wallet.ts index 4edecaaa..4ece3750 100644 --- a/src/providers/wallet.ts +++ b/src/providers/wallet.ts @@ -247,7 +247,7 @@ const walletProvider: Provider = { const porfolio = await provider.getFormattedPortfolio(runtime); return porfolio; } catch (error) { - console.error("Error in wallet provider:", error); + console.error("Error in wallet provider:", error.message); return `Failed to fetch wallet information: ${error instanceof Error ? error.message : 'Unknown error'}`; } } diff --git a/supabase/README.md b/supabase/README.md new file mode 100644 index 00000000..82ed22ff --- /dev/null +++ b/supabase/README.md @@ -0,0 +1,5 @@ +# Postgres Schema + +Install CLI https://www.timescale.com/blog/how-to-install-psql-on-mac-ubuntu-debian-windows/ + +`psql -f ./postgres-schema.sql` diff --git a/supabase/postgres-schema.sql b/supabase/postgres-schema.sql new file mode 100644 index 00000000..aede30a2 --- /dev/null +++ b/supabase/postgres-schema.sql @@ -0,0 +1,101 @@ +-- Enable pgvector extension + +-- -- Drop existing tables and extensions +-- DROP EXTENSION IF EXISTS vector CASCADE; +-- DROP TABLE IF EXISTS relationships CASCADE; +-- DROP TABLE IF EXISTS participants CASCADE; +-- DROP TABLE IF EXISTS logs CASCADE; +-- DROP TABLE IF EXISTS goals CASCADE; +-- DROP TABLE IF EXISTS memories CASCADE; +-- DROP TABLE IF EXISTS rooms CASCADE; +-- DROP TABLE IF EXISTS accounts CASCADE; + + +CREATE EXTENSION IF NOT EXISTS vector; + +BEGIN; + +CREATE TABLE accounts ( + "id" UUID PRIMARY KEY, + "createdAt" TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, + "name" TEXT, + "username" TEXT, + "email" TEXT NOT NULL, + "avatarUrl" TEXT, + "details" JSONB DEFAULT '{}'::jsonb +); + +CREATE TABLE rooms ( + "id" UUID PRIMARY KEY, + "createdAt" TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE memories ( + "id" UUID PRIMARY KEY, + "type" TEXT NOT NULL, + "createdAt" TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, + "content" JSONB NOT NULL, + "embedding" vector(1536), + "userId" UUID REFERENCES accounts("id"), + "roomId" UUID REFERENCES rooms("id"), + "unique" BOOLEAN DEFAULT true NOT NULL, + CONSTRAINT fk_room FOREIGN KEY ("roomId") REFERENCES rooms("id") ON DELETE CASCADE, + CONSTRAINT fk_user FOREIGN KEY ("userId") REFERENCES accounts("id") ON DELETE CASCADE +); + +CREATE TABLE goals ( + "id" UUID PRIMARY KEY, + "createdAt" TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, + "userId" UUID REFERENCES accounts("id"), + "name" TEXT, + "status" TEXT, + "description" TEXT, + "roomId" UUID REFERENCES rooms("id"), + "objectives" JSONB DEFAULT '[]'::jsonb NOT NULL, + CONSTRAINT fk_room FOREIGN KEY ("roomId") REFERENCES rooms("id") ON DELETE CASCADE, + CONSTRAINT fk_user FOREIGN KEY ("userId") REFERENCES accounts("id") ON DELETE CASCADE +); + +CREATE TABLE logs ( + "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(), + "createdAt" TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, + "userId" UUID NOT NULL REFERENCES accounts("id"), + "body" JSONB NOT NULL, + "type" TEXT NOT NULL, + "roomId" UUID NOT NULL REFERENCES rooms("id"), + CONSTRAINT fk_room FOREIGN KEY ("roomId") REFERENCES rooms("id") ON DELETE CASCADE, + CONSTRAINT fk_user FOREIGN KEY ("userId") REFERENCES accounts("id") ON DELETE CASCADE +); + +CREATE TABLE participants ( + "id" UUID PRIMARY KEY, + "createdAt" TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, + "userId" UUID REFERENCES accounts("id"), + "roomId" UUID REFERENCES rooms("id"), + "userState" TEXT, + "last_message_read" TEXT, + UNIQUE("userId", "roomId"), + CONSTRAINT fk_room FOREIGN KEY ("roomId") REFERENCES rooms("id") ON DELETE CASCADE, + CONSTRAINT fk_user FOREIGN KEY ("userId") REFERENCES accounts("id") ON DELETE CASCADE +); + +CREATE TABLE relationships ( + "id" UUID PRIMARY KEY, + "createdAt" TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, + "userA" UUID NOT NULL REFERENCES accounts("id"), + "userB" UUID NOT NULL REFERENCES accounts("id"), + "status" TEXT, + "userId" UUID NOT NULL REFERENCES accounts("id"), + CONSTRAINT fk_user_a FOREIGN KEY ("userA") REFERENCES accounts("id") ON DELETE CASCADE, + CONSTRAINT fk_user_b FOREIGN KEY ("userB") REFERENCES accounts("id") ON DELETE CASCADE, + CONSTRAINT fk_user FOREIGN KEY ("userId") REFERENCES accounts("id") ON DELETE CASCADE +); + +-- Indexes +CREATE INDEX idx_memories_embedding ON memories USING hnsw ("embedding" vector_cosine_ops); +CREATE INDEX idx_memories_type_room ON memories("type", "roomId"); +CREATE INDEX idx_participants_user ON participants("userId"); +CREATE INDEX idx_participants_room ON participants("roomId"); +CREATE INDEX idx_relationships_users ON relationships("userA", "userB"); + +COMMIT; \ No newline at end of file