feat: openai js and python sdk utilities (#389)
Some checks are pending
Publish AI SDK / publish (push) Waiting to run

needs testing
This commit is contained in:
CodeWithShreyans 2025-08-27 23:34:49 +00:00
parent 1d5298e05e
commit 3a0e264b7e
No known key found for this signature in database
GPG key ID: A1B43A0A7B74CD2C
25 changed files with 4709 additions and 83 deletions

View file

@ -2,6 +2,8 @@ name: Publish AI SDK
on:
push:
branches:
- main
paths:
- "packages/ai-sdk/package.json"

3
.gitignore vendored
View file

@ -38,3 +38,6 @@ yarn-error.log*
# Misc
.DS_Store
*.pem
.venv
__pycache__

103
bun.lock
View file

@ -136,18 +136,18 @@
},
"packages/ai-sdk": {
"name": "@supermemory/ai-sdk",
"version": "0.0.1",
"version": "1.0.5",
"dependencies": {
"@ai-sdk/openai": "^2.0.15",
"@ai-sdk/openai": "^2.0.22",
"@ai-sdk/provider": "^2.0.0",
"ai": "^5.0.17",
"supermemory": "^3.0.0-alpha.22",
"zod": "^4.0.17",
"ai": "^5.0.26",
"supermemory": "^3.0.0-alpha.26",
"zod": "^4.1.4",
},
"devDependencies": {
"@total-typescript/tsconfig": "^1.0.4",
"@types/bun": "^1.2.20",
"tsdown": "^0.14.1",
"@types/bun": "^1.2.21",
"tsdown": "^0.14.2",
"typescript": "^5.9.2",
"vitest": "^3.2.4",
},
@ -166,6 +166,23 @@
"ai-gateway-provider": "^0.0.11",
},
},
"packages/openai-sdk-ts": {
"name": "@supermemory/openai-sdk",
"version": "1.0.0",
"dependencies": {
"openai": "^4.63.0",
"supermemory": "^3.0.0-alpha.22",
"zod": "^4.0.17",
},
"devDependencies": {
"@total-typescript/tsconfig": "^1.0.4",
"@types/bun": "^1.2.20",
"dotenv": "^16.4.7",
"tsdown": "^0.14.1",
"typescript": "^5.9.2",
"vitest": "^3.2.4",
},
},
"packages/ui": {
"name": "@repo/ui",
"version": "0.0.0",
@ -842,9 +859,9 @@
"@opentelemetry/sql-common": ["@opentelemetry/sql-common@0.41.0", "", { "dependencies": { "@opentelemetry/core": "^2.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0" } }, "sha512-pmzXctVbEERbqSfiAgdes9Y63xjoOyXcD7B6IXBkVb+vbM7M9U98mn33nGXxPf4dfYR0M+vhcKRZmbSJ7HfqFA=="],
"@oxc-project/runtime": ["@oxc-project/runtime@0.82.2", "", {}, "sha512-cYxcj5CPn/vo5QSpCZcYzBiLidU5+GlFSqIeNaMgBDtcVRBsBJHZg3pHw999W6nHamFQ1EHuPPByB26tjaJiJw=="],
"@oxc-project/runtime": ["@oxc-project/runtime@0.82.3", "", {}, "sha512-LNh5GlJvYHAnMurO+EyA8jJwN1rki7l3PSHuosDh2I7h00T6/u9rCkUjg/SvPmT1CZzvhuW0y+gf7jcqUy/Usg=="],
"@oxc-project/types": ["@oxc-project/types@0.82.2", "", {}, "sha512-WMGSwd9FsNBs/WfqIOH0h3k1LBdjZJQGYjGnC+vla/fh6HUsu5HzGPerRljiq1hgMQ6gs031YJR12VyP57b/hQ=="],
"@oxc-project/types": ["@oxc-project/types@0.82.3", "", {}, "sha512-6nCUxBnGX0c6qfZW5MaF6/fmu5dHJDMiMPaioKHKs5mi5+8/FHQ7WGjgQIz1zxpmceMYfdIXkOaLYE+ejbuOtA=="],
"@peculiar/asn1-android": ["@peculiar/asn1-android@2.4.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.4.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-YFueREq97CLslZZBI8dKzis7jMfEHSLxM+nr0Zdx1POiXFLjqqwoY5s0F1UimdBiEw/iKlHey2m56MRDv7Jtyg=="],
@ -986,35 +1003,35 @@
"@repo/web": ["@repo/web@workspace:apps/web"],
"@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0-beta.33", "", { "os": "android", "cpu": "arm64" }, "sha512-xhDQXKftRkEULIxCddrKMR8y0YO/Y+6BKk/XrQP2B29YjV2wr8DByoEz+AHX9BfLHb2srfpdN46UquBW2QXWpQ=="],
"@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0-beta.34", "", { "os": "android", "cpu": "arm64" }, "sha512-jf5GNe5jP3Sr1Tih0WKvg2bzvh5T/1TA0fn1u32xSH7ca/p5t+/QRr4VRFCV/na5vjwKEhwWrChsL2AWlY+eoA=="],
"@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0-beta.33", "", { "os": "darwin", "cpu": "arm64" }, "sha512-7lhhY08v5ZtRq8JJQaJ49fnJombAPnqllKKCDLU/UvaqNAOEyTGC8J1WVOLC4EA4zbXO5U3CCRgVGyAFNH2VtQ=="],
"@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0-beta.34", "", { "os": "darwin", "cpu": "arm64" }, "sha512-2F/TqH4QuJQ34tgWxqBjFL3XV1gMzeQgUO8YRtCPGBSP0GhxtoFzsp7KqmQEothsxztlv+KhhT9Dbg3HHwHViQ=="],
"@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.0-beta.33", "", { "os": "darwin", "cpu": "x64" }, "sha512-U2iGjcDV7NWyYyhap8YuY0nwrLX6TvX/9i7gBtdEMPm9z3wIUVGNMVdGlA43uqg7xDpRGpEqGnxbeDgiEwYdnA=="],
"@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.0-beta.34", "", { "os": "darwin", "cpu": "x64" }, "sha512-E1QuFslgLWbHQ8Qli/AqUKdfg0pockQPwRxVbhNQ74SciZEZpzLaujkdmOLSccMlSXDfFCF8RPnMoRAzQ9JV8Q=="],
"@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.0-beta.33", "", { "os": "freebsd", "cpu": "x64" }, "sha512-gd6ASromVHFLlzrjJWMG5CXHkS7/36DEZ8HhvGt2NN8eZALCIuyEx8HMMLqvKA7z4EAztVkdToVrdxpGMsKZxw=="],
"@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.0-beta.34", "", { "os": "freebsd", "cpu": "x64" }, "sha512-VS8VInNCwnkpI9WeQaWu3kVBq9ty6g7KrHdLxYMzeqz24+w9hg712TcWdqzdY6sn+24lUoMD9jTZrZ/qfVpk0g=="],
"@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.33", "", { "os": "linux", "cpu": "arm" }, "sha512-xmeLfkfGthuynO1EpCdyTVr0r4G+wqvnKCuyR6rXOet+hLrq5HNAC2XtP/jU2TB4Bc6aiLYxl868B8CGtFDhcw=="],
"@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.34", "", { "os": "linux", "cpu": "arm" }, "sha512-4St4emjcnULnxJYb/5ZDrH/kK/j6PcUgc3eAqH5STmTrcF+I9m/X2xvSF2a2bWv1DOQhxBewThu0KkwGHdgu5w=="],
"@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.0-beta.33", "", { "os": "linux", "cpu": "arm64" }, "sha512-cHGp8yfHL4pes6uaLbO5L58ceFkUK4efd8iE86jClD1QPPDLKiqEXJCFYeuK3OfODuF5EBOmf0SlcUZNEYGdmw=="],
"@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.0-beta.34", "", { "os": "linux", "cpu": "arm64" }, "sha512-a737FTqhFUoWfnebS2SnQ2BS50p0JdukdkUBwy2J06j4hZ6Eej0zEB8vTfAqoCjn8BQKkXBy+3Sx0IRkgwz1gA=="],
"@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.0-beta.33", "", { "os": "linux", "cpu": "arm64" }, "sha512-wZ1t7JAvVeFgskH1L9y7c47ITitPytpL0s8FmAT8pVfXcaTmS58ZyoXT+y6cz8uCkQnETjrX3YezTGI18u3ecg=="],
"@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.0-beta.34", "", { "os": "linux", "cpu": "arm64" }, "sha512-NH+FeQWKyuw0k+PbXqpFWNfvD8RPvfJk766B/njdaWz4TmiEcSB0Nb6guNw1rBpM1FmltQYb3fFnTumtC6pRfA=="],
"@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.0-beta.33", "", { "os": "linux", "cpu": "x64" }, "sha512-cDndWo3VEYbm7yeujOV6Ie2XHz0K8YX/R/vbNmMo03m1QwtBKKvbYNSyJb3B9+8igltDjd8zNM9mpiNNrq/ekQ=="],
"@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.0-beta.34", "", { "os": "linux", "cpu": "x64" }, "sha512-Q3RSCivp8pNadYK8ke3hLnQk08BkpZX9BmMjgwae2FWzdxhxxUiUzd9By7kneUL0vRQ4uRnhD9VkFQ+Haeqdvw=="],
"@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.0-beta.33", "", { "os": "linux", "cpu": "x64" }, "sha512-bl7uzi6es/l6LT++NZcBpiX43ldLyKXCPwEZGY1rZJ99HQ7m1g3KxWwYCcGxtKjlb2ExVvDZicF6k+96vxOJKg=="],
"@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.0-beta.34", "", { "os": "linux", "cpu": "x64" }, "sha512-wDd/HrNcVoBhWWBUW3evJHoo7GJE/RofssBy3Dsiip05YUBmokQVrYAyrboOY4dzs/lJ7HYeBtWQ9hj8wlyF0A=="],
"@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.0-beta.33", "", { "os": "none", "cpu": "arm64" }, "sha512-TrgzQanpLgcmmzolCbYA9BPZgF1gYxkIGZhU/HROnJPsq67gcyaYw/JBLioqQLjIwMipETkn25YY799D2OZzJA=="],
"@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.0-beta.34", "", { "os": "none", "cpu": "arm64" }, "sha512-dH3FTEV6KTNWpYSgjSXZzeX7vLty9oBYn6R3laEdhwZftQwq030LKL+5wyQdlbX5pnbh4h127hpv3Hl1+sj8dg=="],
"@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.0-beta.33", "", { "dependencies": { "@napi-rs/wasm-runtime": "^1.0.3" }, "cpu": "none" }, "sha512-z0LltdUfvoKak9SuaLz/M9AVSg+RTOZjFksbZXzC6Svl1odyW4ai21VHhZy3m2Faeeb/rl/9efVLayj+qYEGxw=="],
"@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.0-beta.34", "", { "dependencies": { "@napi-rs/wasm-runtime": "^1.0.3" }, "cpu": "none" }, "sha512-y5BUf+QtO0JsIDKA51FcGwvhJmv89BYjUl8AmN7jqD6k/eU55mH6RJYnxwCsODq5m7KSSTigVb6O7/GqB8wbPw=="],
"@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.0-beta.33", "", { "os": "win32", "cpu": "arm64" }, "sha512-CpvOHyqDNOYx9riD4giyXQDIu72bWRU2Dwt1xFSPlBudk6NumK0OJl6Ch+LPnkp5podQHcQg0mMauAXPVKct7g=="],
"@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.0-beta.34", "", { "os": "win32", "cpu": "arm64" }, "sha512-ga5hFhdTwpaNxEiuxZHWnD3ed0GBAzbgzS5tRHpe0ObptxM1a9Xrq6TVfNQirBLwb5Y7T/FJmJi3pmdLy95ljg=="],
"@rolldown/binding-win32-ia32-msvc": ["@rolldown/binding-win32-ia32-msvc@1.0.0-beta.33", "", { "os": "win32", "cpu": "ia32" }, "sha512-/tNTvZTWHz6HiVuwpR3zR0kGIyCNb+/tFhnJmti+Aw2fAXs3l7Aj0DcXd0646eFKMX8L2w5hOW9H08FXTUkN0g=="],
"@rolldown/binding-win32-ia32-msvc": ["@rolldown/binding-win32-ia32-msvc@1.0.0-beta.34", "", { "os": "win32", "cpu": "ia32" }, "sha512-4/MBp9T9eRnZskxWr8EXD/xHvLhdjWaeX/qY9LPRG1JdCGV3DphkLTy5AWwIQ5jhAy2ZNJR5z2fYRlpWU0sIyQ=="],
"@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.0-beta.33", "", { "os": "win32", "cpu": "x64" }, "sha512-Bb2qK3z7g2mf4zaKRvkohHzweaP1lLbaoBmXZFkY6jJWMm0Z8Pfnh8cOoRlH1IVM1Ufbo8ZZ1WXp1LbOpRMtXw=="],
"@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.0-beta.34", "", { "os": "win32", "cpu": "x64" }, "sha512-7O5iUBX6HSBKlQU4WykpUoEmb0wQmonb6ziKFr3dJTHud2kzDnWMqk344T0qm3uGv9Ddq6Re/94pInxo1G2d4w=="],
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.33", "", {}, "sha512-she25NCG6NoEPC/SEB4pHs5STcnfI4VBFOzjeI63maSPrWME5J2XC8ogrBgp8NaE/xzj28/kbpSaebiMvFRj+w=="],
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.34", "", {}, "sha512-LyAREkZHP5pMom7c24meKmJCdhf2hEyvam2q0unr3or9ydwDL+DJ8chTF6Av/RFPb3rH8UFBdMzO5MxTZW97oA=="],
"@rollup/plugin-commonjs": ["@rollup/plugin-commonjs@28.0.1", "", { "dependencies": { "@rollup/pluginutils": "^5.0.1", "commondir": "^1.0.1", "estree-walker": "^2.0.2", "fdir": "^6.2.0", "is-reference": "1.2.1", "magic-string": "^0.30.3", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^2.68.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-+tNWdlWKbpB3WgBN7ijjYkq9X5uhjmcvyjEght4NmH5fAU++zfQzAJ6wumLS+dNcvwEZhKx2Z+skY8m7v0wGSA=="],
@ -1230,6 +1247,8 @@
"@supermemory/ai-sdk": ["@supermemory/ai-sdk@workspace:packages/ai-sdk"],
"@supermemory/openai-sdk": ["@supermemory/openai-sdk@workspace:packages/openai-sdk-ts"],
"@swc/core": ["@swc/core@1.13.4", "", { "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.24" }, "optionalDependencies": { "@swc/core-darwin-arm64": "1.13.4", "@swc/core-darwin-x64": "1.13.4", "@swc/core-linux-arm-gnueabihf": "1.13.4", "@swc/core-linux-arm64-gnu": "1.13.4", "@swc/core-linux-arm64-musl": "1.13.4", "@swc/core-linux-x64-gnu": "1.13.4", "@swc/core-linux-x64-musl": "1.13.4", "@swc/core-win32-arm64-msvc": "1.13.4", "@swc/core-win32-ia32-msvc": "1.13.4", "@swc/core-win32-x64-msvc": "1.13.4" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" }, "optionalPeers": ["@swc/helpers"] }, "sha512-bCq2GCuKV16DSOOEdaRqHMm1Ok4YEoLoNdgdzp8BS/Hxxr/0NVCHBUgRLLRy/TlJGv20Idx+djd5FIDvsnqMaw=="],
"@swc/core-darwin-arm64": ["@swc/core-darwin-arm64@1.13.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-CGbTu9dGBwgklUj+NAQAYyPjBuoHaNRWK4QXJRv1QNIkhtE27aY7QA9uEON14SODxsio3t8+Pjjl2Mzx1Pxf+g=="],
@ -1328,7 +1347,7 @@
"@tybys/wasm-util": ["@tybys/wasm-util@0.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ=="],
"@types/bun": ["@types/bun@1.2.20", "", { "dependencies": { "bun-types": "1.2.20" } }, "sha512-dX3RGzQ8+KgmMw7CsW4xT5ITBSCrSbfHc36SNT31EOUg/LA9JWq0VDdEXDRSe1InVWpd2yLUM1FUF/kEOyTzYA=="],
"@types/bun": ["@types/bun@1.2.21", "", { "dependencies": { "bun-types": "1.2.21" } }, "sha512-NiDnvEqmbfQ6dmZ3EeUO577s4P5bf4HCTXtI6trMc6f6RzirY5IrF3aIookuSpyslFzrnvv2lmEWv5HyC1X79A=="],
"@types/chai": ["@types/chai@5.2.2", "", { "dependencies": { "@types/deep-eql": "*" } }, "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg=="],
@ -1774,7 +1793,7 @@
"drizzle-zod": ["drizzle-zod@0.7.1", "", { "peerDependencies": { "drizzle-orm": ">=0.36.0", "zod": ">=3.0.0" } }, "sha512-nZzALOdz44/AL2U005UlmMqaQ1qe5JfanvLujiTHiiT8+vZJTBFhj3pY4Vk+L6UWyKFfNmLhk602Hn4kCTynKQ=="],
"dts-resolver": ["dts-resolver@2.1.1", "", { "peerDependencies": { "oxc-resolver": ">=11.0.0" }, "optionalPeers": ["oxc-resolver"] }, "sha512-3BiGFhB6mj5Kv+W2vdJseQUYW+SKVzAFJL6YNP6ursbrwy1fXHRotfHi3xLNxe4wZl/K8qbAFeCDjZLjzqxxRw=="],
"dts-resolver": ["dts-resolver@2.1.2", "", { "peerDependencies": { "oxc-resolver": ">=11.0.0" }, "optionalPeers": ["oxc-resolver"] }, "sha512-xeXHBQkn2ISSXxbJWD828PFjtyg+/UrMDo7W4Ffcs7+YWCquxU8YjV1KoxuiL+eJ5pg3ll+bC6flVv61L3LKZg=="],
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
@ -2372,6 +2391,8 @@
"open": ["open@10.2.0", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "wsl-utils": "^0.1.0" } }, "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA=="],
"openai": ["openai@4.104.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" }, "peerDependencies": { "ws": "^8.18.0", "zod": "^3.23.8" }, "optionalPeers": ["ws", "zod"], "bin": { "openai": "bin/cli" } }, "sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA=="],
"openapi-types": ["openapi-types@12.1.3", "", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="],
"os-paths": ["os-paths@7.4.0", "", { "optionalDependencies": { "fsevents": "*" } }, "sha512-Ux1J4NUqC6tZayBqLN1kUlDAEvLiQlli/53sSddU4IN+h+3xxnv2HmRSMpVSvr1hvJzotfMs3ERvETGK+f4OwA=="],
@ -2560,9 +2581,9 @@
"retry": ["retry@0.12.0", "", {}, "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="],
"rolldown": ["rolldown@1.0.0-beta.33", "", { "dependencies": { "@oxc-project/runtime": "=0.82.2", "@oxc-project/types": "=0.82.2", "@rolldown/pluginutils": "1.0.0-beta.33", "ansis": "^4.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-beta.33", "@rolldown/binding-darwin-arm64": "1.0.0-beta.33", "@rolldown/binding-darwin-x64": "1.0.0-beta.33", "@rolldown/binding-freebsd-x64": "1.0.0-beta.33", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-beta.33", "@rolldown/binding-linux-arm64-gnu": "1.0.0-beta.33", "@rolldown/binding-linux-arm64-musl": "1.0.0-beta.33", "@rolldown/binding-linux-x64-gnu": "1.0.0-beta.33", "@rolldown/binding-linux-x64-musl": "1.0.0-beta.33", "@rolldown/binding-openharmony-arm64": "1.0.0-beta.33", "@rolldown/binding-wasm32-wasi": "1.0.0-beta.33", "@rolldown/binding-win32-arm64-msvc": "1.0.0-beta.33", "@rolldown/binding-win32-ia32-msvc": "1.0.0-beta.33", "@rolldown/binding-win32-x64-msvc": "1.0.0-beta.33" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-mgu118ZuRguC8unhPCbdZbyRbjQfEMiWqlojBA5aRIncBelRaBomnHNpGKYkYWeK7twRz5Cql30xgqqrA3Xelw=="],
"rolldown": ["rolldown@1.0.0-beta.34", "", { "dependencies": { "@oxc-project/runtime": "=0.82.3", "@oxc-project/types": "=0.82.3", "@rolldown/pluginutils": "1.0.0-beta.34", "ansis": "^4.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-beta.34", "@rolldown/binding-darwin-arm64": "1.0.0-beta.34", "@rolldown/binding-darwin-x64": "1.0.0-beta.34", "@rolldown/binding-freebsd-x64": "1.0.0-beta.34", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-beta.34", "@rolldown/binding-linux-arm64-gnu": "1.0.0-beta.34", "@rolldown/binding-linux-arm64-musl": "1.0.0-beta.34", "@rolldown/binding-linux-x64-gnu": "1.0.0-beta.34", "@rolldown/binding-linux-x64-musl": "1.0.0-beta.34", "@rolldown/binding-openharmony-arm64": "1.0.0-beta.34", "@rolldown/binding-wasm32-wasi": "1.0.0-beta.34", "@rolldown/binding-win32-arm64-msvc": "1.0.0-beta.34", "@rolldown/binding-win32-ia32-msvc": "1.0.0-beta.34", "@rolldown/binding-win32-x64-msvc": "1.0.0-beta.34" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-Wwh7EwalMzzX3Yy3VN58VEajeR2Si8+HDNMf706jPLIqU7CxneRW+dQVfznf5O0TWTnJyu4npelwg2bzTXB1Nw=="],
"rolldown-plugin-dts": ["rolldown-plugin-dts@0.15.7", "", { "dependencies": { "@babel/generator": "^7.28.3", "@babel/parser": "^7.28.3", "@babel/types": "^7.28.2", "ast-kit": "^2.1.2", "birpc": "^2.5.0", "debug": "^4.4.1", "dts-resolver": "^2.1.1", "get-tsconfig": "^4.10.1" }, "peerDependencies": { "@typescript/native-preview": ">=7.0.0-dev.20250601.1", "rolldown": "^1.0.0-beta.9", "typescript": "^5.0.0", "vue-tsc": "~3.0.3" }, "optionalPeers": ["@typescript/native-preview", "typescript", "vue-tsc"] }, "sha512-BpdrnLaa+uyw0rPT47+4FUC7hQFazBFppeFT0ioW5Ybg0XCLeRohc3HHPlnCxI6LtzgSWT7Ot8ahn6ji10IQBg=="],
"rolldown-plugin-dts": ["rolldown-plugin-dts@0.15.10", "", { "dependencies": { "@babel/generator": "^7.28.3", "@babel/parser": "^7.28.3", "@babel/types": "^7.28.2", "ast-kit": "^2.1.2", "birpc": "^2.5.0", "debug": "^4.4.1", "dts-resolver": "^2.1.2", "get-tsconfig": "^4.10.1" }, "peerDependencies": { "@typescript/native-preview": ">=7.0.0-dev.20250601.1", "rolldown": "^1.0.0-beta.9", "typescript": "^5.0.0", "vue-tsc": "~3.0.3" }, "optionalPeers": ["@typescript/native-preview", "typescript", "vue-tsc"] }, "sha512-8cPVAVQUo9tYAoEpc3jFV9RxSil13hrRRg8cHC9gLXxRMNtWPc1LNMSDXzjyD+5Vny49sDZH77JlXp/vlc4I3g=="],
"rollup": ["rollup@4.48.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.48.0", "@rollup/rollup-android-arm64": "4.48.0", "@rollup/rollup-darwin-arm64": "4.48.0", "@rollup/rollup-darwin-x64": "4.48.0", "@rollup/rollup-freebsd-arm64": "4.48.0", "@rollup/rollup-freebsd-x64": "4.48.0", "@rollup/rollup-linux-arm-gnueabihf": "4.48.0", "@rollup/rollup-linux-arm-musleabihf": "4.48.0", "@rollup/rollup-linux-arm64-gnu": "4.48.0", "@rollup/rollup-linux-arm64-musl": "4.48.0", "@rollup/rollup-linux-loongarch64-gnu": "4.48.0", "@rollup/rollup-linux-ppc64-gnu": "4.48.0", "@rollup/rollup-linux-riscv64-gnu": "4.48.0", "@rollup/rollup-linux-riscv64-musl": "4.48.0", "@rollup/rollup-linux-s390x-gnu": "4.48.0", "@rollup/rollup-linux-x64-gnu": "4.48.0", "@rollup/rollup-linux-x64-musl": "4.48.0", "@rollup/rollup-win32-arm64-msvc": "4.48.0", "@rollup/rollup-win32-ia32-msvc": "4.48.0", "@rollup/rollup-win32-x64-msvc": "4.48.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-BXHRqK1vyt9XVSEHZ9y7xdYtuYbwVod2mLwOMFP7t/Eqoc1pHRlG/WdV2qNeNvZHRQdLedaFycljaYYM96RqJQ=="],
@ -2702,7 +2723,7 @@
"suffix-thumb": ["suffix-thumb@5.0.2", "", {}, "sha512-I5PWXAFKx3FYnI9a+dQMWNqTxoRt6vdBdb0O+BJ1sxXCWtSoQCusc13E58f+9p4MYx/qCnEMkD5jac6K2j3dgA=="],
"supermemory": ["supermemory@3.0.0-alpha.23", "", {}, "sha512-bMOfQnEu+m/Me1qDaF9gE9Jr01vZxhS2n9t9kqPYOs2SLQB7vsONG1Iq+dvx2vA+FJubn/KGyl1EkPd2o1b6rw=="],
"supermemory": ["supermemory@3.0.0-alpha.26", "", {}, "sha512-gjLAGEMZLgg1Et5rzCxuy8uuIcy1zjRkKZxsGpbMwxi4b4Z4kH6X/CR9J3QzhSpJRrj35+gP5DW6z8WIMgrCIQ=="],
"supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
@ -2762,7 +2783,7 @@
"ts-tqdm": ["ts-tqdm@0.8.6", "", {}, "sha512-3X3M1PZcHtgQbnwizL+xU8CAgbYbeLHrrDwL9xxcZZrV5J+e7loJm1XrXozHjSkl44J0Zg0SgA8rXbh83kCkcQ=="],
"tsdown": ["tsdown@0.14.1", "", { "dependencies": { "ansis": "^4.1.0", "cac": "^6.7.14", "chokidar": "^4.0.3", "debug": "^4.4.1", "diff": "^8.0.2", "empathic": "^2.0.0", "hookable": "^5.5.3", "rolldown": "latest", "rolldown-plugin-dts": "^0.15.6", "semver": "^7.7.2", "tinyexec": "^1.0.1", "tinyglobby": "^0.2.14", "tree-kill": "^1.2.2", "unconfig": "^7.3.2" }, "peerDependencies": { "@arethetypeswrong/core": "^0.18.1", "publint": "^0.3.0", "typescript": "^5.0.0", "unplugin-lightningcss": "^0.4.0", "unplugin-unused": "^0.5.0" }, "optionalPeers": ["@arethetypeswrong/core", "publint", "typescript", "unplugin-lightningcss", "unplugin-unused"], "bin": { "tsdown": "dist/run.mjs" } }, "sha512-/nBuFDKZeYln9hAxwWG5Cm55/823sNIVI693iVi0xRFHzf9OVUq4b/lx9PH1TErFr/IQ0kd2hutFbJIPM0XQWA=="],
"tsdown": ["tsdown@0.14.2", "", { "dependencies": { "ansis": "^4.1.0", "cac": "^6.7.14", "chokidar": "^4.0.3", "debug": "^4.4.1", "diff": "^8.0.2", "empathic": "^2.0.0", "hookable": "^5.5.3", "rolldown": "latest", "rolldown-plugin-dts": "^0.15.8", "semver": "^7.7.2", "tinyexec": "^1.0.1", "tinyglobby": "^0.2.14", "tree-kill": "^1.2.2", "unconfig": "^7.3.3" }, "peerDependencies": { "@arethetypeswrong/core": "^0.18.1", "publint": "^0.3.0", "typescript": "^5.0.0", "unplugin-lightningcss": "^0.4.0", "unplugin-unused": "^0.5.0" }, "optionalPeers": ["@arethetypeswrong/core", "publint", "typescript", "unplugin-lightningcss", "unplugin-unused"], "bin": { "tsdown": "dist/run.mjs" } }, "sha512-6ThtxVZoTlR5YJov5rYvH8N1+/S/rD/pGfehdCLGznGgbxz+73EASV1tsIIZkLw2n+SXcERqHhcB/OkyxdKv3A=="],
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
@ -3218,13 +3239,17 @@
"@sentry/nextjs/chalk": ["chalk@3.0.0", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg=="],
"@supermemory/ai-sdk/@ai-sdk/openai": ["@ai-sdk/openai@2.0.19", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-sG3/IVaPvV7Vn6513I1bcJILHpLCXbVif2ht6CyROcB9FzXCJe2K5uRbAg30HWsdCEe7xu4OAWtMK6yWTOcsSA=="],
"@supermemory/ai-sdk/@ai-sdk/openai": ["@ai-sdk/openai@2.0.22", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.7" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-qjSIPL5+LNM9flcBPeR64ZWeAZdYg4XWkAK34H3FaY61dSbuIaeqFPSzmQUrxotVcphAzgfL5tuYRqRYP2ZYyg=="],
"@supermemory/ai-sdk/ai": ["ai@5.0.22", "", { "dependencies": { "@ai-sdk/gateway": "1.0.11", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.5", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-RZiYhj7Ux7hrLtXkHPcxzdiSZt4NOiC69O5AkNfMCsz3twwz/KRkl9ASptosoOsg833s5yRcTSdIu5z53Sl6Pw=="],
"@supermemory/ai-sdk/ai": ["ai@5.0.26", "", { "dependencies": { "@ai-sdk/gateway": "1.0.15", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.7", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-bGNtG+nYQ2U+5mzuLbxIg9WxGQJ2u5jv2gYgP8C+CJ1YI4qqIjvjOgGEZWzvNet8jiOGIlqstsht9aQefKzmBw=="],
"@supermemory/ai-sdk/typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="],
"@supermemory/ai-sdk/zod": ["zod@4.1.0", "", {}, "sha512-UWxluYj2IDX9MHRXTMbB/2eeWrAMmmMSESM+MfT9MQw8U1qo9q5ASW08anoJh6AJ7pkt099fLdNFmfI+4aChHg=="],
"@supermemory/ai-sdk/zod": ["zod@4.1.4", "", {}, "sha512-2YqJuWkU6IIK9qcE4k1lLLhyZ6zFw7XVRdQGpV97jEIZwTrscUw+DY31Xczd8nwaoksyJUIxCojZXwckJovWxA=="],
"@supermemory/openai-sdk/typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="],
"@supermemory/openai-sdk/zod": ["zod@4.1.4", "", {}, "sha512-2YqJuWkU6IIK9qcE4k1lLLhyZ6zFw7XVRdQGpV97jEIZwTrscUw+DY31Xczd8nwaoksyJUIxCojZXwckJovWxA=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.4.5", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.4", "tslib": "^2.4.0" }, "bundled": true }, "sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q=="],
@ -3240,6 +3265,8 @@
"@tokenizer/inflate/fflate": ["fflate@0.8.2", "", {}, "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="],
"@types/bun/bun-types": ["bun-types@1.2.21", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-sa2Tj77Ijc/NTLS0/Odjq/qngmEPZfbfnOERi0KRUYhT9R8M4VBioWVmMWE5GrYbKMc+5lVybXygLdibHaqVqw=="],
"@vitejs/plugin-rsc/@mjackson/node-fetch-server": ["@mjackson/node-fetch-server@0.7.0", "", {}, "sha512-un8diyEBKU3BTVj3GzlTPA1kIjCkGdD+AMYQy31Gf9JCkfoZzwgJ79GUtHrF2BN3XPNMLpubbzPcxys+a3uZEw=="],
"@vitejs/plugin-rsc/estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="],
@ -3330,6 +3357,8 @@
"next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="],
"openai/@types/node": ["@types/node@18.19.123", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-K7DIaHnh0mzVxreCR9qwgNxp3MH9dltPNIEddW9MYUlcKAzm+3grKNSTe2vCJHI1FaLpvpL5JGJrz1UZDKYvDg=="],
"parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="],
"periscopic/is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="],
@ -3698,11 +3727,11 @@
"@sentry/nextjs/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
"@supermemory/ai-sdk/@ai-sdk/openai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.5", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.3", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-HliwB/yzufw3iwczbFVE2Fiwf1XqROB/I6ng8EKUsPM5+2wnIa8f4VbljZcDx+grhFrPV+PnRZH7zBqi8WZM7Q=="],
"@supermemory/ai-sdk/@ai-sdk/openai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.7", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-o3BS5/t8KnBL3ubP8k3w77AByOypLm+pkIL/DCw0qKkhDbvhCy+L3hRTGPikpdb8WHcylAeKsjgwOxhj4cqTUA=="],
"@supermemory/ai-sdk/ai/@ai-sdk/gateway": ["@ai-sdk/gateway@1.0.11", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-ErwWS3sPOuWy42eE3AVxlKkTa1XjjKBEtNCOylVKMO5KNyz5qie8QVlLYbULOG56dtxX4zTKX3rQNJudplhcmQ=="],
"@supermemory/ai-sdk/ai/@ai-sdk/gateway": ["@ai-sdk/gateway@1.0.15", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.7" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-xySXoQ29+KbGuGfmDnABx+O6vc7Gj7qugmj1kGpn0rW0rQNn6UKUuvscKMzWyv1Uv05GyC1vqHq8ZhEOLfXscQ=="],
"@supermemory/ai-sdk/ai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.5", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.3", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-HliwB/yzufw3iwczbFVE2Fiwf1XqROB/I6ng8EKUsPM5+2wnIa8f4VbljZcDx+grhFrPV+PnRZH7zBqi8WZM7Q=="],
"@supermemory/ai-sdk/ai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.7", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-o3BS5/t8KnBL3ubP8k3w77AByOypLm+pkIL/DCw0qKkhDbvhCy+L3hRTGPikpdb8WHcylAeKsjgwOxhj4cqTUA=="],
"alchemy/@cloudflare/unenv-preset/unenv": ["unenv@2.0.0-rc.19", "", { "dependencies": { "defu": "^6.1.4", "exsolve": "^1.0.7", "ohash": "^2.0.11", "pathe": "^2.0.3", "ufo": "^1.6.1" } }, "sha512-t/OMHBNAkknVCI7bVB9OWjUUAwhVv9vsPIAGnNUxnu3FxPQN11rjh0sksLMzc3g7IlTgvHmOTl4JM7JHpcv5wA=="],
@ -3780,6 +3809,8 @@
"next/postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
"openai/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="],
"send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
"string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],

View file

@ -1,6 +1,7 @@
{
"name": "@supermemory/ai-sdk",
"version": "1.0.4",
"type": "module",
"version": "1.0.5",
"scripts": {
"build": "tsdown",
"dev": "tsdown --watch",
@ -9,16 +10,16 @@
"test:watch": "vitest --watch"
},
"dependencies": {
"@ai-sdk/openai": "^2.0.15",
"@ai-sdk/openai": "^2.0.22",
"@ai-sdk/provider": "^2.0.0",
"ai": "^5.0.17",
"supermemory": "^3.0.0-alpha.22",
"zod": "^4.0.17"
"ai": "^5.0.26",
"supermemory": "^3.0.0-alpha.26",
"zod": "^4.1.4"
},
"devDependencies": {
"@total-typescript/tsconfig": "^1.0.4",
"@types/bun": "^1.2.20",
"tsdown": "^0.14.1",
"@types/bun": "^1.2.21",
"tsdown": "^0.14.2",
"typescript": "^5.9.2",
"vitest": "^3.2.4"
},
@ -32,5 +33,11 @@
"repository": {
"url": "https://github.com/supermemoryai/supermemory",
"directory": "packages/ai-sdk"
}
},
"keywords": [
"ai",
"sdk",
"supermemory",
"memory"
]
}

View file

@ -39,7 +39,6 @@ describe("supermemoryTools", () => {
expect(tools).toBeDefined()
expect(tools.searchMemories).toBeDefined()
expect(tools.addMemory).toBeDefined()
expect(tools.fetchMemory).toBeDefined()
})
it("should create tools with custom baseUrl", () => {
@ -51,7 +50,6 @@ describe("supermemoryTools", () => {
expect(tools).toBeDefined()
expect(tools.searchMemories).toBeDefined()
expect(tools.addMemory).toBeDefined()
expect(tools.fetchMemory).toBeDefined()
})
it("should create tools with projectId configuration", () => {
@ -63,7 +61,6 @@ describe("supermemoryTools", () => {
expect(tools).toBeDefined()
expect(tools.searchMemories).toBeDefined()
expect(tools.addMemory).toBeDefined()
expect(tools.fetchMemory).toBeDefined()
})
it("should create tools with custom container tags", () => {
@ -75,7 +72,6 @@ describe("supermemoryTools", () => {
expect(tools).toBeDefined()
expect(tools.searchMemories).toBeDefined()
expect(tools.addMemory).toBeDefined()
expect(tools.fetchMemory).toBeDefined()
})
})

View file

@ -21,7 +21,7 @@ export function supermemoryTools(
) {
const client = new Supermemory({
apiKey,
...(config?.baseUrl && { baseURL: config.baseUrl }),
...(config?.baseUrl ? { baseURL: config.baseUrl } : {}),
})
const containerTags = config?.projectId
@ -30,7 +30,7 @@ export function supermemoryTools(
const searchMemories = tool({
description:
"Search user memories and patterns. Run when explicitly asked or when context about user's past choices would be helpful. Uses semantic matching to find relevant details across related experiences.",
"Search (recall) memories/details/information about the user or other facts or entities. Run when explicitly asked or when context about user's past choices would be helpful.",
inputSchema: z.object({
informationToGet: z
.string()
@ -48,7 +48,11 @@ export function supermemoryTools(
.default(10)
.describe("Maximum number of results to return"),
}),
execute: async ({ informationToGet, includeFullDocs = true, limit = 10 }) => {
execute: async ({
informationToGet,
includeFullDocs = true,
limit = 10,
}) => {
try {
const response = await client.search.execute({
q: informationToGet,
@ -74,7 +78,7 @@ export function supermemoryTools(
const addMemory = tool({
description:
"Add a new memory to the user's memories. Run when explicitly asked or when the user mentions any information generalizable beyond the context of the current conversation.",
"Add (remember) memories/details/information about the user or other facts or entities. Run when explicitly asked or when the user mentions any information generalizable beyond the context of the current conversation.",
inputSchema: z.object({
memory: z
.string()
@ -105,32 +109,9 @@ export function supermemoryTools(
},
})
const fetchMemory = tool({
description: "Fetch a specific memory by ID to get its full details",
inputSchema: z.object({
memoryId: z.string().describe("The ID of the memory to fetch"),
}),
execute: async ({ memoryId }) => {
try {
const response = await client.memories.get(memoryId)
return {
success: true,
memory: response,
}
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error",
}
}
},
})
return {
searchMemories,
addMemory,
fetchMemory,
}
}
@ -150,11 +131,3 @@ export const addMemoryTool = (
const { addMemory } = supermemoryTools(apiKey, config)
return addMemory
}
export const fetchMemoryTool = (
apiKey: string,
config?: SupermemoryToolsConfig,
) => {
const { fetchMemory } = supermemoryTools(apiKey, config)
return fetchMemory
}

View file

@ -0,0 +1,318 @@
# Supermemory OpenAI Python SDK
Enhanced OpenAI Python SDK with Supermemory infinite context integration.
This package extends the official [OpenAI Python SDK](https://github.com/openai/openai-python) with [Supermemory](https://supermemory.ai) capabilities, enabling infinite context chat completions and memory management tools.
## Features
- 🚀 **Infinite Context**: Chat completions with unlimited conversation history
- 🧠 **Memory Tools**: Search, add, and fetch user memories seamlessly
- 🔌 **Multiple Providers**: Support for OpenAI, Anthropic, Groq, and more
- 🛠 **Function Calling**: Built-in memory tools for OpenAI function calling
- 🔒 **Type Safe**: Full TypeScript-style type hints for Python
- ⚡ **Async Support**: Full async/await support
## Installation
Install using uv (recommended):
```bash
uv add supermemory-openai
```
Or with pip:
```bash
pip install supermemory-openai
```
## Quick Start
### Basic Chat Completion
```python
import asyncio
from supermemory_openai import SupermemoryOpenAI, SupermemoryInfiniteChatConfigWithProviderName
async def main():
# Initialize client
client = SupermemoryOpenAI(
supermemory_api_key="your-supermemory-api-key",
config=SupermemoryInfiniteChatConfigWithProviderName(
provider_name="openai",
provider_api_key="your-openai-api-key",
)
)
# Create chat completion
response = await client.chat_completion(
messages=[
{"role": "user", "content": "Hello, how are you?"}
],
model="gpt-4o"
)
print(response.choices[0].message.content)
asyncio.run(main())
```
### Using Memory Tools
```python
import asyncio
from supermemory_openai import SupermemoryOpenAI, SupermemoryTools, SupermemoryInfiniteChatConfigWithProviderName
async def main():
# Initialize client and tools
client = SupermemoryOpenAI(
supermemory_api_key="your-supermemory-api-key",
config=SupermemoryInfiniteChatConfigWithProviderName(
provider_name="openai",
provider_api_key="your-openai-api-key",
)
)
tools = SupermemoryTools(
api_key="your-supermemory-api-key",
config={"project_id": "my-project"}
)
# Chat with memory tools
response = await client.chat_completion(
messages=[
{
"role": "system",
"content": "You are a helpful assistant with access to user memories."
},
{
"role": "user",
"content": "Remember that I prefer tea over coffee"
}
],
tools=tools.get_tool_definitions(),
model="gpt-4o"
)
print(response.choices[0].message.content)
asyncio.run(main())
```
## Configuration
### Provider Configuration
#### Using Provider Names
```python
from supermemory_openai import SupermemoryInfiniteChatConfigWithProviderName
config = SupermemoryInfiniteChatConfigWithProviderName(
provider_name="openai", # or "anthropic", "groq", "openrouter", etc.
provider_api_key="your-provider-api-key",
headers={"custom-header": "value"} # optional
)
```
#### Using Custom URLs
```python
from supermemory_openai import SupermemoryInfiniteChatConfigWithProviderUrl
config = SupermemoryInfiniteChatConfigWithProviderUrl(
provider_url="https://your-custom-endpoint.com/v1",
provider_api_key="your-provider-api-key",
headers={"custom-header": "value"} # optional
)
```
### Supported Providers
- `openai` - OpenAI API
- `anthropic` - Anthropic Claude
- `openrouter` - OpenRouter
- `deepinfra` - DeepInfra
- `groq` - Groq
- `google` - Google AI
- `cloudflare` - Cloudflare Workers AI
## Memory Tools
### SupermemoryTools Class
```python
from supermemory_openai import SupermemoryTools
tools = SupermemoryTools(
api_key="your-supermemory-api-key",
config={
"project_id": "my-project", # or use container_tags
"base_url": "https://custom-endpoint.com", # optional
}
)
# Search memories
result = await tools.search_memories(
information_to_get="user preferences",
limit=10,
include_full_docs=True
)
# Add memory
result = await tools.add_memory(
memory="User prefers tea over coffee"
)
# Fetch specific memory
result = await tools.fetch_memory(
memory_id="memory-id-here"
)
```
### Individual Tools
```python
from supermemory_openai import (
create_search_memories_tool,
create_add_memory_tool,
create_fetch_memory_tool
)
search_tool = create_search_memories_tool("your-api-key")
add_tool = create_add_memory_tool("your-api-key")
fetch_tool = create_fetch_memory_tool("your-api-key")
```
### Function Calling Integration
```python
from supermemory_openai import execute_memory_tool_calls
# After getting tool calls from OpenAI
if response.choices[0].message.tool_calls:
tool_results = await execute_memory_tool_calls(
api_key="your-supermemory-api-key",
tool_calls=response.choices[0].message.tool_calls,
config={"project_id": "my-project"}
)
# Add tool results to conversation
messages.append(response.choices[0].message)
messages.extend(tool_results)
```
## API Reference
### SupermemoryOpenAI
Enhanced OpenAI client with infinite context support.
#### Constructor
```python
SupermemoryOpenAI(
supermemory_api_key: str,
config: Optional[SupermemoryInfiniteChatConfig] = None
)
```
#### Methods
- `chat_completion()` - Create chat completion with simplified interface
- `create_chat_completion()` - Create chat completion with full OpenAI parameters
### SupermemoryTools
Memory management tools for function calling.
#### Constructor
```python
SupermemoryTools(
api_key: str,
config: Optional[SupermemoryToolsConfig] = None
)
```
#### Methods
- `get_tool_definitions()` - Get OpenAI function definitions
- `search_memories()` - Search user memories
- `add_memory()` - Add new memory
- `fetch_memory()` - Fetch specific memory by ID
- `execute_tool_call()` - Execute individual tool call
## Error Handling
```python
try:
response = await client.chat_completion(
messages=[{"role": "user", "content": "Hello"}],
model="gpt-4o"
)
except Exception as e:
print(f"Error: {e}")
```
## Environment Variables
Set these environment variables for testing:
- `SUPERMEMORY_API_KEY` - Your Supermemory API key
- `PROVIDER_API_KEY` - Your AI provider API key
- `PROVIDER_NAME` - Provider name (default: "openai")
- `PROVIDER_URL` - Custom provider URL (optional)
- `MODEL_NAME` - Model to use (default: "gpt-4o-mini")
- `SUPERMEMORY_BASE_URL` - Custom Supermemory base URL (optional)
## Development
### Setup
```bash
# Install uv
curl -LsSf https://astral.sh/uv/install.sh | sh
# Clone and setup
git clone <repository-url>
cd packages/openai-sdk-python
uv sync --dev
```
### Testing
```bash
# Run tests
uv run pytest
# Run with coverage
uv run pytest --cov=supermemory_openai
# Run specific test file
uv run pytest tests/test_infinite_chat.py
```
### Type Checking
```bash
uv run mypy src/supermemory_openai
```
### Formatting
```bash
uv run black src/ tests/
uv run isort src/ tests/
```
## License
MIT License - see LICENSE file for details.
## Links
- [Supermemory](https://supermemory.ai) - Infinite context memory platform
- [OpenAI Python SDK](https://github.com/openai/openai-python) - Official OpenAI Python library
- [Documentation](https://docs.supermemory.ai) - Full API documentation

View file

@ -0,0 +1,96 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "supermemory-openai-sdk"
version = "1.0.0"
description = "OpenAI SDK utilities for supermemory"
readme = "README.md"
license = { text = "MIT" }
keywords = ["openai", "supermemory", "ai", "memory"]
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Scientific/Engineering :: Artificial Intelligence",
]
requires-python = ">=3.8.1"
dependencies = [
"openai>=1.102.0",
"supermemory>=3.0.0a28",
"typing-extensions>=4.0.0",
]
[dependency-groups]
dev = [
"black>=24.8.0",
"flake8>=7.1.2",
"isort>=5.13.2",
"mypy>=1.14.1",
"pytest>=8.3.5",
"pytest-asyncio>=0.24.0",
"python-dotenv>=1.0.1",
]
[project.urls]
Homepage = "https://github.com/supermemoryai/supermemory"
Repository = "https://github.com/supermemoryai/supermemory"
Documentation = "https://docs.supermemory.ai"
[tool.hatch.build.targets.wheel]
packages = ["src"]
[tool.black]
line-length = 88
target-version = ['py38']
include = '\.pyi?$'
extend-exclude = '''
/(
# directories
\.eggs
| \.git
| \.hg
| \.mypy_cache
| \.tox
| \.venv
| build
| dist
)/
'''
[tool.isort]
profile = "black"
multi_line_output = 3
line_length = 88
[tool.mypy]
python_version = "3.8"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
check_untyped_defs = true
disallow_untyped_decorators = true
no_implicit_optional = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_no_return = true
warn_unreachable = true
strict_equality = true
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = "-v --tb=short"
asyncio_mode = "auto"

View file

@ -0,0 +1,54 @@
"""Supermemory OpenAI SDK - Enhanced OpenAI Python SDK with infinite context."""
from .infinite_chat import (
SupermemoryOpenAI,
SupermemoryInfiniteChatConfig,
SupermemoryInfiniteChatConfigWithProviderName,
SupermemoryInfiniteChatConfigWithProviderUrl,
ProviderName,
PROVIDER_MAP,
create_supermemory_openai,
)
from .tools import (
SupermemoryTools,
SupermemoryToolsConfig,
MemoryObject,
MemorySearchResult,
MemoryAddResult,
SearchMemoriesTool,
AddMemoryTool,
MEMORY_TOOL_SCHEMAS,
create_supermemory_tools,
get_memory_tool_definitions,
execute_memory_tool_calls,
create_search_memories_tool,
create_add_memory_tool,
)
__version__ = "0.1.0"
__all__ = [
# Infinite Chat
"SupermemoryOpenAI",
"SupermemoryInfiniteChatConfig",
"SupermemoryInfiniteChatConfigWithProviderName",
"SupermemoryInfiniteChatConfigWithProviderUrl",
"ProviderName",
"PROVIDER_MAP",
"create_supermemory_openai",
# Tools
"SupermemoryTools",
"SupermemoryToolsConfig",
"MemoryObject",
"MemorySearchResult",
"MemoryAddResult",
"SearchMemoriesTool",
"AddMemoryTool",
"MEMORY_TOOL_SCHEMAS",
"create_supermemory_tools",
"get_memory_tool_definitions",
"execute_memory_tool_calls",
"create_search_memories_tool",
"create_add_memory_tool",
]

View file

@ -0,0 +1,268 @@
"""Enhanced OpenAI client with Supermemory infinite context integration."""
from typing import Dict, List, Optional, Union, overload, Unpack
from typing_extensions import Literal
from openai import OpenAI, AsyncStream
from openai.types.chat import (
ChatCompletion,
ChatCompletionMessageParam,
ChatCompletionToolParam,
ChatCompletionToolChoiceOptionParam,
CompletionCreateParams,
)
# Provider URL mapping
PROVIDER_MAP = {
"openai": "https://api.openai.com/v1",
"anthropic": "https://api.anthropic.com/v1",
"openrouter": "https://openrouter.ai/api/v1",
"deepinfra": "https://api.deepinfra.com/v1/openai",
"groq": "https://api.groq.com/openai/v1",
"google": "https://generativelanguage.googleapis.com/v1beta/openai",
"cloudflare": "https://gateway.ai.cloudflare.com/v1/*/unlimited-context/openai",
}
ProviderName = Literal[
"openai", "anthropic", "openrouter", "deepinfra", "groq", "google", "cloudflare"
]
class SupermemoryInfiniteChatConfigBase:
"""Base configuration for Supermemory infinite chat."""
def __init__(
self,
provider_api_key: str,
headers: Optional[Dict[str, str]] = None,
):
self.provider_api_key = provider_api_key
self.headers = headers or {}
class SupermemoryInfiniteChatConfigWithProviderName(SupermemoryInfiniteChatConfigBase):
"""Configuration using a predefined provider name."""
def __init__(
self,
provider_name: ProviderName,
provider_api_key: str,
headers: Optional[Dict[str, str]] = None,
):
super().__init__(provider_api_key, headers)
self.provider_name = provider_name
self.provider_url: None = None
class SupermemoryInfiniteChatConfigWithProviderUrl(SupermemoryInfiniteChatConfigBase):
"""Configuration using a custom provider URL."""
def __init__(
self,
provider_url: str,
provider_api_key: str,
headers: Optional[Dict[str, str]] = None,
):
super().__init__(provider_api_key, headers)
self.provider_url = provider_url
self.provider_name: None = None
SupermemoryInfiniteChatConfig = Union[
SupermemoryInfiniteChatConfigWithProviderName,
SupermemoryInfiniteChatConfigWithProviderUrl,
]
class SupermemoryOpenAI(OpenAI):
"""Enhanced OpenAI client with supermemory integration.
Only chat completions are supported - all other OpenAI API endpoints are disabled.
"""
def __init__(
self,
supermemory_api_key: str,
config: Optional[SupermemoryInfiniteChatConfig] = None,
):
"""Initialize the SupermemoryOpenAI client.
Args:
supermemory_api_key: API key for Supermemory service
config: Configuration for the AI provider
"""
# Determine base URL
if config is None:
base_url = "https://api.openai.com/v1"
api_key = None
headers = {}
elif hasattr(config, "provider_name") and config.provider_name:
base_url = PROVIDER_MAP[config.provider_name]
api_key = config.provider_api_key
headers = config.headers
else:
base_url = config.provider_url
api_key = config.provider_api_key
headers = config.headers
# Prepare default headers
default_headers = {
"x-supermemory-api-key": supermemory_api_key,
**headers,
}
# Initialize the parent OpenAI client
super().__init__(
api_key=api_key,
base_url=base_url,
default_headers=default_headers,
)
self._supermemory_api_key = supermemory_api_key
# Disable unsupported endpoints
self._disable_unsupported_endpoints()
def _disable_unsupported_endpoints(self) -> None:
"""Disable all OpenAI endpoints except chat completions."""
def unsupported_error() -> None:
raise RuntimeError(
"Supermemory only supports chat completions. "
"Use chat_completion() or chat.completions.create() instead."
)
# List of endpoints to disable
endpoints = [
"embeddings",
"fine_tuning",
"images",
"audio",
"models",
"moderations",
"files",
"batches",
"uploads",
"beta",
]
# Override endpoints with error function
for endpoint in endpoints:
setattr(self, endpoint, property(lambda self: unsupported_error()))
async def create_chat_completion(
self,
**params: Unpack[CompletionCreateParams],
) -> ChatCompletion:
"""Create chat completions with infinite context support.
Args:
**params: Parameters for chat completion
Returns:
ChatCompletion response
"""
return await self.chat.completions.create(**params)
@overload
async def chat_completion(
self,
messages: List[ChatCompletionMessageParam],
*,
model: Optional[str] = None,
temperature: Optional[float] = None,
max_tokens: Optional[int] = None,
tools: Optional[List[ChatCompletionToolParam]] = None,
tool_choice: Optional[ChatCompletionToolChoiceOptionParam] = None,
stream: Literal[False] = False,
**kwargs: Unpack[CompletionCreateParams],
) -> ChatCompletion: ...
@overload
async def chat_completion(
self,
messages: List[ChatCompletionMessageParam],
*,
model: Optional[str] = None,
temperature: Optional[float] = None,
max_tokens: Optional[int] = None,
tools: Optional[List[ChatCompletionToolParam]] = None,
tool_choice: Optional[ChatCompletionToolChoiceOptionParam] = None,
stream: Literal[True],
**kwargs: Unpack[CompletionCreateParams],
) -> AsyncStream[ChatCompletion]: ...
async def chat_completion(
self,
messages: List[ChatCompletionMessageParam],
*,
model: Optional[str] = None,
temperature: Optional[float] = None,
max_tokens: Optional[int] = None,
tools: Optional[List[ChatCompletionToolParam]] = None,
tool_choice: Optional[ChatCompletionToolChoiceOptionParam] = None,
stream: bool = False,
**kwargs: Unpack[CompletionCreateParams],
) -> Union[ChatCompletion, AsyncStream[ChatCompletion]]:
"""Create chat completions with simplified interface.
Args:
messages: List of chat messages
model: Model to use (defaults to gpt-4o)
temperature: Sampling temperature
max_tokens: Maximum tokens to generate
tools: Available tools for function calling
tool_choice: Tool choice strategy
stream: Whether to stream the response
**kwargs: Additional parameters
Returns:
ChatCompletion response or stream
"""
params: Dict[
str,
Union[
str,
List[ChatCompletionMessageParam],
List[ChatCompletionToolParam],
ChatCompletionToolChoiceOptionParam,
bool,
float,
int,
],
] = {
"model": model or "gpt-4o",
"messages": messages,
**kwargs,
}
# Add optional parameters if provided
if temperature is not None:
params["temperature"] = temperature
if max_tokens is not None:
params["max_tokens"] = max_tokens
if tools is not None:
params["tools"] = tools
if tool_choice is not None:
params["tool_choice"] = tool_choice
if stream is not None:
params["stream"] = stream
return await self.chat.completions.create(**params)
def create_supermemory_openai(
supermemory_api_key: str,
config: Optional[SupermemoryInfiniteChatConfig] = None,
) -> SupermemoryOpenAI:
"""Helper function to create a SupermemoryOpenAI instance.
Args:
supermemory_api_key: API key for Supermemory service
config: Configuration for the AI provider
Returns:
SupermemoryOpenAI instance
"""
return SupermemoryOpenAI(supermemory_api_key, config)

View file

@ -0,0 +1,366 @@
"""Supermemory tools for OpenAI function calling."""
import json
from typing import Dict, List, Optional, Union, TypedDict
from openai.types.chat import (
ChatCompletionMessageToolCall,
ChatCompletionToolMessageParam,
ChatCompletionToolParam,
)
import supermemory
from supermemory.types import (
MemoryAddResponse,
MemoryGetResponse,
SearchExecuteResponse,
)
from supermemory.types.search_execute_response import Result
class SupermemoryToolsConfig(TypedDict, total=False):
"""Configuration for Supermemory tools.
Only one of `project_id` or `container_tags` can be provided.
"""
base_url: Optional[str]
container_tags: Optional[List[str]]
project_id: Optional[str]
# Type aliases using inferred types from supermemory package
MemoryObject = Union[MemoryGetResponse, MemoryAddResponse]
class MemorySearchResult(TypedDict, total=False):
"""Result type for memory search operations."""
success: bool
results: Optional[List[Result]]
count: Optional[int]
error: Optional[str]
class MemoryAddResult(TypedDict, total=False):
"""Result type for memory add operations."""
success: bool
memory: Optional[MemoryAddResponse]
error: Optional[str]
# Function schemas for OpenAI function calling
MEMORY_TOOL_SCHEMAS = {
"search_memories": {
"name": "search_memories",
"description": (
"Search (recall) memories/details/information about the user or other facts or entities. Run when explicitly asked or when context about user's past choices would be helpful."
),
"parameters": {
"type": "object",
"properties": {
"information_to_get": {
"type": "string",
"description": "Terms to search for in the user's memories",
},
"include_full_docs": {
"type": "boolean",
"description": (
"Whether to include the full document content in the response. "
"Defaults to true for better AI context."
),
"default": True,
},
"limit": {
"type": "number",
"description": "Maximum number of results to return",
"default": 10,
},
},
"required": ["information_to_get"],
},
},
"add_memory": {
"name": "add_memory",
"description": (
"Add (remember) memories/details/information about the user or other facts or entities. Run when explicitly asked or when the user mentions any information generalizable beyond the context of the current conversation."
),
"parameters": {
"type": "object",
"properties": {
"memory": {
"type": "string",
"description": (
"The text content of the memory to add. This should be a "
"single sentence or a short paragraph."
),
},
},
"required": ["memory"],
},
},
}
class SupermemoryTools:
"""Create memory tool handlers for OpenAI function calling."""
def __init__(self, api_key: str, config: Optional[SupermemoryToolsConfig] = None):
"""Initialize SupermemoryTools.
Args:
api_key: Supermemory API key
config: Optional configuration
"""
config = config or {}
# Initialize Supermemory client
client_kwargs = {"api_key": api_key}
if config.get("base_url"):
client_kwargs["base_url"] = config["base_url"]
self.client = supermemory.Supermemory(**client_kwargs)
# Set container tags
if config.get("project_id"):
self.container_tags = [f"sm_project_{config['project_id']}"]
elif config.get("container_tags"):
self.container_tags = config["container_tags"]
else:
self.container_tags = ["sm_project_default"]
def get_tool_definitions(self) -> List[ChatCompletionToolParam]:
"""Get OpenAI function definitions for all memory tools.
Returns:
List of ChatCompletionToolParam definitions
"""
return [
{"type": "function", "function": MEMORY_TOOL_SCHEMAS["search_memories"]},
{"type": "function", "function": MEMORY_TOOL_SCHEMAS["add_memory"]},
]
async def execute_tool_call(self, tool_call: ChatCompletionMessageToolCall) -> str:
"""Execute a tool call based on the function name and arguments.
Args:
tool_call: The tool call from OpenAI
Returns:
JSON string result
"""
function_name = tool_call.function.name
args = json.loads(tool_call.function.arguments)
if function_name == "search_memories":
result = await self.search_memories(**args)
elif function_name == "add_memory":
result = await self.add_memory(**args)
else:
result = {
"success": False,
"error": f"Unknown function: {function_name}",
}
return json.dumps(result)
async def search_memories(
self,
information_to_get: str,
include_full_docs: bool = True,
limit: int = 10,
) -> MemorySearchResult:
"""Search memories.
Args:
information_to_get: Terms to search for
include_full_docs: Whether to include full document content
limit: Maximum number of results
Returns:
MemorySearchResult
"""
try:
response: SearchExecuteResponse = await self.client.search.execute(
q=information_to_get,
container_tags=self.container_tags,
limit=limit,
chunk_threshold=0.6,
include_full_docs=include_full_docs,
)
return MemorySearchResult(
success=True,
results=response.results,
count=len(response.results),
)
except Exception as error:
return MemorySearchResult(
success=False,
error=str(error),
)
async def add_memory(self, memory: str) -> MemoryAddResult:
"""Add a memory.
Args:
memory: The memory content to add
Returns:
MemoryAddResult
"""
try:
metadata: Dict[str, object] = {}
add_params = {
"content": memory,
"container_tags": self.container_tags,
}
if metadata:
add_params["metadata"] = metadata
response: MemoryAddResponse = await self.client.memories.add(**add_params)
return MemoryAddResult(
success=True,
memory=response,
)
except Exception as error:
return MemoryAddResult(
success=False,
error=str(error),
)
def create_supermemory_tools(
api_key: str, config: Optional[SupermemoryToolsConfig] = None
) -> SupermemoryTools:
"""Helper function to create SupermemoryTools instance.
Args:
api_key: Supermemory API key
config: Optional configuration
Returns:
SupermemoryTools instance
"""
return SupermemoryTools(api_key, config)
def get_memory_tool_definitions() -> List[ChatCompletionToolParam]:
"""Get OpenAI function definitions for memory tools.
Returns:
List of ChatCompletionToolParam definitions
"""
return [
{"type": "function", "function": MEMORY_TOOL_SCHEMAS["search_memories"]},
{"type": "function", "function": MEMORY_TOOL_SCHEMAS["add_memory"]},
]
async def execute_memory_tool_calls(
api_key: str,
tool_calls: List[ChatCompletionMessageToolCall],
config: Optional[SupermemoryToolsConfig] = None,
) -> List[ChatCompletionToolMessageParam]:
"""Execute tool calls from OpenAI function calling.
Args:
api_key: Supermemory API key
tool_calls: List of tool calls from OpenAI
config: Optional configuration
Returns:
List of tool message parameters
"""
tools = SupermemoryTools(api_key, config)
async def execute_single_call(
tool_call: ChatCompletionMessageToolCall,
) -> ChatCompletionToolMessageParam:
result = await tools.execute_tool_call(tool_call)
return ChatCompletionToolMessageParam(
tool_call_id=tool_call.id,
role="tool",
content=result,
)
# Execute all tool calls concurrently
import asyncio
results = await asyncio.gather(
*[execute_single_call(tool_call) for tool_call in tool_calls]
)
return results
# Individual tool creators for more granular control
class SearchMemoriesTool:
"""Individual search memories tool."""
def __init__(self, api_key: str, config: Optional[SupermemoryToolsConfig] = None):
self.tools = SupermemoryTools(api_key, config)
self.definition: ChatCompletionToolParam = {
"type": "function",
"function": MEMORY_TOOL_SCHEMAS["search_memories"],
}
async def execute(
self,
information_to_get: str,
include_full_docs: bool = True,
limit: int = 10,
) -> MemorySearchResult:
"""Execute search memories."""
return await self.tools.search_memories(
information_to_get=information_to_get,
include_full_docs=include_full_docs,
limit=limit,
)
class AddMemoryTool:
"""Individual add memory tool."""
def __init__(self, api_key: str, config: Optional[SupermemoryToolsConfig] = None):
self.tools = SupermemoryTools(api_key, config)
self.definition: ChatCompletionToolParam = {
"type": "function",
"function": MEMORY_TOOL_SCHEMAS["add_memory"],
}
async def execute(self, memory: str) -> MemoryAddResult:
"""Execute add memory."""
return await self.tools.add_memory(memory=memory)
def create_search_memories_tool(
api_key: str, config: Optional[SupermemoryToolsConfig] = None
) -> SearchMemoriesTool:
"""Create individual search memories tool.
Args:
api_key: Supermemory API key
config: Optional configuration
Returns:
SearchMemoriesTool instance
"""
return SearchMemoriesTool(api_key, config)
def create_add_memory_tool(
api_key: str, config: Optional[SupermemoryToolsConfig] = None
) -> AddMemoryTool:
"""Create individual add memory tool.
Args:
api_key: Supermemory API key
config: Optional configuration
Returns:
AddMemoryTool instance
"""
return AddMemoryTool(api_key, config)

View file

@ -0,0 +1 @@
# Test package

View file

@ -0,0 +1,387 @@
"""Tests for infinite_chat module."""
import os
import pytest
from typing import List
from openai.types.chat import ChatCompletionMessageParam
from ..src import (
SupermemoryOpenAI,
SupermemoryInfiniteChatConfigWithProviderName,
SupermemoryInfiniteChatConfigWithProviderUrl,
ProviderName,
)
# Test configuration
PROVIDERS: List[ProviderName] = [
"openai",
"anthropic",
"openrouter",
"deepinfra",
"groq",
"google",
"cloudflare",
]
@pytest.fixture
def test_api_key() -> str:
"""Get test Supermemory API key from environment."""
api_key = os.getenv("SUPERMEMORY_API_KEY")
if not api_key:
pytest.skip("SUPERMEMORY_API_KEY environment variable is required for tests")
return api_key
@pytest.fixture
def test_provider_api_key() -> str:
"""Get test provider API key from environment."""
api_key = os.getenv("PROVIDER_API_KEY")
if not api_key:
pytest.skip("PROVIDER_API_KEY environment variable is required for tests")
return api_key
@pytest.fixture
def test_provider_name() -> ProviderName:
"""Get test provider name from environment."""
provider_name = os.getenv("PROVIDER_NAME", "openai")
if provider_name not in PROVIDERS:
pytest.fail(f"Invalid provider name: {provider_name}")
return provider_name # type: ignore
@pytest.fixture
def test_provider_url() -> str:
"""Get test provider URL from environment."""
return os.getenv("PROVIDER_URL", "")
@pytest.fixture
def test_model_name() -> str:
"""Get test model name from environment."""
return os.getenv("MODEL_NAME", "gpt-4o-mini")
@pytest.fixture
def test_headers() -> dict:
"""Get test headers."""
return {"custom-header": "test-value"}
@pytest.fixture
def test_messages() -> List[List[ChatCompletionMessageParam]]:
"""Test message sets."""
return [
[{"role": "user", "content": "Hello!"}],
[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "What is AI?"},
],
[
{"role": "user", "content": "Tell me a joke"},
{
"role": "assistant",
"content": "Why don't scientists trust atoms? Because they make up everything!",
},
{"role": "user", "content": "Tell me another one"},
],
]
class TestClientCreation:
"""Test client creation."""
def test_create_client_with_provider_name(
self,
test_api_key: str,
test_provider_api_key: str,
test_provider_name: ProviderName,
test_headers: dict,
):
"""Test creating client with provider name configuration."""
config = SupermemoryInfiniteChatConfigWithProviderName(
provider_name=test_provider_name,
provider_api_key=test_provider_api_key,
headers=test_headers,
)
client = SupermemoryOpenAI(test_api_key, config)
assert client is not None
assert client.chat is not None
def test_create_client_with_openai_provider(
self, test_api_key: str, test_provider_api_key: str, test_headers: dict
):
"""Test creating client with OpenAI provider configuration."""
config = SupermemoryInfiniteChatConfigWithProviderName(
provider_name="openai",
provider_api_key=test_provider_api_key,
headers=test_headers,
)
client = SupermemoryOpenAI(test_api_key, config)
assert client is not None
def test_create_client_with_custom_provider_url(
self, test_api_key: str, test_provider_api_key: str, test_headers: dict
):
"""Test creating client with custom provider URL."""
custom_url = "https://custom-provider.com/v1"
config = SupermemoryInfiniteChatConfigWithProviderUrl(
provider_url=custom_url,
provider_api_key=test_provider_api_key,
headers=test_headers,
)
client = SupermemoryOpenAI(test_api_key, config)
assert client is not None
class TestChatCompletions:
"""Test chat completions functionality."""
@pytest.mark.asyncio
async def test_create_chat_completion_simple_message(
self,
test_api_key: str,
test_provider_api_key: str,
test_provider_name: ProviderName,
test_model_name: str,
test_messages: List[List[ChatCompletionMessageParam]],
):
"""Test creating chat completion with simple message."""
config = SupermemoryInfiniteChatConfigWithProviderName(
provider_name=test_provider_name,
provider_api_key=test_provider_api_key,
headers={},
)
client = SupermemoryOpenAI(test_api_key, config)
result = await client.create_chat_completion(
model=test_model_name,
messages=test_messages[0], # "Hello!"
)
assert result is not None
assert hasattr(result, "choices")
assert len(result.choices) > 0
assert result.choices[0].message.content is not None
@pytest.mark.asyncio
async def test_chat_completion_convenience_method(
self,
test_api_key: str,
test_provider_api_key: str,
test_provider_name: ProviderName,
test_model_name: str,
test_messages: List[List[ChatCompletionMessageParam]],
):
"""Test chat completion using convenience method."""
config = SupermemoryInfiniteChatConfigWithProviderName(
provider_name=test_provider_name,
provider_api_key=test_provider_api_key,
headers={},
)
client = SupermemoryOpenAI(test_api_key, config)
result = await client.chat_completion(
messages=test_messages[1], # System + user messages
model=test_model_name,
temperature=0.7,
)
assert result is not None
assert hasattr(result, "choices")
assert len(result.choices) > 0
assert result.choices[0].message.content is not None
@pytest.mark.asyncio
async def test_handle_conversation_history(
self,
test_api_key: str,
test_provider_api_key: str,
test_provider_name: ProviderName,
test_model_name: str,
test_messages: List[List[ChatCompletionMessageParam]],
):
"""Test handling conversation history."""
config = SupermemoryInfiniteChatConfigWithProviderName(
provider_name=test_provider_name,
provider_api_key=test_provider_api_key,
headers={},
)
client = SupermemoryOpenAI(test_api_key, config)
result = await client.chat_completion(
messages=test_messages[2], # Multi-turn conversation
model=test_model_name,
)
assert result is not None
assert hasattr(result, "choices")
assert len(result.choices) > 0
assert result.choices[0].message.content is not None
@pytest.mark.asyncio
async def test_custom_headers(
self,
test_api_key: str,
test_provider_api_key: str,
test_provider_name: ProviderName,
test_model_name: str,
):
"""Test working with custom headers."""
config = SupermemoryInfiniteChatConfigWithProviderName(
provider_name=test_provider_name,
provider_api_key=test_provider_api_key,
headers={"x-custom-header": "test-value"},
)
client = SupermemoryOpenAI(test_api_key, config)
result = await client.chat_completion(
messages=[{"role": "user", "content": "Hello"}],
model=test_model_name,
)
assert result is not None
assert hasattr(result, "choices")
class TestConfigurationValidation:
"""Test configuration validation."""
def test_handle_empty_headers_object(
self,
test_api_key: str,
test_provider_api_key: str,
test_provider_name: ProviderName,
):
"""Test handling empty headers object."""
config = SupermemoryInfiniteChatConfigWithProviderName(
provider_name=test_provider_name,
provider_api_key=test_provider_api_key,
headers={},
)
client = SupermemoryOpenAI(test_api_key, config)
assert client is not None
def test_handle_configuration_without_headers(
self,
test_api_key: str,
test_provider_api_key: str,
test_provider_name: ProviderName,
):
"""Test handling configuration without headers."""
config = SupermemoryInfiniteChatConfigWithProviderName(
provider_name=test_provider_name,
provider_api_key=test_provider_api_key,
)
client = SupermemoryOpenAI(test_api_key, config)
assert client is not None
def test_handle_different_api_keys(self):
"""Test handling different API keys."""
config = SupermemoryInfiniteChatConfigWithProviderName(
provider_name="openai",
provider_api_key="different-provider-key",
)
client = SupermemoryOpenAI("different-sm-key", config)
assert client is not None
class TestDisabledEndpoints:
"""Test that non-chat endpoints are disabled."""
def test_disabled_endpoints_throw_errors(
self, test_api_key: str, test_provider_api_key: str
):
"""Test that all disabled endpoints throw appropriate errors."""
config = SupermemoryInfiniteChatConfigWithProviderName(
provider_name="openai",
provider_api_key=test_provider_api_key,
)
client = SupermemoryOpenAI(test_api_key, config)
# Test that all disabled endpoints throw appropriate errors
with pytest.raises(
RuntimeError, match="Supermemory only supports chat completions"
):
_ = client.embeddings
with pytest.raises(
RuntimeError, match="Supermemory only supports chat completions"
):
_ = client.fine_tuning
with pytest.raises(
RuntimeError, match="Supermemory only supports chat completions"
):
_ = client.images
with pytest.raises(
RuntimeError, match="Supermemory only supports chat completions"
):
_ = client.audio
with pytest.raises(
RuntimeError, match="Supermemory only supports chat completions"
):
_ = client.models
with pytest.raises(
RuntimeError, match="Supermemory only supports chat completions"
):
_ = client.moderations
with pytest.raises(
RuntimeError, match="Supermemory only supports chat completions"
):
_ = client.files
with pytest.raises(
RuntimeError, match="Supermemory only supports chat completions"
):
_ = client.batches
with pytest.raises(
RuntimeError, match="Supermemory only supports chat completions"
):
_ = client.uploads
with pytest.raises(
RuntimeError, match="Supermemory only supports chat completions"
):
_ = client.beta
def test_chat_completions_still_work(
self, test_api_key: str, test_provider_api_key: str
):
"""Test that chat completions still work after disabling other endpoints."""
config = SupermemoryInfiniteChatConfigWithProviderName(
provider_name="openai",
provider_api_key=test_provider_api_key,
)
client = SupermemoryOpenAI(test_api_key, config)
# Chat completions should still be accessible
assert client.chat is not None
assert client.chat.completions is not None
assert callable(client.create_chat_completion)
assert callable(client.chat_completion)

View file

@ -0,0 +1,350 @@
"""Tests for tools module."""
import os
import pytest
import json
from typing import List
from openai.types.chat import ChatCompletionMessageToolCall
from ..src import (
SupermemoryTools,
SupermemoryToolsConfig,
SupermemoryOpenAI,
SupermemoryInfiniteChatConfigWithProviderName,
create_supermemory_tools,
get_memory_tool_definitions,
execute_memory_tool_calls,
create_search_memories_tool,
create_add_memory_tool,
)
@pytest.fixture
def test_api_key() -> str:
"""Get test Supermemory API key from environment."""
api_key = os.getenv("SUPERMEMORY_API_KEY")
if not api_key:
pytest.skip("SUPERMEMORY_API_KEY environment variable is required for tests")
return api_key
@pytest.fixture
def test_provider_api_key() -> str:
"""Get test provider API key from environment."""
api_key = os.getenv("PROVIDER_API_KEY")
if not api_key:
pytest.skip("PROVIDER_API_KEY environment variable is required for tests")
return api_key
@pytest.fixture
def test_base_url() -> str:
"""Get test base URL from environment."""
return os.getenv("SUPERMEMORY_BASE_URL", "")
@pytest.fixture
def test_model_name() -> str:
"""Get test model name from environment."""
return os.getenv("MODEL_NAME", "gpt-4o-mini")
class TestToolInitialization:
"""Test tool initialization."""
def test_create_tools_with_default_configuration(self, test_api_key: str):
"""Test creating tools with default configuration."""
config: SupermemoryToolsConfig = {}
tools = SupermemoryTools(test_api_key, config)
assert tools is not None
assert tools.get_tool_definitions() is not None
assert len(tools.get_tool_definitions()) == 3
def test_create_tools_with_helper(self, test_api_key: str):
"""Test creating tools with createSupermemoryTools helper."""
tools = create_supermemory_tools(
test_api_key,
{
"project_id": "test-project",
},
)
assert tools is not None
assert tools.get_tool_definitions() is not None
def test_create_tools_with_custom_base_url(
self, test_api_key: str, test_base_url: str
):
"""Test creating tools with custom baseUrl."""
if not test_base_url:
pytest.skip("SUPERMEMORY_BASE_URL not provided")
config: SupermemoryToolsConfig = {
"base_url": test_base_url,
}
tools = SupermemoryTools(test_api_key, config)
assert tools is not None
assert len(tools.get_tool_definitions()) == 3
def test_create_tools_with_project_id(self, test_api_key: str):
"""Test creating tools with projectId configuration."""
config: SupermemoryToolsConfig = {
"project_id": "test-project-123",
}
tools = SupermemoryTools(test_api_key, config)
assert tools is not None
assert len(tools.get_tool_definitions()) == 3
def test_create_tools_with_custom_container_tags(self, test_api_key: str):
"""Test creating tools with custom container tags."""
config: SupermemoryToolsConfig = {
"container_tags": ["custom-tag-1", "custom-tag-2"],
}
tools = SupermemoryTools(test_api_key, config)
assert tools is not None
assert len(tools.get_tool_definitions()) == 3
class TestToolDefinitions:
"""Test tool definitions."""
def test_return_proper_openai_function_definitions(self):
"""Test returning proper OpenAI function definitions."""
definitions = get_memory_tool_definitions()
assert definitions is not None
assert len(definitions) == 3
# Check searchMemories
search_tool = next(
(d for d in definitions if d["function"]["name"] == "search_memories"), None
)
assert search_tool is not None
assert search_tool["type"] == "function"
assert "information_to_get" in search_tool["function"]["parameters"]["required"]
# Check addMemory
add_tool = next(
(d for d in definitions if d["function"]["name"] == "add_memory"), None
)
assert add_tool is not None
assert add_tool["type"] == "function"
assert "memory" in add_tool["function"]["parameters"]["required"]
def test_consistent_tool_definitions_from_class_and_helper(self, test_api_key: str):
"""Test that tool definitions are consistent between class and helper."""
tools = SupermemoryTools(test_api_key)
class_definitions = tools.get_tool_definitions()
helper_definitions = get_memory_tool_definitions()
assert class_definitions == helper_definitions
class TestMemoryOperations:
"""Test memory operations."""
@pytest.mark.asyncio
async def test_search_memories(self, test_api_key: str, test_base_url: str):
"""Test searching memories."""
config: SupermemoryToolsConfig = {
"project_id": "test-search",
}
if test_base_url:
config["base_url"] = test_base_url
tools = SupermemoryTools(test_api_key, config)
result = await tools.search_memories(
information_to_get="test preferences",
limit=5,
)
assert result is not None
assert "success" in result
assert isinstance(result["success"], bool)
if result["success"]:
assert "results" in result
assert "count" in result
assert isinstance(result["count"], int)
else:
assert "error" in result
@pytest.mark.asyncio
async def test_add_memory(self, test_api_key: str, test_base_url: str):
"""Test adding memory."""
config: SupermemoryToolsConfig = {
"container_tags": ["test-add-memory"],
}
if test_base_url:
config["base_url"] = test_base_url
tools = SupermemoryTools(test_api_key, config)
result = await tools.add_memory(
memory="User prefers dark roast coffee in the morning - test memory"
)
assert result is not None
assert "success" in result
assert isinstance(result["success"], bool)
if result["success"]:
assert "memory" in result
else:
assert "error" in result
class TestIndividualToolCreators:
"""Test individual tool creators."""
def test_create_individual_search_tool(self, test_api_key: str):
"""Test creating individual search tool."""
search_tool = create_search_memories_tool(
test_api_key,
{
"project_id": "test-individual",
},
)
assert search_tool is not None
assert search_tool.definition is not None
assert callable(search_tool.execute)
assert search_tool.definition["function"]["name"] == "search_memories"
def test_create_individual_add_tool(self, test_api_key: str):
"""Test creating individual add tool."""
add_tool = create_add_memory_tool(
test_api_key,
{
"project_id": "test-individual",
},
)
assert add_tool is not None
assert add_tool.definition is not None
assert callable(add_tool.execute)
assert add_tool.definition["function"]["name"] == "add_memory"
class TestOpenAIIntegration:
"""Test OpenAI integration."""
@pytest.mark.asyncio
async def test_work_with_supermemory_openai_for_function_calling(
self,
test_api_key: str,
test_provider_api_key: str,
test_model_name: str,
test_base_url: str,
):
"""Test working with SupermemoryOpenAI for function calling."""
client = SupermemoryOpenAI(
test_api_key,
SupermemoryInfiniteChatConfigWithProviderName(
provider_name="openai",
provider_api_key=test_provider_api_key,
),
)
tools_config: SupermemoryToolsConfig = {
"project_id": "test-openai-integration",
}
if test_base_url:
tools_config["base_url"] = test_base_url
tools = SupermemoryTools(test_api_key, tools_config)
response = await client.chat_completion(
messages=[
{
"role": "system",
"content": (
"You are a helpful assistant with access to user memories. "
"When the user asks you to remember something, use the add_memory tool."
),
},
{
"role": "user",
"content": "Please remember that I prefer tea over coffee",
},
],
model=test_model_name,
tools=tools.get_tool_definitions(),
)
assert response is not None
assert hasattr(response, "choices")
choice = response.choices[0]
assert choice.message is not None
# If the model decided to use function calling, test the execution
if hasattr(choice.message, "tool_calls") and choice.message.tool_calls:
tool_results = await execute_memory_tool_calls(
test_api_key,
choice.message.tool_calls,
tools_config,
)
assert tool_results is not None
assert len(tool_results) == len(choice.message.tool_calls)
for result in tool_results:
assert result["role"] == "tool"
assert "content" in result
assert "tool_call_id" in result
@pytest.mark.asyncio
async def test_handle_multiple_tool_calls(
self, test_api_key: str, test_base_url: str
):
"""Test handling multiple tool calls."""
tools_config: SupermemoryToolsConfig = {
"container_tags": ["test-multi-tools"],
}
if test_base_url:
tools_config["base_url"] = test_base_url
# Simulate tool calls (normally these would come from OpenAI)
mock_tool_calls: List[ChatCompletionMessageToolCall] = [
ChatCompletionMessageToolCall(
id="call_1",
type="function",
function={
"name": "search_memories",
"arguments": json.dumps({"information_to_get": "preferences"}),
},
),
ChatCompletionMessageToolCall(
id="call_2",
type="function",
function={
"name": "add_memory",
"arguments": json.dumps(
{"memory": "Test memory for multiple calls"}
),
},
),
]
results = await execute_memory_tool_calls(
test_api_key, mock_tool_calls, tools_config
)
assert results is not None
assert len(results) == 2
assert results[0]["tool_call_id"] == "call_1"
assert results[1]["tool_call_id"] == "call_2"
for result in results:
assert result["role"] == "tool"
assert "content" in result
content = json.loads(result["content"])
assert "success" in content

1313
packages/openai-sdk-python/uv.lock generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,5 @@
src/
.turbo/
.env
tsdown.config.ts
tsconfig.json

View file

@ -0,0 +1,314 @@
# supermemory OpenAI SDK Utilities
OpenAI JS/TS SDK utilities for supermemory
## Installation
```bash
npm install @supermemory/openai-sdk
# or
bun add @supermemory/openai-sdk
# or
pnpm add @supermemory/openai-sdk
# or
yarn add @supermemory/openai-sdk
```
## Features
Choose **one** of the following approaches (they cannot be used together):
- **Infinite Chat Client**: Enhanced OpenAI client with unlimited context support
- **Memory Tools**: Search, add, and fetch memories from supermemory using OpenAI function calling
## Infinite Chat Client
The infinite chat client provides an enhanced OpenAI client with supermemory's context management.
```typescript
import { SupermemoryOpenAI } from '@supermemory/openai-sdk'
// Using a named provider
const client = new SupermemoryOpenAI('your-supermemory-api-key', {
providerName: 'openai',
providerApiKey: 'your-openai-api-key',
headers: {
// Optional additional headers
}
})
// Using a custom provider URL
const client = new SupermemoryOpenAI('your-supermemory-api-key', {
providerUrl: 'https://your-custom-provider.com/v1',
providerApiKey: 'your-provider-api-key',
headers: {
// Optional additional headers
}
})
const response = await client.chat.completions.create({
model: 'gpt-4o',
messages: [
{ role: 'user', content: 'Hello, how are you?' }
]
})
```
### Complete Infinite Chat Example
```typescript
import { SupermemoryOpenAI } from '@supermemory/openai-sdk'
const supermemoryApiKey = process.env.SUPERMEMORY_API_KEY!
const openaiApiKey = process.env.OPENAI_API_KEY!
// Initialize infinite chat client
const client = new SupermemoryOpenAI(supermemoryApiKey, {
providerName: 'openai',
providerApiKey: openaiApiKey
})
async function chat(userMessage: string) {
const response = await client.chatCompletion([
{
role: 'system',
content: 'You are a helpful assistant with unlimited context.'
},
{
role: 'user',
content: userMessage
}
], {
model: 'gpt-4o'
})
return response.choices[0].message.content
}
```
### Configuration
```typescript
// Option 1: Use a named provider
interface ConfigWithProviderName {
providerName: 'openai' | 'anthropic' | 'openrouter' | 'deepinfra' | 'groq' | 'google' | 'cloudflare'
providerApiKey: string
headers?: Record<string, string>
}
// Option 2: Use a custom provider URL
interface ConfigWithProviderUrl {
providerUrl: string
providerApiKey: string
headers?: Record<string, string>
}
```
## Memory Tools
supermemory tools allow OpenAI function calling to interact with user memories for enhanced context and personalization.
```typescript
import { SupermemoryTools, executeMemoryToolCalls } from '@supermemory/openai-sdk'
import OpenAI from 'openai'
const openai = new OpenAI({ apiKey: 'your-openai-api-key' })
const response = await openai.chat.completions.create({
model: 'gpt-4o',
messages: [
{ role: 'user', content: 'What do you remember about my preferences?' }
],
tools: new SupermemoryTools('your-supermemory-api-key', {
// Optional: specify a base URL for self-hosted instances
baseUrl: 'https://api.supermemory.com',
// Use either projectId OR containerTags, not both
projectId: 'your-project-id',
// OR
containerTags: ['tag1', 'tag2']
}).getToolDefinitions()
})
```
### Complete Memory Tools Example
```typescript
import { SupermemoryTools, executeMemoryToolCalls } from '@supermemory/openai-sdk'
import OpenAI from 'openai'
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY! })
const supermemoryApiKey = process.env.SUPERMEMORY_API_KEY!
async function chatWithTools(userMessage: string) {
const tools = new SupermemoryTools(supermemoryApiKey, {
projectId: 'my-project'
})
const response = await openai.chat.completions.create({
model: 'gpt-4o',
messages: [
{
role: 'system',
content: 'You are a helpful assistant with access to user memories.'
},
{
role: 'user',
content: userMessage
}
],
tools: tools.getToolDefinitions()
})
// Handle tool calls if present
if (response.choices[0].message.tool_calls) {
const toolResults = await executeMemoryToolCalls(
supermemoryApiKey,
response.choices[0].message.tool_calls,
{ projectId: 'my-project' }
)
// Continue conversation with tool results...
}
return response.choices[0].message.content
}
```
### Configuration
```typescript
interface SupermemoryToolsConfig {
// Optional: Base URL for API calls (default: https://api.supermemory.com)
baseUrl?: string
// Container tags for organizing memories (cannot be used with projectId)
containerTags?: string[]
// Project ID for scoping memories (cannot be used with containerTags)
projectId?: string
}
```
### Self-Hosted supermemory
If you're running a self-hosted supermemory instance:
```typescript
const tools = new SupermemoryTools('your-api-key', {
baseUrl: 'https://your-supermemory-instance.com',
containerTags: ['production', 'user-memories']
})
```
### Available Tools
##### Search Memories
Search through user memories using semantic matching.
```typescript
const searchResult = await tools.searchMemories({
informationToGet: 'user preferences about coffee'
})
```
##### Add Memory
Add new memories to the user's memory store.
```typescript
const addResult = await tools.addMemory({
memory: 'User prefers dark roast coffee in the morning'
})
```
##### Fetch Memory
Retrieve a specific memory by its ID.
```typescript
const fetchResult = await tools.fetchMemory({
memoryId: 'memory-id-123'
})
```
### Using Individual Tools
For more flexibility, you can import and use individual tools:
```typescript
import {
createSearchMemoriesTool,
createAddMemoryTool,
createFetchMemoryTool
} from '@supermemory/openai-sdk'
const searchTool = createSearchMemoriesTool('your-api-key', {
projectId: 'your-project-id'
})
// Use only the search tool
const response = await openai.chat.completions.create({
model: 'gpt-4o',
messages: [...],
tools: [searchTool.definition]
})
```
### Error Handling
All tool executions return a result object with a `success` field:
```typescript
const result = await tools.searchMemories({
informationToGet: 'user preferences'
})
if (result.success) {
console.log('Found memories:', result.results)
console.log('Total count:', result.count)
} else {
console.error('Error searching memories:', result.error)
}
```
## Development
### Running Tests
```bash
# Run all tests
bun test
# Run tests in watch mode
bun test --watch
```
#### Environment Variables for Tests
All tests require API keys to run. Copy `.env.example` to `.env` and set the required values:
```bash
cp .env.example .env
```
**Required:**
- `SUPERMEMORY_API_KEY`: Your Supermemory API key
- `PROVIDER_API_KEY`: Your AI provider API key (OpenAI, etc.)
**Optional:**
- `SUPERMEMORY_BASE_URL`: Custom Supermemory base URL (defaults to `https://api.supermemory.ai`)
- `PROVIDER_NAME`: Provider name (defaults to `openai`) - one of: `openai`, `anthropic`, `openrouter`, `deepinfra`, `groq`, `google`, `cloudflare`
- `PROVIDER_URL`: Custom provider URL (use instead of `PROVIDER_NAME`)
- `MODEL_NAME`: Model to use in tests (defaults to `gpt-4o-mini`)
Tests will fail if required API keys are not provided.
## License
MIT
## Support
Email our [24/7 Founder/CEO/Support Executive](mailto:dhravya@supermemory.com)

View file

@ -0,0 +1,46 @@
{
"name": "@supermemory/openai-sdk",
"version": "1.0.0",
"type": "module",
"description": "OpenAI SDK utilities for supermemory",
"scripts": {
"build": "tsdown",
"dev": "tsdown --watch",
"check-types": "tsc --noEmit",
"test": "vitest",
"test:watch": "vitest --watch"
},
"dependencies": {
"openai": "^4.104.0",
"supermemory": "^3.0.0-alpha.26",
"zod": "^4.1.4"
},
"devDependencies": {
"@total-typescript/tsconfig": "^1.0.4",
"@types/bun": "^1.2.21",
"dotenv": "^16.6.1",
"tsdown": "^0.14.2",
"typescript": "^5.9.2",
"vitest": "^3.2.4"
},
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": "./dist/index.js",
"./package.json": "./package.json"
},
"repository": {
"url": "https://github.com/supermemoryai/supermemory",
"directory": "packages/openai-sdk-ts"
},
"keywords": [
"openai",
"typescript",
"supermemory",
"ai",
"memory",
"context"
],
"license": "MIT"
}

View file

@ -0,0 +1,2 @@
export * from "./infinite-chat"
export * from "./tools"

View file

@ -0,0 +1,338 @@
import { describe, expect, it } from "vitest"
import z from "zod"
import type OpenAI from "openai"
import {
SupermemoryOpenAI,
type SupermemoryInfiniteChatConfig,
} from "./infinite-chat"
import "dotenv/config"
const providers = z.enum([
"openai",
"anthropic",
"openrouter",
"deepinfra",
"groq",
"google",
"cloudflare",
] satisfies SupermemoryInfiniteChatConfig["providerName"][])
describe("SupermemoryOpenAI", () => {
// Required API keys - tests will fail if not provided
const testApiKey = process.env.SUPERMEMORY_API_KEY
const testProviderApiKey = process.env.PROVIDER_API_KEY
if (!testApiKey) {
throw new Error(
"SUPERMEMORY_API_KEY environment variable is required for tests",
)
}
if (!testProviderApiKey) {
throw new Error(
"PROVIDER_API_KEY environment variable is required for tests",
)
}
// Optional configuration with defaults
const testProviderName = providers.parse(
process.env.PROVIDER_NAME ?? "openai",
)
const testProviderUrl = process.env.PROVIDER_URL
const testModelName = process.env.MODEL_NAME || "gpt-4o-mini"
const testHeaders = { "custom-header": "test-value" }
// Validate provider configuration - either name OR URL, not both
if (testProviderUrl && process.env.PROVIDER_NAME) {
throw new Error(
"Cannot specify both PROVIDER_NAME and PROVIDER_URL - use one or the other",
)
}
// Test prompts
const testMessages: OpenAI.Chat.Completions.ChatCompletionMessageParam[][] = [
[{ role: "user", content: "Hello!" }],
[
{ role: "system", content: "You are a helpful assistant." },
{ role: "user", content: "What is AI?" },
],
[
{ role: "user", content: "Tell me a joke" },
{
role: "assistant",
content:
"Why don't scientists trust atoms? Because they make up everything!",
},
{ role: "user", content: "Tell me another one" },
],
]
describe("client creation", () => {
it("should create client with SupermemoryOpenAI class", () => {
const config: SupermemoryInfiniteChatConfig = testProviderUrl
? {
providerUrl: testProviderUrl,
providerApiKey: testProviderApiKey,
headers: testHeaders,
}
: {
providerName: testProviderName,
providerApiKey: testProviderApiKey,
headers: testHeaders,
}
const client = new SupermemoryOpenAI(testApiKey, config)
expect(client).toBeDefined()
expect(client.chat).toBeDefined()
})
it("should create client with openai provider configuration", () => {
const config: SupermemoryInfiniteChatConfig = {
providerName: "openai",
providerApiKey: testProviderApiKey,
headers: testHeaders,
}
const client = new SupermemoryOpenAI(testApiKey, config)
expect(client).toBeDefined()
})
it("should create client with custom provider URL", () => {
const customUrl = "https://custom-provider.com/v1"
const config: SupermemoryInfiniteChatConfig = {
providerUrl: customUrl,
providerApiKey: testProviderApiKey,
headers: testHeaders,
}
const client = new SupermemoryOpenAI(testApiKey, config)
expect(client).toBeDefined()
})
})
describe("chat completions", () => {
it("should create chat completion with simple message", async () => {
const config: SupermemoryInfiniteChatConfig = testProviderUrl
? {
providerUrl: testProviderUrl,
providerApiKey: testProviderApiKey,
headers: {},
}
: {
providerName: testProviderName,
providerApiKey: testProviderApiKey,
headers: {},
}
const client = new SupermemoryOpenAI(testApiKey, config)
const result = await client.createChatCompletion({
model: testModelName,
messages: testMessages[0]!, // "Hello!"
})
expect(result).toBeDefined()
expect("choices" in result).toBe(true)
if ("choices" in result) {
expect(result.choices).toBeDefined()
expect(result.choices.length).toBeGreaterThan(0)
expect(result.choices[0]!.message.content).toBeDefined()
}
})
it("should create chat completion using convenience method", async () => {
const config: SupermemoryInfiniteChatConfig = testProviderUrl
? {
providerUrl: testProviderUrl,
providerApiKey: testProviderApiKey,
headers: {},
}
: {
providerName: testProviderName,
providerApiKey: testProviderApiKey,
headers: {},
}
const client = new SupermemoryOpenAI(testApiKey, config)
const result = await client.chatCompletion(testMessages[1]!, {
model: testModelName,
temperature: 0.7,
})
expect(result).toBeDefined()
expect("choices" in result).toBe(true)
if ("choices" in result) {
expect(result.choices).toBeDefined()
expect(result.choices.length).toBeGreaterThan(0)
expect(result.choices[0]!.message.content).toBeDefined()
}
})
it("should handle conversation history", async () => {
const config: SupermemoryInfiniteChatConfig = testProviderUrl
? {
providerUrl: testProviderUrl,
providerApiKey: testProviderApiKey,
headers: {},
}
: {
providerName: testProviderName,
providerApiKey: testProviderApiKey,
headers: {},
}
const client = new SupermemoryOpenAI(testApiKey, config)
const result = await client.chatCompletion(testMessages[2]!, {
model: testModelName,
})
expect(result).toBeDefined()
expect("choices" in result).toBe(true)
if ("choices" in result) {
expect(result.choices).toBeDefined()
expect(result.choices.length).toBeGreaterThan(0)
expect(result.choices[0]!.message.content).toBeDefined()
}
})
it("should work with custom headers", async () => {
const config: SupermemoryInfiniteChatConfig = testProviderUrl
? {
providerUrl: testProviderUrl,
providerApiKey: testProviderApiKey,
headers: {
"x-custom-header": "test-value",
},
}
: {
providerName: testProviderName,
providerApiKey: testProviderApiKey,
headers: {
"x-custom-header": "test-value",
},
}
const client = new SupermemoryOpenAI(testApiKey, config)
const result = await client.chatCompletion(
[{ role: "user", content: "Hello" }],
{
model: testModelName,
},
)
expect(result).toBeDefined()
expect("choices" in result).toBe(true)
})
})
describe("configuration validation", () => {
it("should handle empty headers object", () => {
const config: SupermemoryInfiniteChatConfig = testProviderUrl
? {
providerUrl: testProviderUrl,
providerApiKey: testProviderApiKey,
headers: {},
}
: {
providerName: testProviderName,
providerApiKey: testProviderApiKey,
headers: {},
}
const client = new SupermemoryOpenAI(testApiKey, config)
expect(client).toBeDefined()
})
it("should handle configuration without headers", () => {
const config: SupermemoryInfiniteChatConfig = testProviderUrl
? {
providerUrl: testProviderUrl,
providerApiKey: testProviderApiKey,
}
: {
providerName: testProviderName,
providerApiKey: testProviderApiKey,
}
const client = new SupermemoryOpenAI(testApiKey, config)
expect(client).toBeDefined()
})
it("should handle different API keys", () => {
const config: SupermemoryInfiniteChatConfig = {
providerName: "openai",
providerApiKey: "different-provider-key",
}
const client = new SupermemoryOpenAI("different-sm-key", config)
expect(client).toBeDefined()
})
})
describe("disabled endpoints", () => {
it("should throw errors for disabled OpenAI endpoints", () => {
const config: SupermemoryInfiniteChatConfig = {
providerName: "openai",
providerApiKey: testProviderApiKey,
}
const client = new SupermemoryOpenAI(testApiKey, config)
// Test that all disabled endpoints throw appropriate errors
expect(() => client.embeddings).toThrow(
"Supermemory only supports chat completions",
)
expect(() => client.fineTuning).toThrow(
"Supermemory only supports chat completions",
)
expect(() => client.images).toThrow(
"Supermemory only supports chat completions",
)
expect(() => client.audio).toThrow(
"Supermemory only supports chat completions",
)
expect(() => client.models).toThrow(
"Supermemory only supports chat completions",
)
expect(() => client.moderations).toThrow(
"Supermemory only supports chat completions",
)
expect(() => client.files).toThrow(
"Supermemory only supports chat completions",
)
expect(() => client.batches).toThrow(
"Supermemory only supports chat completions",
)
expect(() => client.uploads).toThrow(
"Supermemory only supports chat completions",
)
expect(() => client.beta).toThrow(
"Supermemory only supports chat completions",
)
})
it("should still allow chat completions to work", () => {
const config: SupermemoryInfiniteChatConfig = {
providerName: "openai",
providerApiKey: testProviderApiKey,
}
const client = new SupermemoryOpenAI(testApiKey, config)
// Chat completions should still be accessible
expect(client.chat).toBeDefined()
expect(client.chat.completions).toBeDefined()
expect(client.createChatCompletion).toBeDefined()
expect(client.chatCompletion).toBeDefined()
})
})
})

View file

@ -0,0 +1,133 @@
import OpenAI from "openai"
interface SupermemoryInfiniteChatConfigBase {
providerApiKey: string
headers?: Record<string, string>
}
interface SupermemoryInfiniteChatConfigWithProviderName
extends SupermemoryInfiniteChatConfigBase {
providerName: keyof typeof providerMap
providerUrl?: never
}
interface SupermemoryInfiniteChatConfigWithProviderUrl
extends SupermemoryInfiniteChatConfigBase {
providerUrl: string
providerName?: never
}
export type SupermemoryInfiniteChatConfig =
| SupermemoryInfiniteChatConfigWithProviderName
| SupermemoryInfiniteChatConfigWithProviderUrl
type SupermemoryApiKey = string
const providerMap = {
openai: "https://api.openai.com/v1",
anthropic: "https://api.anthropic.com/v1",
openrouter: "https://openrouter.ai/api/v1",
deepinfra: "https://api.deepinfra.com/v1/openai",
groq: "https://api.groq.com/openai/v1",
google: "https://generativelanguage.googleapis.com/v1beta/openai",
cloudflare: "https://gateway.ai.cloudflare.com/v1/*/unlimited-context/openai",
} as const
/**
* Enhanced OpenAI client with supermemory integration
* Only chat completions are supported - all other OpenAI API endpoints are disabled
*/
export class SupermemoryOpenAI extends OpenAI {
private supermemoryApiKey: string
constructor(
supermemoryApiKey: SupermemoryApiKey,
config?: SupermemoryInfiniteChatConfig,
) {
const baseURL = config?.providerName
? providerMap[config.providerName]
: (config?.providerUrl ?? "https://api.openai.com/v1")
super({
apiKey: config?.providerApiKey,
baseURL,
defaultHeaders: {
"x-supermemory-api-key": supermemoryApiKey,
...config?.headers,
},
})
this.supermemoryApiKey = supermemoryApiKey
// Disable all non-chat completion endpoints
this.disableUnsupportedEndpoints()
}
/**
* Disable all OpenAI endpoints except chat completions
*/
private disableUnsupportedEndpoints() {
const unsupportedError = (): never => {
throw new Error(
"Supermemory only supports chat completions. Use chatCompletion() or chat.completions.create() instead.",
)
}
// Override all other OpenAI API endpoints using Object.defineProperty
const endpoints = [
"embeddings",
"fineTuning",
"images",
"audio",
"models",
"moderations",
"files",
"batches",
"uploads",
"beta",
]
for (const endpoint of endpoints) {
Object.defineProperty(this, endpoint, {
get: unsupportedError,
configurable: true,
})
}
}
/**
* Create chat completions with infinite context support
*/
async createChatCompletion<
T extends OpenAI.Chat.Completions.ChatCompletionCreateParams,
>(params: T) {
return this.chat.completions.create(params)
}
/**
* Create chat completions with simplified interface
*/
async chatCompletion(
messages: OpenAI.Chat.Completions.ChatCompletionMessageParam[],
options?: {
model?: string
temperature?: number
maxTokens?: number
tools?: OpenAI.Chat.Completions.ChatCompletionTool[]
toolChoice?: OpenAI.Chat.Completions.ChatCompletionToolChoiceOption
stream?: boolean
},
) {
const params = {
model: options?.model ?? "gpt-4o",
messages,
temperature: options?.temperature,
max_tokens: options?.maxTokens,
tools: options?.tools,
tool_choice: options?.toolChoice,
stream: options?.stream,
} satisfies OpenAI.Chat.Completions.ChatCompletionCreateParams
return this.chat.completions.create(params)
}
}

View file

@ -0,0 +1,297 @@
import { describe, expect, it } from "vitest"
import {
SupermemoryTools,
createSupermemoryTools,
getMemoryToolDefinitions,
executeMemoryToolCalls,
createSearchMemoriesTool,
createAddMemoryTool,
type SupermemoryToolsConfig,
} from "./tools"
import { SupermemoryOpenAI } from "./infinite-chat"
import "dotenv/config"
describe("SupermemoryTools", () => {
// Required API keys - tests will fail if not provided
const testApiKey = process.env.SUPERMEMORY_API_KEY
const testProviderApiKey = process.env.PROVIDER_API_KEY
if (!testApiKey) {
throw new Error(
"SUPERMEMORY_API_KEY environment variable is required for tests",
)
}
if (!testProviderApiKey) {
throw new Error(
"PROVIDER_API_KEY environment variable is required for tests",
)
}
// Optional configuration with defaults
const testBaseUrl = process.env.SUPERMEMORY_BASE_URL ?? undefined
const testModelName = process.env.MODEL_NAME || "gpt-4o-mini"
describe("tool initialization", () => {
it("should create tools with default configuration", () => {
const config: SupermemoryToolsConfig = {}
const tools = new SupermemoryTools(testApiKey, config)
expect(tools).toBeDefined()
expect(tools.getToolDefinitions()).toBeDefined()
expect(tools.getToolDefinitions().length).toBe(3)
})
it("should create tools with createSupermemoryTools helper", () => {
const tools = createSupermemoryTools(testApiKey, {
projectId: "test-project",
})
expect(tools).toBeDefined()
expect(tools.getToolDefinitions()).toBeDefined()
})
it("should create tools with custom baseUrl", () => {
const config: SupermemoryToolsConfig = {
baseUrl: testBaseUrl,
}
const tools = new SupermemoryTools(testApiKey, config)
expect(tools).toBeDefined()
expect(tools.getToolDefinitions().length).toBe(3)
})
it("should create tools with projectId configuration", () => {
const config: SupermemoryToolsConfig = {
projectId: "test-project-123",
}
const tools = new SupermemoryTools(testApiKey, config)
expect(tools).toBeDefined()
expect(tools.getToolDefinitions().length).toBe(3)
})
it("should create tools with custom container tags", () => {
const config: SupermemoryToolsConfig = {
containerTags: ["custom-tag-1", "custom-tag-2"],
}
const tools = new SupermemoryTools(testApiKey, config)
expect(tools).toBeDefined()
expect(tools.getToolDefinitions().length).toBe(3)
})
})
describe("tool definitions", () => {
it("should return proper OpenAI function definitions", () => {
const definitions = getMemoryToolDefinitions()
expect(definitions).toBeDefined()
expect(definitions.length).toBe(3)
// Check searchMemories
const searchTool = definitions.find(
(d) => d.function.name === "searchMemories",
)
expect(searchTool).toBeDefined()
expect(searchTool!.type).toBe("function")
expect(searchTool!.function.parameters?.required).toContain(
"informationToGet",
)
// Check addMemory
const addTool = definitions.find((d) => d.function.name === "addMemory")
expect(addTool).toBeDefined()
expect(addTool!.type).toBe("function")
expect(addTool!.function.parameters?.required).toContain("memory")
})
it("should have consistent tool definitions from class and helper", () => {
const tools = new SupermemoryTools(testApiKey)
const classDefinitions = tools.getToolDefinitions()
const helperDefinitions = getMemoryToolDefinitions()
expect(classDefinitions).toEqual(helperDefinitions)
})
})
describe("memory operations", () => {
it("should search memories", async () => {
const tools = new SupermemoryTools(testApiKey, {
projectId: "test-search",
baseUrl: testBaseUrl,
})
const result = await tools.searchMemories({
informationToGet: "test preferences",
limit: 5,
})
expect(result).toBeDefined()
expect(result.success).toBeDefined()
expect(typeof result.success).toBe("boolean")
if (result.success) {
expect(result.results).toBeDefined()
expect(result.count).toBeDefined()
expect(typeof result.count).toBe("number")
} else {
expect(result.error).toBeDefined()
}
})
it("should add memory", async () => {
const tools = new SupermemoryTools(testApiKey, {
containerTags: ["test-add-memory"],
baseUrl: testBaseUrl,
})
const result = await tools.addMemory({
memory: "User prefers dark roast coffee in the morning - test memory",
})
expect(result).toBeDefined()
expect(result.success).toBeDefined()
expect(typeof result.success).toBe("boolean")
if (result.success) {
expect(result.memory).toBeDefined()
} else {
expect(result.error).toBeDefined()
}
})
})
describe("individual tool creators", () => {
it("should create individual search tool", () => {
const searchTool = createSearchMemoriesTool(testApiKey, {
projectId: "test-individual",
})
expect(searchTool).toBeDefined()
expect(searchTool.definition).toBeDefined()
expect(searchTool.execute).toBeDefined()
expect(searchTool.definition.function.name).toBe("searchMemories")
})
it("should create individual add tool", () => {
const addTool = createAddMemoryTool(testApiKey, {
projectId: "test-individual",
})
expect(addTool).toBeDefined()
expect(addTool.definition).toBeDefined()
expect(addTool.execute).toBeDefined()
expect(addTool.definition.function.name).toBe("addMemory")
})
})
describe("OpenAI integration", () => {
it("should work with SupermemoryOpenAI for function calling", async () => {
const client = new SupermemoryOpenAI(testApiKey, {
providerName: "openai",
providerApiKey: testProviderApiKey,
})
const tools = new SupermemoryTools(testApiKey, {
projectId: "test-openai-integration",
baseUrl: testBaseUrl,
})
const response = await client.chatCompletion(
[
{
role: "system",
content:
"You are a helpful assistant with access to user memories. When the user asks you to remember something, use the addMemory tool.",
},
{
role: "user",
content: "Please remember that I prefer tea over coffee",
},
],
{
model: testModelName,
tools: tools.getToolDefinitions(),
},
)
expect(response).toBeDefined()
expect("choices" in response).toBe(true)
if ("choices" in response) {
const choice = response.choices[0]!
expect(choice.message).toBeDefined()
// If the model decided to use function calling, test the execution
if (choice.message.tool_calls && choice.message.tool_calls.length > 0) {
const toolResults = await executeMemoryToolCalls(
testApiKey,
choice.message.tool_calls,
{
projectId: "test-openai-integration",
baseUrl: testBaseUrl,
},
)
expect(toolResults).toBeDefined()
expect(toolResults.length).toBe(choice.message.tool_calls.length)
for (const result of toolResults) {
expect(result.role).toBe("tool")
expect(result.content).toBeDefined()
expect(result.tool_call_id).toBeDefined()
}
}
}
})
it("should handle multiple tool calls", async () => {
const tools = new SupermemoryTools(testApiKey, {
containerTags: ["test-multi-tools"],
baseUrl: testBaseUrl,
})
// Simulate tool calls (normally these would come from OpenAI)
const mockToolCalls = [
{
id: "call_1",
type: "function" as const,
function: {
name: "searchMemories",
arguments: JSON.stringify({ informationToGet: "preferences" }),
},
},
{
id: "call_2",
type: "function" as const,
function: {
name: "addMemory",
arguments: JSON.stringify({
memory: "Test memory for multiple calls",
}),
},
},
]
const results = await executeMemoryToolCalls(testApiKey, mockToolCalls, {
containerTags: ["test-multi-tools"],
baseUrl: testBaseUrl,
})
expect(results).toBeDefined()
expect(results.length).toBe(2)
expect(results[0]!.tool_call_id).toBe("call_1")
expect(results[1]!.tool_call_id).toBe("call_2")
for (const result of results) {
expect(result.role).toBe("tool")
expect(result.content).toBeDefined()
const content = JSON.parse(result.content as string)
expect(content.success).toBeDefined()
}
})
})
})

View file

@ -0,0 +1,299 @@
import type OpenAI from "openai"
import Supermemory from "supermemory"
/**
* Supermemory configuration
* Only one of `projectId` or `containerTags` can be provided.
*/
export interface SupermemoryToolsConfig {
baseUrl?: string
containerTags?: string[]
projectId?: string
}
/**
* Result types for memory operations
*/
export interface MemorySearchResult {
success: boolean
results?: Awaited<ReturnType<Supermemory["search"]["execute"]>>["results"]
count?: number
error?: string
}
export interface MemoryAddResult {
success: boolean
memory?: Awaited<ReturnType<Supermemory["memories"]["add"]>>
error?: string
}
export interface MemoryFetchResult {
success: boolean
memory?: Awaited<ReturnType<Supermemory["memories"]["get"]>>
error?: string
}
/**
* Function schemas for OpenAI function calling
*/
export const memoryToolSchemas = {
searchMemories: {
name: "searchMemories",
description:
"Search (recall) memories/details/information about the user or other facts or entities. Run when explicitly asked or when context about user's past choices would be helpful.",
parameters: {
type: "object",
properties: {
informationToGet: {
type: "string",
description: "Terms to search for in the user's memories",
},
includeFullDocs: {
type: "boolean",
description:
"Whether to include the full document content in the response. Defaults to true for better AI context.",
default: true,
},
limit: {
type: "number",
description: "Maximum number of results to return",
default: 10,
},
},
required: ["informationToGet"],
},
} satisfies OpenAI.FunctionDefinition,
addMemory: {
name: "addMemory",
description:
"Add (remember) memories/details/information about the user or other facts or entities. Run when explicitly asked or when the user mentions any information generalizable beyond the context of the current conversation.",
parameters: {
type: "object",
properties: {
memory: {
type: "string",
description:
"The text content of the memory to add. This should be a single sentence or a short paragraph.",
},
},
required: ["memory"],
},
} satisfies OpenAI.FunctionDefinition,
} as const
/**
* Create memory tool handlers for OpenAI function calling
*/
export class SupermemoryTools {
private client: Supermemory
private containerTags: string[]
constructor(apiKey: string, config?: SupermemoryToolsConfig) {
this.client = new Supermemory({
apiKey,
...(config?.baseUrl && { baseURL: config.baseUrl }),
})
this.containerTags = config?.projectId
? [`sm_project_${config.projectId}`]
: (config?.containerTags ?? ["sm_project_default"])
}
/**
* Get OpenAI function definitions for all memory tools
*/
getToolDefinitions(): OpenAI.Chat.Completions.ChatCompletionTool[] {
return [
{ type: "function", function: memoryToolSchemas.searchMemories },
{ type: "function", function: memoryToolSchemas.addMemory },
]
}
/**
* Execute a tool call based on the function name and arguments
*/
async executeToolCall(
toolCall: OpenAI.Chat.Completions.ChatCompletionMessageToolCall,
): Promise<string> {
const functionName = toolCall.function.name
const args = JSON.parse(toolCall.function.arguments)
switch (functionName) {
case "searchMemories":
return JSON.stringify(await this.searchMemories(args))
case "addMemory":
return JSON.stringify(await this.addMemory(args))
default:
return JSON.stringify({
success: false,
error: `Unknown function: ${functionName}`,
})
}
}
/**
* Search memories
*/
async searchMemories({
informationToGet,
includeFullDocs = true,
limit = 10,
}: {
informationToGet: string
includeFullDocs?: boolean
limit?: number
}): Promise<MemorySearchResult> {
try {
const response = await this.client.search.execute({
q: informationToGet,
containerTags: this.containerTags,
limit,
chunkThreshold: 0.6,
includeFullDocs,
})
return {
success: true,
results: response.results,
count: response.results?.length || 0,
}
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error",
}
}
}
/**
* Add a memory
*/
async addMemory({ memory }: { memory: string }): Promise<MemoryAddResult> {
try {
const metadata: Record<string, string | number | boolean> = {}
const response = await this.client.memories.add({
content: memory,
containerTags: this.containerTags,
...(Object.keys(metadata).length > 0 && { metadata }),
})
return {
success: true,
memory: response,
}
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error",
}
}
}
/**
* Fetch a specific memory by ID
*/
async fetchMemory({
memoryId,
}: {
memoryId: string
}): Promise<MemoryFetchResult> {
try {
const response = await this.client.memories.get(memoryId)
return {
success: true,
memory: response,
}
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error",
}
}
}
}
/**
* Helper function to create SupermemoryTools instance
*/
export function createSupermemoryTools(
apiKey: string,
config?: SupermemoryToolsConfig,
): SupermemoryTools {
return new SupermemoryTools(apiKey, config)
}
/**
* Get OpenAI function definitions for memory tools
*/
export function getMemoryToolDefinitions(): OpenAI.Chat.Completions.ChatCompletionTool[] {
return [
{ type: "function", function: memoryToolSchemas.searchMemories },
{ type: "function", function: memoryToolSchemas.addMemory },
]
}
/**
* Execute tool calls from OpenAI function calling
*/
export async function executeMemoryToolCalls(
apiKey: string,
toolCalls: OpenAI.Chat.Completions.ChatCompletionMessageToolCall[],
config?: SupermemoryToolsConfig,
): Promise<OpenAI.Chat.Completions.ChatCompletionToolMessageParam[]> {
const tools = new SupermemoryTools(apiKey, config)
const results = await Promise.all(
toolCalls.map(async (toolCall) => {
const result = await tools.executeToolCall(toolCall)
return {
tool_call_id: toolCall.id,
role: "tool" as const,
content: result,
}
}),
)
return results
}
/**
* Individual tool creators for more granular control
*/
export function createSearchMemoriesTool(
apiKey: string,
config?: SupermemoryToolsConfig,
) {
return {
definition: {
type: "function" as const,
function: memoryToolSchemas.searchMemories,
},
execute: (args: {
informationToGet: string
includeFullDocs?: boolean
limit?: number
}) => {
const tools = new SupermemoryTools(apiKey, config)
return tools.searchMemories(args)
},
}
}
export function createAddMemoryTool(
apiKey: string,
config?: SupermemoryToolsConfig,
) {
return {
definition: {
type: "function" as const,
function: memoryToolSchemas.addMemory,
},
execute: (args: { memory: string }) => {
const tools = new SupermemoryTools(apiKey, config)
return tools.addMemory(args)
},
}
}

View file

@ -0,0 +1,12 @@
{
"extends": "@total-typescript/tsconfig/bundler/dom/library-monorepo",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./*"],
"@lib/*": ["../lib/*"]
},
"declaration": true,
"declarationMap": true
}
}

View file

@ -0,0 +1,15 @@
import { defineConfig } from "tsdown"
export default defineConfig({
entry: ["src/index.ts"],
format: "esm",
sourcemap: true,
target: "es2020",
tsconfig: "./tsconfig.json",
clean: true,
minify: true,
dts: {
sourcemap: true,
},
exports: true,
})