diff --git a/package.json b/package.json index 53e6850..6a375b0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@requesty/ai-sdk", - "version": "2.1.2", + "version": "3.0.0", "license": "Apache-2.0", "sideEffects": false, "main": "./dist/index.js", @@ -41,7 +41,7 @@ } }, "dependencies": { - "@ai-sdk/provider": "^2.0.0", + "@ai-sdk/provider": "^3.0.0", "@ai-sdk/provider-utils": "^3.0.9" }, "devDependencies": { @@ -49,7 +49,7 @@ "@edge-runtime/vm": "5.0.0", "@types/jest": "29.5.14", "@types/node": "22.13.5", - "ai": "^5.0.52", + "ai": "^6.0.1", "dotenv": "^17.2.3", "msw": "^2.11.3", "tsdown": "^0.15.7", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ee68d03..bfcc44f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,14 +9,14 @@ importers: .: dependencies: '@ai-sdk/provider': - specifier: ^2.0.0 - version: 2.0.0 + specifier: ^3.0.0 + version: 3.0.0 '@ai-sdk/provider-utils': specifier: ^3.0.9 version: 3.0.9(zod@4.1.11) vite: specifier: ^7.1.7 - version: 7.1.10(@types/node@22.13.5)(jiti@2.6.1) + version: 6.3.6(@types/node@22.13.5)(jiti@2.6.1) devDependencies: '@biomejs/biome': specifier: 2.2.5 @@ -31,8 +31,8 @@ importers: specifier: 22.13.5 version: 22.13.5 ai: - specifier: ^5.0.52 - version: 5.0.52(zod@4.1.11) + specifier: ^6.0.1 + version: 6.0.1(zod@4.1.11) dotenv: specifier: ^17.2.3 version: 17.2.3 @@ -54,11 +54,11 @@ importers: packages: - '@ai-sdk/gateway@1.0.29': - resolution: {integrity: sha512-o9LtmBiG2WAgs3GAmL79F8idan/UupxHG8Tyr2gP4aUSOzflM0bsvfzozBp8x6WatQnOx+Pio7YNw45Y6I16iw==} + '@ai-sdk/gateway@3.0.0': + resolution: {integrity: sha512-JcjePYVpbezv+XOxkxPemwnorjWpgDiiKWMYy6FXTCG2rFABIK2Co1bFxIUSDT4vYO6f1448x9rKbn38vbhDiA==} engines: {node: '>=18'} peerDependencies: - zod: ^3.25.76 || ^4 + zod: ^3.25.76 || ^4.1.8 '@ai-sdk/provider-utils@3.0.9': resolution: {integrity: sha512-Pm571x5efqaI4hf9yW4KsVlDBDme8++UepZRnq+kqVBWWjgvGhQlzU8glaFq0YJEB9kkxZHbRRyVeHoV2sRYaQ==} @@ -66,10 +66,20 @@ packages: peerDependencies: zod: ^3.25.76 || ^4 + '@ai-sdk/provider-utils@4.0.0': + resolution: {integrity: sha512-HyCyOls9I3a3e38+gtvOJOEjuw9KRcvbBnCL5GBuSmJvS9Jh9v3fz7pRC6ha1EUo/ZH1zwvLWYXBMtic8MTguA==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + '@ai-sdk/provider@2.0.0': resolution: {integrity: sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==} engines: {node: '>=18'} + '@ai-sdk/provider@3.0.0': + resolution: {integrity: sha512-m9ka3ptkPQbaHHZHqDXDF9C9B5/Mav0KTdky1k2HZ3/nrW2t1AgObxIVPyGDWQNS9FXT/FS6PIoSjpcP/No8rQ==} + engines: {node: '>=18'} + '@babel/code-frame@7.27.1': resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} @@ -162,11 +172,11 @@ packages: resolution: {integrity: sha512-NKBGBSIKUG584qrS1tyxVpX/AKJKQw5HgjYEnPLC0QsTw79JrGn+qUr8CXFb955Iy7GUdiiUv1rJ6JBGvaKb6w==} engines: {node: '>=18'} - '@emnapi/core@1.5.0': - resolution: {integrity: sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==} + '@emnapi/core@1.7.1': + resolution: {integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==} - '@emnapi/runtime@1.5.0': - resolution: {integrity: sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==} + '@emnapi/runtime@1.7.1': + resolution: {integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==} '@emnapi/wasi-threads@1.1.0': resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} @@ -391,8 +401,8 @@ packages: resolution: {integrity: sha512-sURvQbbKsq5f8INV54YJgJEdk8oxBanqkTiXXd33rKmofFCwZLhLRszPduMZ9TA9b8/1CHc/IJmOlBHJk2Q5AQ==} engines: {node: '>=18'} - '@napi-rs/wasm-runtime@1.0.7': - resolution: {integrity: sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==} + '@napi-rs/wasm-runtime@1.1.0': + resolution: {integrity: sha512-Fq6DJW+Bb5jaWE69/qOE0D1TUN9+6uWhCeZpdnSBk14pjLcCWR7Q8n49PTSPHazM37JqrsdpEthXy2xn6jWWiA==} '@open-draft/deferred-promise@2.2.0': resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==} @@ -407,97 +417,91 @@ packages: resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} engines: {node: '>=8.0.0'} - '@oxc-project/types@0.94.0': - resolution: {integrity: sha512-+UgQT/4o59cZfH6Cp7G0hwmqEQ0wE+AdIwhikdwnhWI9Dp8CgSY081+Q3O67/wq3VJu8mgUEB93J9EHHn70fOw==} + '@oxc-project/types@0.103.0': + resolution: {integrity: sha512-bkiYX5kaXWwUessFRSoXFkGIQTmc6dLGdxuRTrC+h8PSnIdZyuXHHlLAeTmOue5Br/a0/a7dHH0Gca6eXn9MKg==} '@quansync/fs@0.1.5': resolution: {integrity: sha512-lNS9hL2aS2NZgNW7BBj+6EBl4rOf8l+tQ0eRY6JWCI8jI2kc53gSoqbjojU0OnAWhzoXiOjFyGsHcDGePB3lhA==} - '@rolldown/binding-android-arm64@1.0.0-beta.43': - resolution: {integrity: sha512-TP8bcPOb1s6UmY5syhXrDn9k0XkYcw+XaoylTN4cJxf0JOVS2j682I3aTcpfT51hOFGr2bRwNKN9RZ19XxeQbA==} + '@rolldown/binding-android-arm64@1.0.0-beta.56': + resolution: {integrity: sha512-GFsly+vPnl1Sa61sC2LwK4Hrz48W+YBqBmLSxBEj9IJW6nHNsWof1wwh1gwnxMIm/yN5F9M0B/cRAwn6rTINyg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@rolldown/binding-darwin-arm64@1.0.0-beta.43': - resolution: {integrity: sha512-kuVWnZsE4vEjMF/10SbSUyzucIW2zmdsqFghYMqy+fsjXnRHg0luTU6qWF8IqJf4Cbpm9NEZRnjIEPpAbdiSNQ==} + '@rolldown/binding-darwin-arm64@1.0.0-beta.56': + resolution: {integrity: sha512-8fSkk5g5MVZpddrH8hOyc9O5t5Dqv2Vi3Qe628xe+2zJedJxucUc5DX/KY1OVBRp8XY09LJO+J1V56LsxeBVPA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@rolldown/binding-darwin-x64@1.0.0-beta.43': - resolution: {integrity: sha512-u9Ps4sh6lcmJ3vgLtyEg/x4jlhI64U0mM93Ew+tlfFdLDe7yKyA+Fe80cpr2n1mNCeZXrvTSbZluKpXQ0GxLjw==} + '@rolldown/binding-darwin-x64@1.0.0-beta.56': + resolution: {integrity: sha512-R+Q5zd763MKvgYSkBfr2gr/3nZQENaK88qEqfRUUYrpq/W0okOpbOJaxn5FDIIS+yq3cjyktYm115I5RiI6G5A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@rolldown/binding-freebsd-x64@1.0.0-beta.43': - resolution: {integrity: sha512-h9lUtVtXgfbk/tnicMpbFfZ3DJvk5Zn2IvmlC1/e0+nUfwoc/TFqpfrRRqcNBXk/e+xiWMSKv6b0MF8N+Rtvlg==} + '@rolldown/binding-freebsd-x64@1.0.0-beta.56': + resolution: {integrity: sha512-YEsv0rfJoHHRNaVx6AfW/o4bmwTY7BJnSQ45rRCyU6DWEgvFZMojh6qzMQmW5ZVdcikE3cU1ZnrQQ2yem9H9Yg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.43': - resolution: {integrity: sha512-IX2C6bA6wM2rX/RvD75ko+ix9yxPKjKGGq7pOhB8wGI4Z4fqX5B1nDHga/qMDmAdCAR1m9ymzxkmqhm/AFYf7A==} + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.56': + resolution: {integrity: sha512-mpaV+NCKcHUOkcAThvz1KiXcNshLQRSBLNNKqum2dG7oLZKk+z+02Fxa8BSuFFqq/rmmO6Fq2TPAdZUgOrwiqw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.43': - resolution: {integrity: sha512-mcjd57vEj+CEQbZAzUiaxNzNgwwgOpFtZBWcINm8DNscvkXl5b/s622Z1dqGNWSdrZmdjdC6LWMvu8iHM6v9sQ==} + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.56': + resolution: {integrity: sha512-wj1uQRN4GEhYw5cs0dobGzZg3oKMLuQ3hY3fW7cLzvlwi9XRdzW7NmU58e6YUp6boOQLarSxdmAaqCMgaMZfcQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - '@rolldown/binding-linux-arm64-musl@1.0.0-beta.43': - resolution: {integrity: sha512-Pa8QMwlkrztTo/1mVjZmPIQ44tCSci10TBqxzVBvXVA5CFh5EpiEi99fPSll2dHG2uT4dCOMeC6fIhyDdb0zXA==} + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.56': + resolution: {integrity: sha512-Z2PWbAHjW2EUflb1/tPvouMqppwWF5Va1Y9b4GQpO6QlpGK0Wqmn90GO2VKiheDh/gSZlsxZ7uOZoXh2y8R7Kg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - '@rolldown/binding-linux-x64-gnu@1.0.0-beta.43': - resolution: {integrity: sha512-BgynXKMjeaX4AfWLARhOKDetBOOghnSiVRjAHVvhiAaDXgdQN8e65mSmXRiVoVtD3cHXx/cfU8Gw0p0K+qYKVQ==} + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.56': + resolution: {integrity: sha512-Z/uv04/Tsf7oqhwjPUiDiSildhWmCpsklA0e5PEB+0eGGmm07B+M2SmqRe9Fd0ypfU2TPGhq+Hn7RVUGIfSMxg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - '@rolldown/binding-linux-x64-musl@1.0.0-beta.43': - resolution: {integrity: sha512-VIsoPlOB/tDSAw9CySckBYysoIBqLeps1/umNSYUD8pMtalJyzMTneAVI1HrUdf4ceFmQ5vARoLIXSsPwVFxNg==} + '@rolldown/binding-linux-x64-musl@1.0.0-beta.56': + resolution: {integrity: sha512-u+yP0Pt9ar3PkLGGiyGmQKVj9j20X0E831DY0OVmbKYHAAbTyLKYx+UIIorCm+SQnhGKfkD+0pmwfTc2t2Vt/g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - '@rolldown/binding-openharmony-arm64@1.0.0-beta.43': - resolution: {integrity: sha512-YDXTxVJG67PqTQMKyjVJSddoPbSWJ4yRz/E3xzTLHqNrTDGY0UuhG8EMr8zsYnfH/0cPFJ3wjQd/hJWHuR6nkA==} + '@rolldown/binding-openharmony-arm64@1.0.0-beta.56': + resolution: {integrity: sha512-Kuc6r5Uya+KxdJ7MUSok3K8zta/1bcsaSNxTvYujm2mWYuffadqgkkR3d0UCRbbCH5klZ+7VG6DR3VtPRlCntw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@rolldown/binding-wasm32-wasi@1.0.0-beta.43': - resolution: {integrity: sha512-3M+2DmorXvDuAIGYQ9Z93Oy1G9ETkejLwdXXb1uRTgKN9pMcu7N+KG2zDrJwqyxeeLIFE22AZGtSJm3PJbNu9Q==} + '@rolldown/binding-wasm32-wasi@1.0.0-beta.56': + resolution: {integrity: sha512-pejT5oLj8xlfn8tjC3bJKeuAsk/un6GKwjbsBQG0AchefdaHf2+S4QRn8XfEMB1l1ZTbe5yEiiV92mr7Jdjaeg==} engines: {node: '>=14.0.0'} cpu: [wasm32] - '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.43': - resolution: {integrity: sha512-/B1j1pJs33y9ywtslOMxryUPHq8zIGu/OGEc2gyed0slimJ8fX2uR/SaJVhB4+NEgCFIeYDR4CX6jynAkeRuCA==} + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.56': + resolution: {integrity: sha512-1NKkRLQR2ghmHMd+14nm1noOhoLei62pkdGlf1g4F+9lfFws66+9LBnP6Z+E+KK8Do9hzQ6FFRwtkC3EADAeyA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.43': - resolution: {integrity: sha512-29oG1swCz7hNP+CQYrsM4EtylsKwuYzM8ljqbqC5TsQwmKat7P8ouDpImsqg/GZxFSXcPP9ezQm0Q0wQwGM3JA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [ia32] - os: [win32] - - '@rolldown/binding-win32-x64-msvc@1.0.0-beta.43': - resolution: {integrity: sha512-eWBV1Ef3gfGNehxVGCyXs7wLayRIgCmyItuCZwYYXW5bsk4EvR4n2GP5m3ohjnx7wdiY3nLmwQfH2Knb5gbNZw==} + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.56': + resolution: {integrity: sha512-BC3mObCr7/O+1jMJ/Hm3INikBk5D25RTxCha10Rq8b1gHlBfb9eA460+7xQfc8FxUsMCUgHtvrK3Vs5izgwBOQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@rolldown/pluginutils@1.0.0-beta.43': - resolution: {integrity: sha512-5Uxg7fQUCmfhax7FJke2+8B6cqgeUJUD9o2uXIKXhD+mG0mL6NObmVoi9wXEU1tY89mZKgAYA6fTbftx3q2ZPQ==} + '@rolldown/pluginutils@1.0.0-beta.56': + resolution: {integrity: sha512-cw9jwAgCs024Nic4OB8PeFDLBHLD1Athcv3bRvyYATIVD9B/gL5X5cJkezT94Y7m7Dk9HXaUMcvb7ypvSX46sA==} '@rollup/rollup-android-arm-eabi@4.52.2': resolution: {integrity: sha512-o3pcKzJgSGt4d74lSZ+OCnHwkKBeAbFDmbEm5gg70eA8VkyCuC/zV9TwBnmw6VjDlRdF4Pshfb+WE9E6XY1PoQ==} @@ -615,6 +619,9 @@ packages: '@standard-schema/spec@1.0.0': resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + '@tybys/wasm-util@0.10.1': resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} @@ -651,6 +658,10 @@ packages: '@types/yargs@17.0.33': resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} + '@vercel/oidc@3.0.5': + resolution: {integrity: sha512-fnYhv671l+eTTp48gB4zEsTW/YtRgRPnkI2nT7x6qw5rkI1Lq2hTmQIpHPgyThI0znLK+vX2n9XxKdXZ7BUbbw==} + engines: {node: '>= 20'} + '@vitest/expect@3.0.7': resolution: {integrity: sha512-QP25f+YJhzPfHrHfYHtvRn+uvkCFCqFtW9CktfBxmB+25QqWsx7VB2As6f4GmwllHLDhXNHvqedwhvMmSnNmjw==} @@ -683,11 +694,11 @@ packages: '@vitest/utils@3.0.7': resolution: {integrity: sha512-xePVpCRfooFX3rANQjwoditoXgWb1MaFbzmGuPP59MK6i13mrnDw/yEIyJudLeW6/38mCNcwCiJIGmpDPibAIg==} - ai@5.0.52: - resolution: {integrity: sha512-GLlRHjMlvN9+w7UYGxCpUQ8GgCRv5Z+JCprRH3Q8YbXJ/JyIc6EP9+YRUmQsyExX/qQsuehe7y/LLygarbSTOw==} + ai@6.0.1: + resolution: {integrity: sha512-g/jPakC6h4vUJKDww0d6+VaJmfMC38UqH3kKsngiP+coT0uvCUdQ7lpFDJ0mNmamaOyRMaY2zwEB2RnTAaJU/w==} engines: {node: '>=18'} peerDependencies: - zod: ^3.25.76 || ^4 + zod: ^3.25.76 || ^4.1.8 ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} @@ -1029,8 +1040,8 @@ packages: vue-tsc: optional: true - rolldown@1.0.0-beta.43: - resolution: {integrity: sha512-6RcqyRx0tY1MlRLnjXPp/849Rl/CPFhzpGGwNPEPjKwqBMqPq/Rbbkxasa8s0x+IkUk46ty4jazb5skZ/Vgdhw==} + rolldown@1.0.0-beta.56: + resolution: {integrity: sha512-9MHiUvRH2R8rb6ad6EaLxahS3RbQKdMMlrh9XKmbz2HiCGfK4IWKSNv4N6GhYr+7kHExg6oIc5EF1xA3iR4x1A==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -1220,46 +1231,6 @@ packages: yaml: optional: true - vite@7.1.10: - resolution: {integrity: sha512-CmuvUBzVJ/e3HGxhg6cYk88NGgTnBoOo7ogtfJJ0fefUWAxN/WDSUa50o+oVBxuIhO8FoEZW0j2eW7sfjs5EtA==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true - peerDependencies: - '@types/node': ^20.19.0 || >=22.12.0 - jiti: '>=1.21.0' - less: ^4.0.0 - lightningcss: ^1.21.0 - sass: ^1.70.0 - sass-embedded: ^1.70.0 - stylus: '>=0.54.8' - sugarss: ^5.0.0 - terser: ^5.16.0 - tsx: ^4.8.1 - yaml: ^2.4.2 - peerDependenciesMeta: - '@types/node': - optional: true - jiti: - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - tsx: - optional: true - yaml: - optional: true - vitest@3.0.7: resolution: {integrity: sha512-IP7gPK3LS3Fvn44x30X1dM9vtawm0aesAa2yBIZ9vQf+qB69NXC5776+Qmcr7ohUXIQuLhk7xQR0aSUIDPqavg==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} @@ -1322,10 +1293,11 @@ packages: snapshots: - '@ai-sdk/gateway@1.0.29(zod@4.1.11)': + '@ai-sdk/gateway@3.0.0(zod@4.1.11)': dependencies: - '@ai-sdk/provider': 2.0.0 - '@ai-sdk/provider-utils': 3.0.9(zod@4.1.11) + '@ai-sdk/provider': 3.0.0 + '@ai-sdk/provider-utils': 4.0.0(zod@4.1.11) + '@vercel/oidc': 3.0.5 zod: 4.1.11 '@ai-sdk/provider-utils@3.0.9(zod@4.1.11)': @@ -1335,10 +1307,21 @@ snapshots: eventsource-parser: 3.0.6 zod: 4.1.11 + '@ai-sdk/provider-utils@4.0.0(zod@4.1.11)': + dependencies: + '@ai-sdk/provider': 3.0.0 + '@standard-schema/spec': 1.1.0 + eventsource-parser: 3.0.6 + zod: 4.1.11 + '@ai-sdk/provider@2.0.0': dependencies: json-schema: 0.4.0 + '@ai-sdk/provider@3.0.0': + dependencies: + json-schema: 0.4.0 + '@babel/code-frame@7.27.1': dependencies: '@babel/helper-validator-identifier': 7.27.1 @@ -1415,13 +1398,13 @@ snapshots: dependencies: '@edge-runtime/primitives': 6.0.0 - '@emnapi/core@1.5.0': + '@emnapi/core@1.7.1': dependencies: '@emnapi/wasi-threads': 1.1.0 tslib: 2.8.1 optional: true - '@emnapi/runtime@1.5.0': + '@emnapi/runtime@1.7.1': dependencies: tslib: 2.8.1 optional: true @@ -1577,10 +1560,10 @@ snapshots: outvariant: 1.4.3 strict-event-emitter: 0.5.1 - '@napi-rs/wasm-runtime@1.0.7': + '@napi-rs/wasm-runtime@1.1.0': dependencies: - '@emnapi/core': 1.5.0 - '@emnapi/runtime': 1.5.0 + '@emnapi/core': 1.7.1 + '@emnapi/runtime': 1.7.1 '@tybys/wasm-util': 0.10.1 optional: true @@ -1595,57 +1578,54 @@ snapshots: '@opentelemetry/api@1.9.0': {} - '@oxc-project/types@0.94.0': {} + '@oxc-project/types@0.103.0': {} '@quansync/fs@0.1.5': dependencies: quansync: 0.2.11 - '@rolldown/binding-android-arm64@1.0.0-beta.43': + '@rolldown/binding-android-arm64@1.0.0-beta.56': optional: true - '@rolldown/binding-darwin-arm64@1.0.0-beta.43': + '@rolldown/binding-darwin-arm64@1.0.0-beta.56': optional: true - '@rolldown/binding-darwin-x64@1.0.0-beta.43': + '@rolldown/binding-darwin-x64@1.0.0-beta.56': optional: true - '@rolldown/binding-freebsd-x64@1.0.0-beta.43': + '@rolldown/binding-freebsd-x64@1.0.0-beta.56': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.43': + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.56': optional: true - '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.43': + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.56': optional: true - '@rolldown/binding-linux-arm64-musl@1.0.0-beta.43': + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.56': optional: true - '@rolldown/binding-linux-x64-gnu@1.0.0-beta.43': + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.56': optional: true - '@rolldown/binding-linux-x64-musl@1.0.0-beta.43': + '@rolldown/binding-linux-x64-musl@1.0.0-beta.56': optional: true - '@rolldown/binding-openharmony-arm64@1.0.0-beta.43': + '@rolldown/binding-openharmony-arm64@1.0.0-beta.56': optional: true - '@rolldown/binding-wasm32-wasi@1.0.0-beta.43': + '@rolldown/binding-wasm32-wasi@1.0.0-beta.56': dependencies: - '@napi-rs/wasm-runtime': 1.0.7 + '@napi-rs/wasm-runtime': 1.1.0 optional: true - '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.43': + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.56': optional: true - '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.43': + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.56': optional: true - '@rolldown/binding-win32-x64-msvc@1.0.0-beta.43': - optional: true - - '@rolldown/pluginutils@1.0.0-beta.43': {} + '@rolldown/pluginutils@1.0.0-beta.56': {} '@rollup/rollup-android-arm-eabi@4.52.2': optional: true @@ -1717,6 +1697,8 @@ snapshots: '@standard-schema/spec@1.0.0': {} + '@standard-schema/spec@1.1.0': {} + '@tybys/wasm-util@0.10.1': dependencies: tslib: 2.8.1 @@ -1755,6 +1737,8 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 + '@vercel/oidc@3.0.5': {} + '@vitest/expect@3.0.7': dependencies: '@vitest/spy': 3.0.7 @@ -1800,11 +1784,11 @@ snapshots: loupe: 3.2.1 tinyrainbow: 2.0.0 - ai@5.0.52(zod@4.1.11): + ai@6.0.1(zod@4.1.11): dependencies: - '@ai-sdk/gateway': 1.0.29(zod@4.1.11) - '@ai-sdk/provider': 2.0.0 - '@ai-sdk/provider-utils': 3.0.9(zod@4.1.11) + '@ai-sdk/gateway': 3.0.0(zod@4.1.11) + '@ai-sdk/provider': 3.0.0 + '@ai-sdk/provider-utils': 4.0.0(zod@4.1.11) '@opentelemetry/api': 1.9.0 zod: 4.1.11 @@ -2100,7 +2084,7 @@ snapshots: rettime@0.7.0: {} - rolldown-plugin-dts@0.16.11(rolldown@1.0.0-beta.43)(typescript@5.7.3): + rolldown-plugin-dts@0.16.11(rolldown@1.0.0-beta.56)(typescript@5.7.3): dependencies: '@babel/generator': 7.28.3 '@babel/parser': 7.28.4 @@ -2111,33 +2095,31 @@ snapshots: dts-resolver: 2.1.2 get-tsconfig: 4.12.0 magic-string: 0.30.19 - rolldown: 1.0.0-beta.43 + rolldown: 1.0.0-beta.56 optionalDependencies: typescript: 5.7.3 transitivePeerDependencies: - oxc-resolver - supports-color - rolldown@1.0.0-beta.43: + rolldown@1.0.0-beta.56: dependencies: - '@oxc-project/types': 0.94.0 - '@rolldown/pluginutils': 1.0.0-beta.43 - ansis: 4.2.0 + '@oxc-project/types': 0.103.0 + '@rolldown/pluginutils': 1.0.0-beta.56 optionalDependencies: - '@rolldown/binding-android-arm64': 1.0.0-beta.43 - '@rolldown/binding-darwin-arm64': 1.0.0-beta.43 - '@rolldown/binding-darwin-x64': 1.0.0-beta.43 - '@rolldown/binding-freebsd-x64': 1.0.0-beta.43 - '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.43 - '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.43 - '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.43 - '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.43 - '@rolldown/binding-linux-x64-musl': 1.0.0-beta.43 - '@rolldown/binding-openharmony-arm64': 1.0.0-beta.43 - '@rolldown/binding-wasm32-wasi': 1.0.0-beta.43 - '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.43 - '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.43 - '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.43 + '@rolldown/binding-android-arm64': 1.0.0-beta.56 + '@rolldown/binding-darwin-arm64': 1.0.0-beta.56 + '@rolldown/binding-darwin-x64': 1.0.0-beta.56 + '@rolldown/binding-freebsd-x64': 1.0.0-beta.56 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.56 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.56 + '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.56 + '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.56 + '@rolldown/binding-linux-x64-musl': 1.0.0-beta.56 + '@rolldown/binding-openharmony-arm64': 1.0.0-beta.56 + '@rolldown/binding-wasm32-wasi': 1.0.0-beta.56 + '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.56 + '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.56 rollup@4.52.2: dependencies: @@ -2245,8 +2227,8 @@ snapshots: diff: 8.0.2 empathic: 2.0.0 hookable: 5.5.3 - rolldown: 1.0.0-beta.43 - rolldown-plugin-dts: 0.16.11(rolldown@1.0.0-beta.43)(typescript@5.7.3) + rolldown: 1.0.0-beta.56 + rolldown-plugin-dts: 0.16.11(rolldown@1.0.0-beta.56)(typescript@5.7.3) semver: 7.7.3 tinyexec: 1.0.1 tinyglobby: 0.2.15 @@ -2313,19 +2295,6 @@ snapshots: fsevents: 2.3.3 jiti: 2.6.1 - vite@7.1.10(@types/node@22.13.5)(jiti@2.6.1): - dependencies: - esbuild: 0.25.10 - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 - postcss: 8.5.6 - rollup: 4.52.2 - tinyglobby: 0.2.15 - optionalDependencies: - '@types/node': 22.13.5 - fsevents: 2.3.3 - jiti: 2.6.1 - vitest@3.0.7(@edge-runtime/vm@5.0.0)(@types/node@22.13.5)(jiti@2.6.1)(msw@2.11.3(@types/node@22.13.5)(typescript@5.7.3)): dependencies: '@vitest/expect': 3.0.7 diff --git a/src/e2e/pizza.test.ts b/src/e2e/pizza.test.ts index 1087ac0..9246d3b 100644 --- a/src/e2e/pizza.test.ts +++ b/src/e2e/pizza.test.ts @@ -1,4 +1,4 @@ -import { Experimental_Agent as Agent, type ToolSet, tool } from 'ai' +import { ToolLoopAgent, type ToolSet, tool } from 'ai' import { beforeAll, describe, expect, it } from 'vitest' import { z } from 'zod' import { createRequesty } from '..' @@ -417,7 +417,7 @@ function resetInventoryAndOrders() { const modelsToTest = getTestModels() const testGenerateAgent = async ( - agent: Agent, + agent: ToolLoopAgent, prompt: string, ) => { const { steps } = await agent.generate({ @@ -428,10 +428,10 @@ const testGenerateAgent = async ( } const testStreamAgent = async ( - agent: Agent, + agent: ToolLoopAgent, prompt: string, ) => { - const result = agent.stream({ + const result = await agent.stream({ prompt, }) @@ -448,15 +448,15 @@ describe.concurrent.each(modelsToTest)( 'Pizza Agent Tests - $name', { timeout: 90_000 }, ({ id }) => { - let pizzaAgent: Agent + let pizzaAgent: ToolLoopAgent beforeAll(() => { resetInventoryAndOrders() - pizzaAgent = new Agent({ + pizzaAgent = new ToolLoopAgent({ model: requesty.chat(id), - system: systemPrompt, + instructions: systemPrompt, tools, - }) + }) as any }) describe('agent.generate() tests', () => { diff --git a/src/index.ts b/src/index.ts index d5e0b54..5b163ae 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,11 +2,6 @@ export type { RequestyChatModelId, RequestyChatSettings, } from './requesty-chat-settings' -export type { RequestyCompletionLanguageModel } from './requesty-completion-language-model' -export type { - RequestyCompletionModelId, - RequestyCompletionSettings, -} from './requesty-completion-settings' export * from './requesty-provider' export type { RequestyChatMessage, diff --git a/src/integration/chat.test.ts b/src/integration/chat.test.ts index 3c1d533..f347574 100644 --- a/src/integration/chat.test.ts +++ b/src/integration/chat.test.ts @@ -57,11 +57,9 @@ describe('Requesty Integration - Chat', () => { expect(result.text).toBe('Hello! How can I help you today?') expect(result.finishReason).toBe('stop') - expect(result.usage).toEqual({ - inputTokens: 10, - outputTokens: 9, - totalTokens: 19, - }) + expect(result.usage.inputTokens).toBe(10) + expect(result.usage.outputTokens).toBe(9) + expect(result.usage.totalTokens).toBe(19) }) it('should handle tool calls', async () => { @@ -271,11 +269,9 @@ describe('Requesty Integration - Chat', () => { const usage = await result.usage expect(finishReason).toBe('stop') - expect(usage).toEqual({ - inputTokens: 10, - outputTokens: 5, - totalTokens: 15, - }) + expect(usage.inputTokens).toBe(10) + expect(usage.outputTokens).toBe(5) + expect(usage.totalTokens).toBe(15) }) it('should handle streaming tool calls', async () => { diff --git a/src/internal/index.ts b/src/internal/index.ts index 32ee335..9238f66 100644 --- a/src/internal/index.ts +++ b/src/internal/index.ts @@ -1,5 +1,3 @@ export * from '../requesty-chat-language-model' export * from '../requesty-chat-settings' -export * from '../requesty-completion-language-model' -export * from '../requesty-completion-settings' export * from '../types' diff --git a/src/map-requesty-finish-reason.ts b/src/map-requesty-finish-reason.ts index 9c60a7a..751822d 100644 --- a/src/map-requesty-finish-reason.ts +++ b/src/map-requesty-finish-reason.ts @@ -1,20 +1,20 @@ -import type { LanguageModelV2FinishReason } from '@ai-sdk/provider' +import type { LanguageModelV3FinishReason } from '@ai-sdk/provider' export function mapRequestyFinishReason( - finishReason: string | null | undefined, -): LanguageModelV2FinishReason { + finishReason: string | undefined, +): LanguageModelV3FinishReason { switch (finishReason) { case 'stop': - return 'stop' + return { unified: 'stop', raw: finishReason } case 'length': case 'max_tokens': - return 'length' + return { unified: 'length', raw: finishReason } case 'tool_calls': case 'function_call': - return 'tool-calls' + return { unified: 'tool-calls', raw: finishReason } case 'content_filter': - return 'content-filter' + return { unified: 'content-filter', raw: finishReason } default: - return 'unknown' + return { unified: 'other', raw: finishReason } } } diff --git a/src/messages/handle-assistant-message.test.ts b/src/messages/handle-assistant-message.test.ts index 4788f07..d2ffed4 100644 --- a/src/messages/handle-assistant-message.test.ts +++ b/src/messages/handle-assistant-message.test.ts @@ -1,4 +1,4 @@ -import type { LanguageModelV2Message } from '@ai-sdk/provider' +import type { LanguageModelV3Message } from '@ai-sdk/provider' import { describe, expect, it } from 'vitest' import type { RequestyChatMessage } from '../types' import { handleAssistantMessage } from './handle-assistant-message' @@ -7,7 +7,7 @@ describe('assistant messages', () => { it.for< [ string, - Extract, + Extract, RequestyChatMessage, ] >([ diff --git a/src/messages/handle-assistant-message.ts b/src/messages/handle-assistant-message.ts index a1e488b..9d71b97 100644 --- a/src/messages/handle-assistant-message.ts +++ b/src/messages/handle-assistant-message.ts @@ -1,9 +1,9 @@ -import type { LanguageModelV2Message } from '@ai-sdk/provider' +import type { LanguageModelV3Message } from '@ai-sdk/provider' import type { RequestyChatMessage, RequestyToolCall } from '../types' import { maybeGetReasoningContent } from '../util' export function handleAssistantMessage( - message: Extract, + message: Extract, ): RequestyChatMessage { const assistantMessage: RequestyChatMessage = { role: 'assistant', diff --git a/src/messages/handle-system-message.ts b/src/messages/handle-system-message.ts index eda077f..b0b6447 100644 --- a/src/messages/handle-system-message.ts +++ b/src/messages/handle-system-message.ts @@ -1,8 +1,8 @@ -import type { LanguageModelV2Message } from '@ai-sdk/provider' +import type { LanguageModelV3Message } from '@ai-sdk/provider' import type { RequestyChatMessage } from '../types' export function handleSystemMessage( - message: Extract, + message: Extract, ): RequestyChatMessage { return { role: 'system', diff --git a/src/messages/handle-tool-message.test.ts b/src/messages/handle-tool-message.test.ts index 506e36d..34cc5c5 100644 --- a/src/messages/handle-tool-message.test.ts +++ b/src/messages/handle-tool-message.test.ts @@ -1,13 +1,13 @@ import type { - LanguageModelV2Message, - LanguageModelV2ToolResultPart, + LanguageModelV3Message, + LanguageModelV3ToolResultPart, } from '@ai-sdk/provider' import { describe, expect, it } from 'vitest' import type { RequestyChatMessage } from '../types' import { handleToolContentPart, handleToolMessage } from './handle-tool-message' describe('tool messages', () => { - it.for<[string, LanguageModelV2ToolResultPart, string]>([ + it.for<[string, LanguageModelV3ToolResultPart, string]>([ [ 'text output', { @@ -166,7 +166,7 @@ describe('tool messages', () => { it.for< [ string, - Extract, + Extract, Array, ] >([ diff --git a/src/messages/handle-tool-message.ts b/src/messages/handle-tool-message.ts index a9f4212..384743a 100644 --- a/src/messages/handle-tool-message.ts +++ b/src/messages/handle-tool-message.ts @@ -1,33 +1,44 @@ import type { - LanguageModelV2Message, - LanguageModelV2ToolResultPart, + LanguageModelV3Message, + LanguageModelV3ToolApprovalResponsePart, + LanguageModelV3ToolResultPart, } from '@ai-sdk/provider' import type { RequestyChatMessage } from '../types' export function handleToolContentPart( - contentPart: LanguageModelV2ToolResultPart, + contentPart: + | LanguageModelV3ToolResultPart + | LanguageModelV3ToolApprovalResponsePart, ): string { - switch (contentPart.output.type) { - case 'error-text': - case 'text': - return contentPart.output.value - case 'error-json': - case 'json': - return JSON.stringify(contentPart.output.value) - case 'content': - return contentPart.output.value - .filter((c) => c.type === 'text') - .map((c) => c.text) - .join('') + if (contentPart.type === 'tool-result') { + switch (contentPart.output.type) { + case 'error-text': + case 'text': + return contentPart.output.value + case 'error-json': + case 'json': + return JSON.stringify(contentPart.output.value) + case 'execution-denied': + return contentPart.output.reason ?? 'Execution denied' + case 'content': + return contentPart.output.value + .filter((c) => c.type === 'text') + .map((c) => c.text) + .join('') + } + } else { + return 'TODO: implement tool approval flow' } } export function handleToolMessage( - message: Extract, + message: Extract, ): Array { return message.content.map((c) => ({ role: 'tool', content: handleToolContentPart(c), - tool_call_id: c.toolCallId, + + // TODO: implement tool approval flow + tool_call_id: c.type === 'tool-result' ? c.toolCallId : undefined, })) } diff --git a/src/messages/handle-user-message.ts b/src/messages/handle-user-message.ts index f1e7a31..44c543f 100644 --- a/src/messages/handle-user-message.ts +++ b/src/messages/handle-user-message.ts @@ -1,6 +1,6 @@ import type { - LanguageModelV2FilePart, - LanguageModelV2Message, + LanguageModelV3FilePart, + LanguageModelV3Message, } from '@ai-sdk/provider' import type { RequestUserObjectContentParts, @@ -22,7 +22,7 @@ function toBase64DataUrl(base64Data: string, mediaType: string): string { function transformUrl({ data, mediaType, -}: LanguageModelV2FilePart): [string, boolean] { +}: LanguageModelV3FilePart): [string, boolean] { if (data instanceof Uint8Array) { const base64 = Buffer.from(data).toString('base64') return [toBase64DataUrl(base64, mediaType), true] @@ -46,7 +46,7 @@ function transformUrl({ } function handleUserFileImageMessage( - part: LanguageModelV2FilePart, + part: LanguageModelV3FilePart, ): RequestyImagePart { const [url, isDataUrl] = transformUrl(part) return { @@ -60,7 +60,7 @@ function handleUserFileImageMessage( } function handleUserFilePDFMessage( - part: LanguageModelV2FilePart, + part: LanguageModelV3FilePart, ): RequestyFilePart { const [url] = transformUrl(part) return { @@ -72,7 +72,7 @@ function handleUserFilePDFMessage( } function handleUserFileMessage( - part: LanguageModelV2FilePart, + part: LanguageModelV3FilePart, ): RequestyDocumentPart { if (part.mediaType.startsWith(IMAGE_MEDIA_TYPE)) { return handleUserFileImageMessage(part) @@ -84,7 +84,7 @@ function handleUserFileMessage( } export function handleUserMessage( - message: Extract, + message: Extract, ): RequestyChatMessage { const includesImages = message.content.some((c) => c.type === 'file') if (!includesImages) { diff --git a/src/messages/index.ts b/src/messages/index.ts index a8d1d23..3163c20 100644 --- a/src/messages/index.ts +++ b/src/messages/index.ts @@ -1,6 +1,6 @@ import type { - LanguageModelV2Message, - LanguageModelV2Prompt, + LanguageModelV3Message, + LanguageModelV3Prompt, } from '@ai-sdk/provider' import type { RequestyChatMessage, RequestyChatPrompt } from '../types' import { handleAssistantMessage } from './handle-assistant-message' @@ -9,7 +9,7 @@ import { handleToolMessage } from './handle-tool-message' import { handleUserMessage } from './handle-user-message' function handleMessage( - message: LanguageModelV2Message, + message: LanguageModelV3Message, ): Array { switch (message.role) { case 'system': @@ -24,7 +24,7 @@ function handleMessage( } export function convertToRequestyChatMessages( - prompt: LanguageModelV2Prompt, + prompt: LanguageModelV3Prompt, ): RequestyChatPrompt { return prompt.flatMap(handleMessage) } diff --git a/src/requesty-chat-language-model.ts b/src/requesty-chat-language-model.ts index f4dd16c..e08f3a7 100644 --- a/src/requesty-chat-language-model.ts +++ b/src/requesty-chat-language-model.ts @@ -1,17 +1,17 @@ import type { - LanguageModelV2, - LanguageModelV2CallOptions, - LanguageModelV2CallWarning, - LanguageModelV2Content, - LanguageModelV2FinishReason, - LanguageModelV2FunctionTool, - LanguageModelV2ProviderDefinedTool, - LanguageModelV2ResponseMetadata, - LanguageModelV2StreamPart, - LanguageModelV2Usage, - SharedV2Headers, - SharedV2ProviderMetadata, - SharedV2ProviderOptions, + LanguageModelV3, + LanguageModelV3CallOptions, + LanguageModelV3Content, + LanguageModelV3FinishReason, + LanguageModelV3FunctionTool, + LanguageModelV3ProviderTool, + LanguageModelV3ResponseMetadata, + LanguageModelV3StreamPart, + LanguageModelV3Usage, + SharedV3Headers, + SharedV3ProviderMetadata, + SharedV3ProviderOptions, + SharedV3Warning, } from '@ai-sdk/provider' import { UnsupportedFunctionalityError } from '@ai-sdk/provider' import type { ParseResult } from '@ai-sdk/provider-utils' @@ -31,6 +31,8 @@ import type { } from './requesty-chat-settings' import { requestyFailedResponseHandler } from './requesty-error' import { createStreamMethods } from './stream' +import { adaptUsage } from './usage' +import { RequestyUsageSchema } from './usage/schema' import { maybeSetReasoningContent } from './util' type RequestyChatConfig = { @@ -45,8 +47,8 @@ type RequestyChatConfig = { const URL_REGEX = /^https?:\/\/.*$/ -export class RequestyChatLanguageModel implements LanguageModelV2 { - readonly specificationVersion = 'v2' +export class RequestyChatLanguageModel implements LanguageModelV3 { + readonly specificationVersion = 'v3' readonly provider: string readonly modelId: RequestyChatModelId @@ -87,7 +89,7 @@ export class RequestyChatLanguageModel implements LanguageModelV2 { tools, toolChoice, providerOptions, - }: LanguageModelV2CallOptions) { + }: LanguageModelV3CallOptions) { // Extract requesty metadata from providerOptions and put it at root level const requestyMetadata = providerOptions?.['requesty'] ?? {} const extraCallingBody: Record = {} @@ -166,20 +168,20 @@ export class RequestyChatLanguageModel implements LanguageModelV2 { return baseArgs } - async doGenerate(options: LanguageModelV2CallOptions): Promise<{ - content: Array - finishReason: LanguageModelV2FinishReason - usage: LanguageModelV2Usage - providerMetadata?: SharedV2ProviderMetadata + async doGenerate(options: LanguageModelV3CallOptions): Promise<{ + content: Array + finishReason: LanguageModelV3FinishReason + usage: LanguageModelV3Usage + providerMetadata?: SharedV3ProviderMetadata request?: { body?: unknown } - response?: LanguageModelV2ResponseMetadata & { - headers?: SharedV2Headers + response?: LanguageModelV3ResponseMetadata & { + headers?: SharedV3Headers body?: unknown } - warnings: Array - providerOptions?: SharedV2ProviderOptions + providerOptions?: SharedV3ProviderOptions + warnings: Array }> { const args = this.getArgs(options) @@ -217,11 +219,11 @@ export class RequestyChatLanguageModel implements LanguageModelV2 { .cached_tokens ?? 0, }, }, - } satisfies SharedV2ProviderMetadata) + } satisfies SharedV3ProviderMetadata) : undefined // Convert to content format - const content: Array = [] + const content: Array = [] // Add text content if (choice.message.content) { @@ -242,7 +244,7 @@ export class RequestyChatLanguageModel implements LanguageModelV2 { // Add tool calls if (choice.message.tool_calls) { for (const toolCall of choice.message.tool_calls) { - const toolCallContent: LanguageModelV2Content = { + const toolCallContent: LanguageModelV3Content = { type: 'tool-call', toolCallId: toolCall.id ?? generateId(), toolName: toolCall.function.name, @@ -262,12 +264,10 @@ export class RequestyChatLanguageModel implements LanguageModelV2 { ReturnType > = { content, - finishReason: mapRequestyFinishReason(choice.finish_reason), - usage: { - inputTokens: response.usage?.prompt_tokens ?? 0, - outputTokens: response.usage?.completion_tokens ?? 0, - totalTokens: response.usage?.total_tokens ?? 0, - }, + finishReason: mapRequestyFinishReason( + choice.finish_reason ?? undefined, + ), + usage: adaptUsage(response.usage), ...(providerMetadata ? { providerMetadata } : {}), request: { body: rawSettings }, response: { @@ -282,13 +282,13 @@ export class RequestyChatLanguageModel implements LanguageModelV2 { return res } - async doStream(options: LanguageModelV2CallOptions): Promise<{ - stream: ReadableStream + async doStream(options: LanguageModelV3CallOptions): Promise<{ + stream: ReadableStream request?: { body?: unknown } response?: { - headers?: SharedV2Headers + headers?: SharedV3Headers } }> { const args = this.getArgs(options) @@ -327,7 +327,7 @@ export class RequestyChatLanguageModel implements LanguageModelV2 { ParseResult< z.infer >, - LanguageModelV2StreamPart + LanguageModelV3StreamPart >({ transform, flush, @@ -366,19 +366,7 @@ const RequestyNonStreamChatCompletionResponseSchema = z.object({ logprobs: z.any().optional(), }), ), - usage: z - .object({ - prompt_tokens: z.number().optional(), - completion_tokens: z.number().optional(), - total_tokens: z.number().optional(), - prompt_tokens_details: z - .object({ - caching_tokens: z.number().optional(), - cached_tokens: z.number().optional(), - }) - .optional(), - }) - .optional(), + usage: RequestyUsageSchema, }) export const RequestyStreamChatCompletionToolSchema = z @@ -416,19 +404,7 @@ export const RequestyStreamChatCompletionChunkSchema = z.object({ }), ) .optional(), - usage: z - .object({ - prompt_tokens: z.number().optional(), - completion_tokens: z.number().optional(), - total_tokens: z.number().optional(), - prompt_tokens_details: z - .object({ - caching_tokens: z.number().optional(), - cached_tokens: z.number().optional(), - }) - .optional(), - }) - .optional(), + usage: RequestyUsageSchema, error: z.any().optional(), }) @@ -436,10 +412,8 @@ function prepareToolsAndToolChoice({ tools, toolChoice, }: { - tools?: Array< - LanguageModelV2FunctionTool | LanguageModelV2ProviderDefinedTool - > - toolChoice?: LanguageModelV2CallOptions['toolChoice'] + tools?: Array + toolChoice?: LanguageModelV3CallOptions['toolChoice'] }) { // when the tools array is empty, change it to undefined to prevent errors: const validTools = tools?.length ? tools : undefined diff --git a/src/requesty-completion-language-model.ts b/src/requesty-completion-language-model.ts deleted file mode 100644 index 52ba41b..0000000 --- a/src/requesty-completion-language-model.ts +++ /dev/null @@ -1,324 +0,0 @@ -import type { - LanguageModelV2, - LanguageModelV2CallOptions, - LanguageModelV2CallWarning, - LanguageModelV2Content, - LanguageModelV2FinishReason, - LanguageModelV2ResponseMetadata, - LanguageModelV2StreamPart, - LanguageModelV2Usage, - SharedV2Headers, - SharedV2ProviderMetadata, -} from '@ai-sdk/provider' -import type { ParseResult } from '@ai-sdk/provider-utils' -import { - combineHeaders, - createEventSourceResponseHandler, - createJsonResponseHandler, - postJsonToApi, -} from '@ai-sdk/provider-utils' -import { z } from 'zod' -import { convertToRequestyCompletionPrompt } from './completions' -import { mapRequestyFinishReason } from './map-requesty-finish-reason' -import type { - RequestyCompletionModelId, - RequestyCompletionSettings, -} from './requesty-completion-settings' -import { requestyFailedResponseHandler } from './requesty-error' - -type RequestyCompletionConfig = { - provider: string - compatibility: 'strict' | 'compatible' - headers: () => Record - url: (options: { modelId: string; path: string }) => string - fetch?: typeof fetch -} - -export class RequestyCompletionLanguageModel implements LanguageModelV2 { - readonly specificationVersion = 'v2' - readonly provider: string - readonly modelId: RequestyCompletionModelId - readonly supportedUrls = {} - - readonly settings: RequestyCompletionSettings - private readonly config: RequestyCompletionConfig - - constructor( - modelId: RequestyCompletionModelId, - settings: RequestyCompletionSettings, - config: RequestyCompletionConfig, - ) { - this.modelId = modelId - this.settings = settings - this.config = config - this.provider = config.provider - } - - private getArgs({ - prompt, - maxOutputTokens, - temperature, - topP, - frequencyPenalty, - presencePenalty, - seed, - responseFormat, - topK, - stopSequences, - providerOptions, - }: LanguageModelV2CallOptions) { - const extraCallingBody = providerOptions?.requesty ?? {} - - return { - model: this.modelId, - - logit_bias: this.settings.logitBias, - logprobs: this.settings.logprobs, - suffix: this.settings.suffix, - user: this.settings.user, - - max_tokens: maxOutputTokens, - temperature, - top_p: topP, - frequency_penalty: frequencyPenalty, - presence_penalty: presencePenalty, - seed, - - stop: stopSequences, - response_format: responseFormat, - top_k: topK, - - prompt: convertToRequestyCompletionPrompt(prompt), - - ...extraCallingBody, - } - } - - async doGenerate(options: LanguageModelV2CallOptions): Promise<{ - content: Array - finishReason: LanguageModelV2FinishReason - usage: LanguageModelV2Usage - providerMetadata?: SharedV2ProviderMetadata - request?: { - body?: unknown - } - response?: LanguageModelV2ResponseMetadata & { - headers?: SharedV2Headers - body?: unknown - } - warnings: Array - }> { - const args = this.getArgs(options) - - const { responseHeaders, value: response } = await postJsonToApi({ - url: this.config.url({ - path: '/completions', - modelId: this.modelId, - }), - headers: combineHeaders(this.config.headers(), options.headers), - body: args, - failedResponseHandler: requestyFailedResponseHandler, - successfulResponseHandler: createJsonResponseHandler( - RequestyCompletionResponseSchema, - ), - abortSignal: options.abortSignal, - fetch: this.config.fetch, - }) - - const rawSettings = args - const choice = response.choices[0] - - if (!choice) { - throw new Error('No choice in response') - } - - const content: Array = [ - { - type: 'text', - text: choice.text, - }, - ] - - return { - content, - finishReason: mapRequestyFinishReason(choice.finish_reason), - usage: { - inputTokens: response.usage?.prompt_tokens ?? 0, - outputTokens: response.usage?.completion_tokens ?? 0, - totalTokens: response.usage?.total_tokens ?? 0, - }, - request: { body: rawSettings }, - response: { - id: response.id ?? '', - modelId: response.model ?? '', - headers: responseHeaders, - body: response, - }, - warnings: [], - } - } - - async doStream(options: LanguageModelV2CallOptions): Promise<{ - stream: ReadableStream - request?: { - body?: unknown - } - response?: { - headers?: SharedV2Headers - } - }> { - const args = this.getArgs(options) - - const { responseHeaders, value: response } = await postJsonToApi({ - url: this.config.url({ - path: '/completions', - modelId: this.modelId, - }), - headers: combineHeaders(this.config.headers(), options.headers), - body: { - ...args, - stream: true, - }, - failedResponseHandler: requestyFailedResponseHandler, - successfulResponseHandler: createEventSourceResponseHandler( - RequestyCompletionStreamChunkSchema, - ), - abortSignal: options.abortSignal, - fetch: this.config.fetch, - }) - - const { prompt: rawPrompt, ...rawSettings } = args - - let finishReason: LanguageModelV2FinishReason = 'other' - let usage: LanguageModelV2Usage = { - inputTokens: 0, - outputTokens: 0, - totalTokens: 0, - } - - return { - stream: response.pipeThrough( - new TransformStream< - ParseResult< - z.infer - >, - LanguageModelV2StreamPart - >({ - transform(chunk, controller) { - // handle failed chunk parsing / validation: - if (!chunk.success) { - finishReason = 'error' - controller.enqueue({ - type: 'error', - error: (chunk as any).error, - }) - return - } - - const value = chunk.value - - // handle error chunks: - if ('error' in value) { - finishReason = 'error' - controller.enqueue({ - type: 'error', - error: value.error, - }) - return - } - - if (value.id) { - controller.enqueue({ - type: 'response-metadata', - id: value.id, - }) - } - - if (value.model) { - controller.enqueue({ - type: 'response-metadata', - modelId: value.model, - }) - } - - if (value.usage != null) { - usage = { - inputTokens: value.usage.prompt_tokens ?? 0, - outputTokens: - value.usage.completion_tokens ?? 0, - totalTokens: value.usage.total_tokens ?? 0, - } - } - - const choice = value.choices?.[0] - - if (choice?.finish_reason != null) { - finishReason = mapRequestyFinishReason( - choice.finish_reason, - ) - } - - if (choice?.text != null) { - controller.enqueue({ - type: 'text-delta', - id: 'completion', - delta: choice.text, - }) - } - }, - - flush(controller) { - controller.enqueue({ - type: 'finish', - finishReason, - usage, - }) - }, - }), - ), - request: { body: rawSettings }, - response: { headers: responseHeaders }, - } - } -} - -const RequestyCompletionResponseSchema = z.object({ - id: z.string().optional(), - model: z.string().optional(), - choices: z.array( - z.object({ - text: z.string(), - finish_reason: z.string().nullable(), - logprobs: z.any().optional(), - }), - ), - usage: z - .object({ - prompt_tokens: z.number().optional(), - completion_tokens: z.number().optional(), - total_tokens: z.number().optional(), - }) - .optional(), -}) - -const RequestyCompletionStreamChunkSchema = z.object({ - id: z.string().optional(), - model: z.string().optional(), - choices: z - .array( - z.object({ - text: z.string().optional(), - finish_reason: z.string().nullable().optional(), - logprobs: z.any().optional(), - }), - ) - .optional(), - usage: z - .object({ - prompt_tokens: z.number().optional(), - completion_tokens: z.number().optional(), - total_tokens: z.number().optional(), - }) - .optional(), - error: z.any().optional(), -}) diff --git a/src/requesty-completion-settings.ts b/src/requesty-completion-settings.ts deleted file mode 100644 index 451adda..0000000 --- a/src/requesty-completion-settings.ts +++ /dev/null @@ -1,39 +0,0 @@ -import type { RequestySharedSettings } from './types' - -export type RequestyCompletionModelId = string - -export type RequestyCompletionSettings = { - /** -Modify the likelihood of specified tokens appearing in the completion. - -Accepts a JSON object that maps tokens (specified by their token ID in -the GPT tokenizer) to an associated bias value from -100 to 100. You -can use this tokenizer tool to convert text to token IDs. Mathematically, -the bias is added to the logits generated by the model prior to sampling. -The exact effect will vary per model, but values between -1 and 1 should -decrease or increase likelihood of selection; values like -100 or 100 -should result in a ban or exclusive selection of the relevant token. - -As an example, you can pass {"50256": -100} to prevent the <|endoftext|> -token from being generated. - */ - logitBias?: Record - - /** -Return the log probabilities of the tokens. Including logprobs will increase -the response size and can slow down response times. However, it can -be useful to better understand how the model is behaving. - -Setting to true will return the log probabilities of the tokens that -were generated. - -Setting to a number will return the log probabilities of the top n -tokens that were generated. - */ - logprobs?: boolean | number - - /** -The suffix that comes after a completion of inserted text. - */ - suffix?: string -} & RequestySharedSettings diff --git a/src/requesty-provider.ts b/src/requesty-provider.ts index 76c27c9..77a9d12 100644 --- a/src/requesty-provider.ts +++ b/src/requesty-provider.ts @@ -4,48 +4,22 @@ import type { RequestyChatModelId, RequestyChatSettings, } from './requesty-chat-settings' -import { RequestyCompletionLanguageModel } from './requesty-completion-language-model' -import type { - RequestyCompletionModelId, - RequestyCompletionSettings, -} from './requesty-completion-settings' - -export type { RequestyCompletionSettings } export interface RequestyProvider { - ( - modelId: RequestyChatModelId, - settings?: RequestyCompletionSettings, - ): RequestyCompletionLanguageModel ( modelId: RequestyChatModelId, settings?: RequestyChatSettings, ): RequestyChatLanguageModel - languageModel( - modelId: RequestyChatModelId, - settings?: RequestyCompletionSettings, - ): RequestyCompletionLanguageModel languageModel( modelId: RequestyChatModelId, settings?: RequestyChatSettings, ): RequestyChatLanguageModel - /** -Creates an Requesty chat model for text generation. - */ chat( modelId: RequestyChatModelId, settings?: RequestyChatSettings, ): RequestyChatLanguageModel - - /** -Creates an Requesty completion model for text generation. - */ - completion( - modelId: RequestyCompletionModelId, - settings?: RequestyCompletionSettings, - ): RequestyCompletionLanguageModel } export interface RequestyProviderSettings { @@ -123,21 +97,9 @@ export function createRequesty( extraBody: options.extraBody, }) - const createCompletionModel = ( - modelId: RequestyCompletionModelId, - settings: RequestyCompletionSettings = {}, - ) => - new RequestyCompletionLanguageModel(modelId, settings, { - provider: 'requesty.completion', - url: ({ path }) => `${baseURL}${path}`, - headers: getHeaders, - compatibility, - fetch: options.fetch, - }) - const createLanguageModel = ( - modelId: RequestyChatModelId | RequestyCompletionModelId, - settings?: RequestyChatSettings | RequestyCompletionSettings, + modelId: RequestyChatModelId, + settings?: RequestyChatSettings, ) => { if (new.target) { throw new Error( @@ -145,24 +107,15 @@ export function createRequesty( ) } - if (modelId === 'openai/gpt-3.5-turbo-instruct') { - return createCompletionModel( - modelId, - settings as RequestyCompletionSettings, - ) - } - return createChatModel(modelId, settings as RequestyChatSettings) } const provider = ( - modelId: RequestyChatModelId | RequestyCompletionModelId, - settings?: RequestyChatSettings | RequestyCompletionSettings, + modelId: RequestyChatModelId, + settings?: RequestyChatSettings, ) => createLanguageModel(modelId, settings) - provider.languageModel = createLanguageModel provider.chat = createChatModel - provider.completion = createCompletionModel return provider as RequestyProvider } diff --git a/src/stream/index.test.ts b/src/stream/index.test.ts index 74ca030..a5dddb9 100644 --- a/src/stream/index.test.ts +++ b/src/stream/index.test.ts @@ -36,7 +36,10 @@ describe('stream', () => { controller, ) - expect(finishReason.set).toHaveBeenCalledWith('error') + expect(finishReason.set).toHaveBeenCalledWith({ + unified: 'error', + raw: 'error', + }) expect(enqueuedParts).toHaveLength(1) expect(enqueuedParts[0]).toEqual({ type: 'error', @@ -78,7 +81,10 @@ describe('stream', () => { controller, ) - expect(finishReason.set).toHaveBeenCalledWith('error') + expect(finishReason.set).toHaveBeenCalledWith({ + unified: 'error', + raw: 'API error occurred', + }) expect(enqueuedParts).toHaveLength(1) expect(enqueuedParts[0]).toEqual({ type: 'error', @@ -211,9 +217,13 @@ describe('stream', () => { ) expect(usage.set).toHaveBeenCalledWith({ - inputTokens: 10, - outputTokens: 20, - totalTokens: 30, + inputTokens: { + total: 10, + noCache: 10, + cacheRead: 0, + cacheWrite: 5, + }, + outputTokens: { total: 20, reasoning: 0, text: 20 }, }) expect(requestyUsage.set).toHaveBeenCalledWith({ cachingTokens: 5, @@ -478,9 +488,13 @@ describe('stream', () => { ) expect(usage.set).toHaveBeenCalledWith({ - inputTokens: 0, - outputTokens: 0, - totalTokens: 0, + inputTokens: { + total: 0, + noCache: 0, + cacheRead: 0, + cacheWrite: 0, + }, + outputTokens: { total: 0, reasoning: 0, text: 0 }, }) expect(requestyUsage.set).toHaveBeenCalledWith({ cachingTokens: 0, @@ -557,7 +571,10 @@ describe('stream', () => { controller, ) - expect(finishReason.set).toHaveBeenCalledWith('stop') + expect(finishReason.set).toHaveBeenCalledWith({ + unified: 'stop', + raw: 'stop', + }) }) it('maps finish_reason "length" to "length"', () => { @@ -596,46 +613,10 @@ describe('stream', () => { controller, ) - expect(finishReason.set).toHaveBeenCalledWith('length') - }) - - it('maps finish_reason "max_tokens" to "length"', () => { - const finishReason = { get: vi.fn(), set: vi.fn() } - const usage = { get: vi.fn(), set: vi.fn() } - const requestyUsage = { get: vi.fn(), set: vi.fn() } - const activeId = { get: vi.fn(() => undefined), set: vi.fn() } - const reasoningId = { get: vi.fn(), set: vi.fn() } - const existingToolCalls = { get: vi.fn(() => []), set: vi.fn() } - - const transform = createTransform({ - finishReason, - usage, - requestyUsage, - activeId, - reasoningId, - existingToolCalls, + expect(finishReason.set).toHaveBeenCalledWith({ + unified: 'length', + raw: 'length', }) - - const controller = { - enqueue: vi.fn(), - } as any - - transform( - { - success: true, - value: { - choices: [ - { - finish_reason: 'max_tokens', - delta: {}, - }, - ], - }, - } as any, - controller, - ) - - expect(finishReason.set).toHaveBeenCalledWith('length') }) it('maps finish_reason "tool_calls" to "tool-calls"', () => { @@ -674,7 +655,10 @@ describe('stream', () => { controller, ) - expect(finishReason.set).toHaveBeenCalledWith('tool-calls') + expect(finishReason.set).toHaveBeenCalledWith({ + unified: 'tool-calls', + raw: 'tool_calls', + }) }) it('maps finish_reason "function_call" to "tool-calls"', () => { @@ -713,7 +697,10 @@ describe('stream', () => { controller, ) - expect(finishReason.set).toHaveBeenCalledWith('tool-calls') + expect(finishReason.set).toHaveBeenCalledWith({ + unified: 'tool-calls', + raw: 'function_call', + }) }) it('maps finish_reason "content_filter" to "content-filter"', () => { @@ -752,7 +739,10 @@ describe('stream', () => { controller, ) - expect(finishReason.set).toHaveBeenCalledWith('content-filter') + expect(finishReason.set).toHaveBeenCalledWith({ + unified: 'content-filter', + raw: 'content_filter', + }) }) it('maps unknown finish_reason to "unknown"', () => { @@ -791,7 +781,10 @@ describe('stream', () => { controller, ) - expect(finishReason.set).toHaveBeenCalledWith('unknown') + expect(finishReason.set).toHaveBeenCalledWith({ + unified: 'other', + raw: 'some_unknown_reason', + }) }) it('does not set finish_reason when it is null', () => { diff --git a/src/stream/index.ts b/src/stream/index.ts index 4e0862f..169a68c 100644 --- a/src/stream/index.ts +++ b/src/stream/index.ts @@ -1,8 +1,8 @@ import type { - LanguageModelV2FinishReason, - LanguageModelV2StreamPart, - LanguageModelV2Usage, - SharedV2ProviderMetadata, + LanguageModelV3FinishReason, + LanguageModelV3StreamPart, + LanguageModelV3Usage, + SharedV3ProviderMetadata, } from '@ai-sdk/provider' import { generateId, @@ -16,6 +16,7 @@ import type { RequestyStreamChatCompletionToolSchema, } from '../requesty-chat-language-model' import type { RequestyUsage } from '../types' +import { adaptUsage } from '../usage' import { assertDefined, maybeSetReasoningContent } from '../util' type ChunkType = z.infer @@ -27,15 +28,13 @@ type DefinedToolCall = NonNullable[number] function handleUsageChunk( usage: NonNullable, -): [LanguageModelV2Usage, Partial] { - const modelUsage: LanguageModelV2Usage = { - inputTokens: usage.prompt_tokens ?? 0, - outputTokens: usage.completion_tokens ?? 0, - totalTokens: usage.total_tokens ?? 0, - } +): [LanguageModelV3Usage, Partial] { + const cacheReadTokens = usage.prompt_tokens_details?.caching_tokens ?? 0 + + const modelUsage = adaptUsage(usage) const requestyUsage: Partial = { - cachingTokens: usage.prompt_tokens_details?.caching_tokens ?? 0, + cachingTokens: cacheReadTokens, } return [modelUsage, requestyUsage] @@ -44,10 +43,10 @@ function handleUsageChunk( type CreateStreamReturn = { transform: ( chunk: Chunk, - controller: TransformStreamDefaultController, + controller: TransformStreamDefaultController, ) => void flush: ( - controller: TransformStreamDefaultController, + controller: TransformStreamDefaultController, ) => void } @@ -68,8 +67,8 @@ const createValue = (startingValue: T): Value => { } type CreateTransformMethods = { - finishReason: Value - usage: Value + finishReason: Value + usage: Value requestyUsage: Value> activeId: Value reasoningId: Value @@ -86,7 +85,7 @@ export const createTransform = ({ }: CreateTransformMethods): CreateStreamReturn['transform'] => { return (chunk, controller) => { if (!chunk.success) { - finishReason.set('error') + finishReason.set({ unified: 'error', raw: 'error' }) controller.enqueue({ type: 'error', error: chunk.error.message, @@ -96,7 +95,7 @@ export const createTransform = ({ const { value } = chunk if (value.error) { - finishReason.set('error') + finishReason.set({ unified: 'error', raw: value.error }) controller.enqueue({ type: 'error', error: value.error, @@ -257,7 +256,7 @@ export const createTransform = ({ id: currentToolCall.id, }) - const toolStartPart: LanguageModelV2StreamPart = { + const toolStartPart: LanguageModelV3StreamPart = { type: 'tool-call', toolCallId: currentToolCall.id, input: currentToolCall.function.arguments, @@ -304,7 +303,7 @@ export const createFlush = ({ } const currentRequestyUsage = requestyUsage.get() - const providerMetadata: SharedV2ProviderMetadata = { + const providerMetadata: SharedV3ProviderMetadata = { requesty: { usage: currentRequestyUsage.cachedTokens && @@ -330,11 +329,15 @@ export const createFlush = ({ export const createStreamMethods = (): CreateStreamReturn => { const activeId = createValue(undefined) const reasoningId = createValue(undefined) - const finishReason = createValue('other') - const usage = createValue({ - inputTokens: 0, - outputTokens: 0, - totalTokens: 0, + + const finishReason = createValue({ + unified: 'other', + raw: '', + }) + + const usage = createValue({ + inputTokens: { total: 0, noCache: 0, cacheRead: 0, cacheWrite: 0 }, + outputTokens: { total: 0, reasoning: 0, text: 0 }, }) const requestyUsage = createValue>({}) const existingToolCalls = createValue([]) diff --git a/src/usage/adapt.test.ts b/src/usage/adapt.test.ts new file mode 100644 index 0000000..40b778b --- /dev/null +++ b/src/usage/adapt.test.ts @@ -0,0 +1,192 @@ +import { describe, expect, it } from 'vitest' +import type { z } from 'zod' + +import { adaptUsage } from './adapt' +import type { RequestyUsageSchema } from './schema' + +describe('getUsage', () => { + it('converts requesty usage with all fields populated', () => { + const requestyUsage: z.infer = { + prompt_tokens: 100, + completion_tokens: 50, + total_tokens: 150, + prompt_tokens_details: { + cached_tokens: 20, + caching_tokens: 10, + }, + } + + const result = adaptUsage(requestyUsage) + + expect(result).toStrictEqual({ + inputTokens: { + total: 100, + cacheRead: 20, + cacheWrite: 10, + noCache: 80, + }, + outputTokens: { + total: 50, + text: 50, + reasoning: 0, + }, + }) + }) + + it('handles usage with no cache details', () => { + const requestyUsage: z.infer = { + prompt_tokens: 100, + completion_tokens: 50, + } + + const result = adaptUsage(requestyUsage) + + expect(result).toStrictEqual({ + inputTokens: { + total: 100, + cacheRead: 0, + cacheWrite: 0, + noCache: 100, + }, + outputTokens: { + total: 50, + text: 50, + reasoning: 0, + }, + }) + }) + + it('handles undefined usage', () => { + const requestyUsage: z.infer = undefined + + const result = adaptUsage(requestyUsage) + + expect(result).toStrictEqual({ + inputTokens: { + total: 0, + cacheRead: 0, + cacheWrite: 0, + noCache: 0, + }, + outputTokens: { + total: 0, + text: 0, + reasoning: 0, + }, + }) + }) + + it('handles empty object', () => { + const requestyUsage: z.infer = {} + + const result = adaptUsage(requestyUsage) + + expect(result).toStrictEqual({ + inputTokens: { + total: 0, + cacheRead: 0, + cacheWrite: 0, + noCache: 0, + }, + outputTokens: { + total: 0, + text: 0, + reasoning: 0, + }, + }) + }) + + it('handles partial cache details', () => { + const requestyUsage: z.infer = { + prompt_tokens: 100, + completion_tokens: 50, + prompt_tokens_details: { + cached_tokens: 20, + }, + } + + const result = adaptUsage(requestyUsage) + + expect(result).toStrictEqual({ + inputTokens: { + total: 100, + cacheRead: 20, + cacheWrite: 0, + noCache: 80, + }, + outputTokens: { + total: 50, + text: 50, + reasoning: 0, + }, + }) + }) + + it('calculates noCache correctly when cached tokens equal prompt tokens', () => { + const requestyUsage: z.infer = { + prompt_tokens: 100, + completion_tokens: 50, + prompt_tokens_details: { + cached_tokens: 100, + }, + } + + const result = adaptUsage(requestyUsage) + + expect(result.inputTokens.noCache).toBe(0) + }) + + it('handles zero values', () => { + const requestyUsage: z.infer = { + prompt_tokens: 0, + completion_tokens: 0, + prompt_tokens_details: { + cached_tokens: 0, + caching_tokens: 0, + }, + } + + const result = adaptUsage(requestyUsage) + + expect(result).toStrictEqual({ + inputTokens: { + total: 0, + cacheRead: 0, + cacheWrite: 0, + noCache: 0, + }, + outputTokens: { + total: 0, + text: 0, + reasoning: 0, + }, + }) + }) + + it('handles large token counts', () => { + const requestyUsage: z.infer = { + prompt_tokens: 1000000, + completion_tokens: 500000, + prompt_tokens_details: { + cached_tokens: 200000, + caching_tokens: 100000, + }, + } + + const result = adaptUsage(requestyUsage) + + expect(result).toStrictEqual({ + inputTokens: { + total: 1000000, + cacheRead: 200000, + cacheWrite: 100000, + noCache: 800000, + }, + outputTokens: { + total: 500000, + text: 500000, + reasoning: 0, + }, + }) + }) +}) diff --git a/src/usage/adapt.ts b/src/usage/adapt.ts new file mode 100644 index 0000000..e4350d4 --- /dev/null +++ b/src/usage/adapt.ts @@ -0,0 +1,31 @@ +import type { LanguageModelV3Usage } from '@ai-sdk/provider' +import type { z } from 'zod' +import type { RequestyUsageSchema } from './schema' + +export const adaptUsage = ( + requestyUsage: z.infer, +): LanguageModelV3Usage => { + const promptTokens = requestyUsage?.prompt_tokens ?? 0 + const cachedTokens = + requestyUsage?.prompt_tokens_details?.cached_tokens ?? 0 + const cachingTokens = + requestyUsage?.prompt_tokens_details?.caching_tokens ?? 0 + + const completionTokens = requestyUsage?.completion_tokens ?? 0 + + return { + inputTokens: { + total: promptTokens, + cacheRead: cachedTokens, + cacheWrite: cachingTokens, + noCache: promptTokens - cachedTokens, + }, + outputTokens: { + total: completionTokens, + text: completionTokens, + + // TODO adapt reasoning usage + reasoning: 0, + }, + } +} diff --git a/src/usage/index.ts b/src/usage/index.ts new file mode 100644 index 0000000..bf64566 --- /dev/null +++ b/src/usage/index.ts @@ -0,0 +1,2 @@ +export * from './adapt' +export * from './schema' diff --git a/src/usage/schema.ts b/src/usage/schema.ts new file mode 100644 index 0000000..05dc546 --- /dev/null +++ b/src/usage/schema.ts @@ -0,0 +1,15 @@ +import z from 'zod' + +export const RequestyUsageSchema = z + .object({ + prompt_tokens: z.number().optional(), + completion_tokens: z.number().optional(), + total_tokens: z.number().optional(), + prompt_tokens_details: z + .object({ + caching_tokens: z.number().optional(), + cached_tokens: z.number().optional(), + }) + .optional(), + }) + .optional() diff --git a/src/util/set-provider-metadata.ts b/src/util/set-provider-metadata.ts index 339a184..511a43c 100644 --- a/src/util/set-provider-metadata.ts +++ b/src/util/set-provider-metadata.ts @@ -1,17 +1,17 @@ import type { - SharedV2ProviderMetadata, - SharedV2ProviderOptions, + SharedV3ProviderMetadata, + SharedV3ProviderOptions, } from '@ai-sdk/provider' const REQUESTY = 'requesty' const REASONING_SIGNATURE = 'reasoning_signature' type WithProviderOptions = { - providerOptions?: SharedV2ProviderOptions | undefined + providerOptions?: SharedV3ProviderOptions | undefined } type WithProviderMetadata = { - providerMetadata?: SharedV2ProviderMetadata | undefined + providerMetadata?: SharedV3ProviderMetadata | undefined } // we read `providerOptions` when converting to Requesty messages