diff --git a/.github/ISSUE_TEMPLATE/1.bug_report.yml b/.github/ISSUE_TEMPLATE/1.bug_report.yml deleted file mode 100644 index 6a27f1d21c61..000000000000 --- a/.github/ISSUE_TEMPLATE/1.bug_report.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Bug report -description: Report a bug with the AI SDK. -labels: ['bug'] -body: - - type: markdown - attributes: - value: | - This template is to report bugs. If you need help with your own project, feel free to [start a new thread in our discussions](https://github.com/vercel/ai/discussions). - - type: textarea - attributes: - label: Description - description: A detailed description of the issue that you are encountering with the AI SDK, and how other people can reproduce it. This includes helpful information such as the API you are using, the framework and AI provider. - placeholder: | - Reproduction steps... - validations: - required: true - - type: textarea - attributes: - label: Code example - description: Provide an example code snippet that has the problem. - placeholder: | - import { openai } from '@ai-sdk/openai'; - import { streamText } from 'ai'; - ... - - type: input - id: provider - attributes: - label: AI provider - description: The AI provider (e.g. `@ai-sdk/openai`) that you are using, and its version (e.g. `1.0.0`). - placeholder: | - @ai-sdk/openai v1.0.0 - - type: textarea - attributes: - label: Additional context - description: | - Any extra information that might help us investigate. diff --git a/.github/ISSUE_TEMPLATE/1.support_request.yml b/.github/ISSUE_TEMPLATE/1.support_request.yml new file mode 100644 index 000000000000..da34bde0a0ef --- /dev/null +++ b/.github/ISSUE_TEMPLATE/1.support_request.yml @@ -0,0 +1,35 @@ +name: Support Request +description: Report a bug, feature request or other issue with the AI SDK. +labels: ['support'] +body: + - type: markdown + attributes: + value: | + This template is ask for help regarding an issue that could be a bug or a feature request. + - type: textarea + attributes: + label: Description + description: A detailed description. Please include relevant information such as reproduction steps, code examples, and any other information that might help us understand the issue. + placeholder: | + Reproduction steps, code examples, background, etc... + validations: + required: true + - type: textarea + attributes: + label: AI SDK Version + description: Which version of the AI SDK are you using? + placeholder: | + Examples: + - ai: 4.1.2 + - @ai-sdk/react: 2.1.0 + - @ai-sdk/openai: 0.5.2 + validations: + required: false + - type: checkboxes + id: terms + attributes: + label: Code of Conduct + description: By submitting this issue, you agree to follow our [Code of Conduct](https://community.vercel.com/guidelines) + options: + - label: I agree to follow this project's Code of Conduct + required: true \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/2.feature_request.yml b/.github/ISSUE_TEMPLATE/2.feature_request.yml deleted file mode 100644 index 2c7355ea3698..000000000000 --- a/.github/ISSUE_TEMPLATE/2.feature_request.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Feature Request -description: Propose a new feature for the AI SDK. -labels: ['enhancement'] -body: - - type: markdown - attributes: - value: | - This template is to propose new features for the AI SDK. If you need help with your own project, feel free to [start a new thread in our discussions](https://github.com/vercel/ai/discussions). - - type: textarea - attributes: - label: Feature Description - description: A detailed description of the feature you are proposing for the SDK. - placeholder: | - Feature description... - validations: - required: true - - type: textarea - attributes: - label: Use Cases - description: Provide use cases where this feature would be beneficial. - placeholder: | - Use case... - - type: textarea - attributes: - label: Additional context - description: | - Any extra information that might help us understand your feature request. - placeholder: | - Additional context... diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000000..8dfa57b22180 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,52 @@ + + +## Background + + + +## Summary + + + +## Manual Verification + + + +## Tasks + + + +- [ ] Tests have been added / updated (for bug fixes / features) +- [ ] Documentation has been added / updated (for bug fixes / features) +- [ ] A _patch_ changeset for relevant packages has been added (for bug fixes / features - run `pnpm changeset` in the project root) +- [ ] Formatting issues have been fixed (run `pnpm prettier-fix` in the project root) +- [ ] I have reviewed this pull request (self-review) + +## Future Work + + + +## Related Issues + + diff --git a/.github/scripts/cleanup-examples-changesets.mjs b/.github/scripts/cleanup-examples-changesets.mjs index f728fb35a78e..0d132eefc6f5 100644 --- a/.github/scripts/cleanup-examples-changesets.mjs +++ b/.github/scripts/cleanup-examples-changesets.mjs @@ -46,5 +46,5 @@ for (const app of readdirSync(fileURLToPath(examplesUrl))) { // next test server cleanup( '.', - new URL('../../packages/ai/tests/e2e/next-server', import.meta.url), + new URL('../../packages/rsc/tests/e2e/next-server', import.meta.url), ); diff --git a/.github/workflows/actions/verify-changesets/index.js b/.github/workflows/actions/verify-changesets/index.js new file mode 100644 index 000000000000..fa21a3fb171b --- /dev/null +++ b/.github/workflows/actions/verify-changesets/index.js @@ -0,0 +1,137 @@ +import fs from 'node:fs/promises'; + +const BYPASS_LABELS = ['minor', 'major']; + +// check if current file is the entry point +if (import.meta.url.endsWith(process.argv[1])) { + // https://docs.github.com/en/webhooks/webhook-events-and-payloads#pull_request + const pullRequestEvent = JSON.parse( + await fs.readFile(process.env.GITHUB_EVENT_PATH, 'utf-8'), + ); + + try { + const message = await verifyChangesets( + pullRequestEvent, + process.env, + fs.readFile, + ); + await fs.writeFile( + process.env.GITHUB_STEP_SUMMARY, + `## Changeset verification passed ✅\n\n${message || ''}`, + ); + } catch (error) { + // write error to summary + console.error(error.message); + await fs.writeFile( + process.env.GITHUB_STEP_SUMMARY, + `## Changeset verification failed ❌ + +${error.message}`, + ); + + if (error.path) { + await fs.appendFile( + process.env.GITHUB_STEP_SUMMARY, + `\n\nFile: \`${error.path}\``, + ); + } + + if (error.content) { + await fs.appendFile( + process.env.GITHUB_STEP_SUMMARY, + `\n\n\`\`\`yaml\n${error.content}\n\`\`\``, + ); + } + + process.exit(1); + } +} + +export async function verifyChangesets( + event, + env = process.env, + readFile = fs.readFile, +) { + // Skip check if pull request has "minor-release" label + const byPassLabel = event.pull_request.labels.find(label => + BYPASS_LABELS.includes(label.name), + ); + if (byPassLabel) { + return `Skipping changeset verification - "${byPassLabel.name}" label found`; + } + + // Iterate through all changed .changeset/*.md files + for (const path of env.CHANGED_FILES.trim().split(' ')) { + // ignore README.md file + if (path === '.changeset/README.md') continue; + + // Check if the file is a .changeset file + if (!/^\.changeset\/[a-z-]+\.md/.test(path)) { + throw Object.assign(new Error(`Invalid file - not a .changeset file`), { + path, + }); + } + + // find frontmatter + const content = await readFile(`../../../../${path}`, 'utf-8'); + const result = content.match(/---\n([\s\S]+?)\n---/); + if (!result) { + throw Object.assign( + new Error(`Invalid .changeset file - no frontmatter found`), + { + path, + content, + }, + ); + } + + const [frontmatter] = result; + + // Find version bump by package. `frontmatter` looks like this: + // + // ```yaml + // 'ai': patch + // '@ai-sdk/provider': patch + // ``` + const lines = frontmatter.split('\n').slice(1, -1); + const versionBumps = {}; + for (const line of lines) { + const [packageName, versionBump] = line.split(':').map(s => s.trim()); + if (!packageName || !versionBump) { + throw Object.assign( + new Error(`Invalid .changeset file - invalid frontmatter`, { + path, + content, + }), + ); + } + + // Check if packageName is already set + if (versionBumps[packageName]) { + throw Object.assign( + new Error( + `Invalid .changeset file - duplicate package name "${packageName}"`, + ), + { path, content }, + ); + } + + versionBumps[packageName] = versionBump; + } + + // check if any of the version bumps are not "patch" + const invalidVersionBumps = Object.entries(versionBumps).filter( + ([, versionBump]) => versionBump !== 'patch', + ); + + if (invalidVersionBumps.length > 0) { + throw Object.assign( + new Error( + `Invalid .changeset file - invalid version bump (only "patch" is allowed, see https://ai-sdk.dev/docs/migration-guides/versioning). To bypass, add one of the following labels: ${BYPASS_LABELS.join(', ')}`, + ), + + { path, content }, + ); + } + } +} diff --git a/.github/workflows/actions/verify-changesets/package.json b/.github/workflows/actions/verify-changesets/package.json new file mode 100644 index 000000000000..bff806df20e3 --- /dev/null +++ b/.github/workflows/actions/verify-changesets/package.json @@ -0,0 +1,8 @@ +{ + "name": "verify-changesets-action", + "private": true, + "type": "module", + "scripts": { + "test": "node --test test.js" + } +} diff --git a/.github/workflows/actions/verify-changesets/test.js b/.github/workflows/actions/verify-changesets/test.js new file mode 100644 index 000000000000..0c4e024f38ef --- /dev/null +++ b/.github/workflows/actions/verify-changesets/test.js @@ -0,0 +1,193 @@ +import assert from 'node:assert'; +import { mock, test } from 'node:test'; + +import { verifyChangesets } from './index.js'; + +test('happy path', async () => { + const event = { + pull_request: { + labels: [], + }, + }; + const env = { + CHANGED_FILES: '.changeset/some-happy-path.md', + }; + + const readFile = mock.fn(async path => { + return `---\nai: patch\n@ai-sdk/provider: patch\n---\n## Test changeset`; + }); + + await verifyChangesets(event, env, readFile); + + assert.strictEqual(readFile.mock.callCount(), 1); + assert.deepStrictEqual(readFile.mock.calls[0].arguments, [ + '../../../../.changeset/some-happy-path.md', + 'utf-8', + ]); +}); + +test('ignores .changeset/README.md', async () => { + const event = { + pull_request: { + labels: [], + }, + }; + const env = { + CHANGED_FILES: '.changeset/README.md', + }; + + const readFile = mock.fn(() => {}); + + await verifyChangesets(event, env, readFile); + + assert.strictEqual(readFile.mock.callCount(), 0); +}); + +test('invalid file - not a .changeset file', async () => { + const event = { + pull_request: { + labels: [], + }, + }; + const env = { + CHANGED_FILES: '.changeset/not-a-changeset-file.txt', + }; + + const readFile = mock.fn(() => {}); + + await assert.rejects( + () => verifyChangesets(event, env, readFile), + Object.assign(new Error('Invalid file - not a .changeset file'), { + path: '.changeset/not-a-changeset-file.txt', + }), + ); + + assert.strictEqual(readFile.mock.callCount(), 0); +}); + +test('invalid .changeset file - no frontmatter', async () => { + const event = { + pull_request: { + labels: [], + }, + }; + const env = { + CHANGED_FILES: '.changeset/invalid-changeset-file.md', + }; + + const readFile = mock.fn(async path => { + return 'frontmatter missing'; + }); + await assert.rejects( + () => verifyChangesets(event, env, readFile), + Object.assign(new Error('Invalid .changeset file - no frontmatter found'), { + path: '.changeset/invalid-changeset-file.md', + content: 'frontmatter missing', + }), + ); + assert.strictEqual(readFile.mock.callCount(), 1); + assert.deepStrictEqual(readFile.mock.calls[0].arguments, [ + '../../../../.changeset/invalid-changeset-file.md', + 'utf-8', + ]); +}); + +test('minor update', async () => { + const event = { + pull_request: { + labels: [], + }, + }; + const env = { + CHANGED_FILES: '.changeset/patch-update.md .changeset/minor-update.md', + }; + + const readFile = mock.fn(async path => { + if (path.endsWith('patch-update.md')) { + return `---\nai: patch\n---\n## Test changeset`; + } + + return `---\n@ai-sdk/provider: minor\n---\n## Test changeset`; + }); + + await assert.rejects( + () => verifyChangesets(event, env, readFile), + Object.assign( + new Error( + `Invalid .changeset file - invalid version bump (only "patch" is allowed, see https://ai-sdk.dev/docs/migration-guides/versioning). To bypass, add one of the following labels: minor, major`, + ), + { + path: '.changeset/minor-update.md', + content: '---\n@ai-sdk/provider: minor\n---\n## Test changeset', + }, + ), + ); + + assert.strictEqual(readFile.mock.callCount(), 2); + assert.deepStrictEqual(readFile.mock.calls[0].arguments, [ + '../../../../.changeset/patch-update.md', + 'utf-8', + ]); + assert.deepStrictEqual(readFile.mock.calls[1].arguments, [ + '../../../../.changeset/minor-update.md', + 'utf-8', + ]); +}); + +test('minor update - with "minor" label', async () => { + const event = { + pull_request: { + labels: [ + { + name: 'minor', + }, + ], + }, + }; + const env = { + CHANGED_FILES: '.changeset/patch-update.md .changeset/minor-update.md', + }; + + const readFile = mock.fn(async path => { + if (path.endsWith('patch-update.md')) { + return `---\nai: patch\n---\n## Test changeset`; + } + + return `---\n@ai-sdk/provider: minor\n---\n## Test changeset`; + }); + + const message = await verifyChangesets(event, env, readFile); + assert.strictEqual( + message, + 'Skipping changeset verification - "minor" label found', + ); +}); + +test('major update - with "major" label', async () => { + const event = { + pull_request: { + labels: [ + { + name: 'major', + }, + ], + }, + }; + const env = { + CHANGED_FILES: '.changeset/patch-update.md .changeset/major-update.md', + }; + + const readFile = mock.fn(async path => { + if (path.endsWith('patch-update.md')) { + return `---\nai: patch\n---\n## Test changeset`; + } + + return `---\n@ai-sdk/provider: major\n---\n## Test changeset`; + }); + + const message = await verifyChangesets(event, env, readFile); + assert.strictEqual( + message, + 'Skipping changeset verification - "major" label found', + ); +}); diff --git a/.github/workflows/ai-provider-api-changes.yml b/.github/workflows/ai-provider-api-changes.yml new file mode 100644 index 000000000000..3ba2381cb25e --- /dev/null +++ b/.github/workflows/ai-provider-api-changes.yml @@ -0,0 +1,33 @@ +name: AI Provider API Changes + +on: + repository_dispatch: + types: [ai-provider-api-change] + +jobs: + create-issue: + name: 'Create Issue for Provider API Change' + runs-on: ubuntu-latest + steps: + - uses: actions/create-github-app-token@v2 + id: app-token + with: + app-id: ${{ vars.VERCEL_AI_SDK_GITHUB_APP_CLIENT_ID }} + private-key: ${{ secrets.VERCEL_AI_SDK_GITHUB_APP_PRIVATE_KEY_PKCS8 }} + + - name: Create issues + uses: octokit/request-action@v2.x + with: + route: POST /repos/{owner}/{repo}/issues + owner: vercel + repo: ai + title: | + 🤖 Provider API update - ${{ github.event.client_payload.release.tag_name }} + body: | + | + ${{ github.event.client_payload.release.body }} + + ${{ github.event.client_payload.release.html_url }} + labels: '["maintenance"]' + env: + GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} diff --git a/.github/workflows/assign-team-pull-request.yml b/.github/workflows/assign-team-pull-request.yml new file mode 100644 index 000000000000..5de0ad5d32fe --- /dev/null +++ b/.github/workflows/assign-team-pull-request.yml @@ -0,0 +1,23 @@ +name: Assign Team Pull Requests to Author + +on: + pull_request: + types: [opened] + +permissions: + pull-requests: write + +jobs: + assign: + runs-on: ubuntu-latest + # Only assign pull requests by team members, ignore pull requests from forks + # And only assign if pull request was created by a user, ignore bots + if: github.event.pull_request.head.repo.full_name == github.repository && github.event.pull_request.user.type == 'User' + steps: + - uses: actions/checkout@v4 + - name: Assign pull request to author + run: gh pr edit $PULL_REQUEST_URL --add-assignee $AUTHOR_LOGIN + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PULL_REQUEST_URL: ${{ github.event.pull_request.html_url }} + AUTHOR_LOGIN: ${{ github.event.pull_request.user.login }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7de754472594..89c274e80b81 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,11 +2,107 @@ name: CI on: push: - branches: [main] + branches: [main, v5] pull_request: - branches: [main] + branches: [main, v5] jobs: + build-examples: + name: 'Build Examples' + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 10.11.0 + + - name: Use Node.js 22 + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build Examples + run: pnpm run build:examples + + prettier: + name: 'Prettier' + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 10.11.0 + + - name: Use Node.js 22 + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Run Prettier check + run: pnpm run prettier-check + + eslint: + name: 'ESLint' + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 10.11.0 + + - name: Use Node.js 22 + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Run ESLint check + run: pnpm run lint + + types: + name: 'TypeScript' + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 10.11.0 + + - name: Use Node.js 22 + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Run TypeScript type check + run: pnpm run type-check:full + test: name: 'Test' runs-on: ubuntu-latest @@ -23,7 +119,7 @@ jobs: - name: Setup pnpm uses: pnpm/action-setup@v4 with: - version: 9.12.3 + version: 10.11.0 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v4 diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml deleted file mode 100644 index 42a185321759..000000000000 --- a/.github/workflows/quality.yml +++ /dev/null @@ -1,80 +0,0 @@ -name: Quality - -on: - push: - branches: [main] - pull_request: - branches: [main] - -jobs: - prettier: - name: 'Prettier' - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup pnpm - uses: pnpm/action-setup@v4 - with: - version: 9.12.3 - - - name: Use Node.js 22 - uses: actions/setup-node@v4 - with: - node-version: 22 - cache: 'pnpm' - - - name: Install dependencies - run: pnpm install --frozen-lockfile - - - name: Run Prettier check - run: pnpm run prettier-check - - eslint: - name: 'ESLint' - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup pnpm - uses: pnpm/action-setup@v4 - with: - version: 9.12.3 - - - name: Use Node.js 22 - uses: actions/setup-node@v4 - with: - node-version: 22 - cache: 'pnpm' - - - name: Install dependencies - run: pnpm install --frozen-lockfile - - - name: Run ESLint check - run: pnpm run lint - - types: - name: 'TypeScript' - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup pnpm - uses: pnpm/action-setup@v4 - with: - version: 9.12.3 - - - name: Use Node.js 22 - uses: actions/setup-node@v4 - with: - node-version: 22 - cache: 'pnpm' - - - name: Install dependencies - run: pnpm install --frozen-lockfile - - - name: Run TypeScript type check - run: pnpm run type-check diff --git a/.github/workflows/release-snapshot.yml b/.github/workflows/release-snapshot.yml index e58a9e0bfd8f..97e198e40651 100644 --- a/.github/workflows/release-snapshot.yml +++ b/.github/workflows/release-snapshot.yml @@ -45,7 +45,7 @@ jobs: - name: Setup pnpm uses: pnpm/action-setup@v4 with: - version: 9.12.3 + version: 10.11.0 - name: Setup Node.js 22 uses: actions/setup-node@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 12eec2f442e2..0ecedf873e48 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,6 +4,7 @@ on: push: branches: - main + - v5 paths: - '.changeset/**' - '.github/workflows/release.yml' @@ -17,15 +18,35 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 steps: + - name: Create access token for GitHub App + uses: actions/create-github-app-token@v2 + id: app-token + with: + app-id: ${{ vars.VERCEL_AI_SDK_GITHUB_APP_CLIENT_ID }} + private-key: ${{ secrets.VERCEL_AI_SDK_GITHUB_APP_PRIVATE_KEY_PKCS8 }} + + - name: Get GitHub App User ID + id: app-user-id + run: echo "user-id=$(gh api "/users/${{ steps.app-token.outputs.app-slug }}[bot]" --jq .id)" >> "$GITHUB_OUTPUT" + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + + - name: Configure git user for GitHub App + run: | + git config --global user.name '${{ steps.app-token.outputs.app-slug }}[bot]' + git config --global user.email '${{ steps.app-user-id.outputs.user-id }}+${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com' + - name: Checkout Repo uses: actions/checkout@v4 with: fetch-depth: 0 + token: ${{ steps.app-token.outputs.token }} + persist-credentials: false - name: Setup pnpm uses: pnpm/action-setup@v4 with: - version: 9.12.3 + version: 10.11.0 - name: Setup Node.js 22 uses: actions/setup-node@v4 @@ -39,9 +60,9 @@ jobs: id: changesets uses: changesets/action@v1 with: - # This expects you to have a script called release which does a build for your packages and calls changeset publish version: pnpm ci:version publish: pnpm ci:release + setupGitUser: false env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} NPM_TOKEN: ${{ secrets.NPM_TOKEN_ELEVATED }} diff --git a/.github/workflows/triage.yml b/.github/workflows/triage.yml new file mode 100644 index 000000000000..bab6f8393283 --- /dev/null +++ b/.github/workflows/triage.yml @@ -0,0 +1,103 @@ +name: Triage + +on: + issues: + types: [opened] + +permissions: + issues: write + +jobs: + triage: + name: Auto-triage Issue + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Create access token for GitHub App + uses: actions/create-github-app-token@v2 + id: app-token + with: + app-id: ${{ vars.VERCEL_AI_SDK_GITHUB_APP_CLIENT_ID }} + private-key: ${{ secrets.VERCEL_AI_SDK_GITHUB_APP_PRIVATE_KEY_PKCS8 }} + + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Fetch existing provider labels + id: fetch-labels + run: | + labels=$(gh api /repos/${{ github.repository }}/labels | jq -r '.[] | select(.name | startswith("provider/")) | .name' | jq -R -s -c 'split("\n")[:-1]') + echo "labels=$labels" >> $GITHUB_OUTPUT + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + + - name: Determine appropriate provider labels + id: classify-issue + uses: vercel/ai-action@v2 + with: + model: "openai/gpt-4o" + api-key: ${{ secrets.AI_GATEWAY_API_KEY }} + schema: | + { + "type": "object", + "properties": { + "labels": { + "type": "array", + "items": { + "type": "string", + "enum": ${{ steps.fetch-labels.outputs.labels }} + }, + "description": "Array of provider labels that are most relevant to this issue. Choose one or more labels that best match the AI provider mentioned in the issue." + }, + "confidence": { + "type": "number", + "minimum": 0, + "maximum": 1, + "description": "Confidence score for the label classification (0-1)" + } + }, + "required": ["labels", "confidence"] + } + prompt: | + Analyze the following GitHub issue and determine if it is related to any AI provider. If it is, determine which AI provider labels are most relevant. + + Available provider labels: ${{ steps.fetch-labels.outputs.labels }} + + Issue Title: ${{ github.event.issue.title }} + + Issue Body: ${{ github.event.issue.body }} + + Rules: + - If the issue is not related to any AI provider, return an empty array of labels. + - Look for mentions of specific AI providers like OpenAI, Anthropic, Google, Azure, etc. + - If no specific provider is mentioned, consider "provider/other" + - If the issue mentions community or third-party providers, use "provider/community" + - If it's about OpenAI-compatible APIs, use "provider/openai-compatible" + - Multiple labels can be assigned if the issue involves multiple providers + - Only assign labels if you're reasonably confident (>0.6) about the relevance + + - name: Apply provider labels to issue + if: fromJSON(steps.classify-issue.outputs.json).confidence > 0.6 + run: | + labels='${{ toJSON(fromJSON(steps.classify-issue.outputs.json).labels) }}' + if [ "$labels" != "[]" ]; then + gh api /repos/${{ github.repository }}/issues/${{ github.event.issue.number }}/labels \ + --method POST \ + --input - <<< "{\"labels\": $labels}" + echo "Applied labels: $labels" + else + echo "No labels to apply" + fi + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + + - name: Add comment if no provider detected + if: fromJSON(steps.classify-issue.outputs.json).confidence <= 0.6 + run: | + gh api /repos/${{ github.repository }}/issues/${{ github.event.issue.number }}/comments \ + --method POST \ + --input - <<< '{ + "body": "👋 Thanks for opening this issue! I was unable to automatically detect which AI provider this issue relates to. A maintainer will review and apply the appropriate `provider/*` labels manually." + }' + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} diff --git a/.github/workflows/verify-changesets.yml b/.github/workflows/verify-changesets.yml new file mode 100644 index 000000000000..4feca6aef7bb --- /dev/null +++ b/.github/workflows/verify-changesets.yml @@ -0,0 +1,38 @@ +# vercel/ai uses https://github.com/changesets/changesets for versioning and changelogs, +# but is not following semantic versioning. Instead, it uses `patch` for both fixes +# and features. It uses `minor` for "marketing releases", accompanied by a blog post and migration guide. +# This workflow verifies that all `.changeset/*.md` files use `patch` unless a `minor-release` label is present. +name: Verify Changesets + +on: + pull_request: + types: [opened, synchronize, reopened, labeled, unlabeled] + branches: + - main + paths: + - '.changeset/*.md' + +jobs: + verify-changesets: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-node@v4 + with: + node-version: 'lts/*' + - name: get all changed files from .changeset/*.md + id: changeset-files + run: | + echo "changed-files=$(git diff --diff-filter=dr --name-only $BASE_SHA -- '.changeset/*.md' | tr '\n' ' ')" >> $GITHUB_OUTPUT + env: + BASE_SHA: ${{ github.event.pull_request.base.sha }} + - name: Verify changesets + if: steps.changeset-files.outputs.changed-files != '' + working-directory: .github/workflows/actions/verify-changesets + run: | + node index.js + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CHANGED_FILES: ${{ steps.changeset-files.outputs.changed-files }} diff --git a/.gitignore b/.gitignore index 22b004a9f1da..abc64accd0a0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,17 @@ .DS_Store -node_modules -.turbo -*.log +.cache +.env +.envrc .next +.turbo dist dist-ssr -*.local -.env -.cache -server/dist +examples/*/build +node_modules public/dist -.turbo +server/dist test-results +tsconfig.vitest-temp.json +*.log +*.local +*.tsbuildinfo diff --git a/.npmrc b/.npmrc index 2a53e07c0db1..13562b7874fa 100644 --- a/.npmrc +++ b/.npmrc @@ -1,2 +1,4 @@ auto-install-peers = true -link-workspace-packages = true \ No newline at end of file +link-workspace-packages = true +public-hoist-pattern[]=*eslint* +public-hoist-pattern[]=*prettier* \ No newline at end of file diff --git a/.prettierignore b/.prettierignore index c74a64043bc3..ee60870ab39e 100644 --- a/.prettierignore +++ b/.prettierignore @@ -3,6 +3,5 @@ node_modules dist .svelte-kit -.solid _nuxt __testfixtures__ diff --git a/CHANGELOG.md b/CHANGELOG.md index 11f54b62a9ff..86eecc10d977 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ You can find the changelogs for the individual packages in their respective `CHA - [@ai-sdk/deepseek](./packages/deepseek/CHANGELOG.md) - [@ai-sdk/fal](./packages/fal/CHANGELOG.md) - [@ai-sdk/fireworks](./packages/fireworks/CHANGELOG.md) +- [@ai-sdk/gateway](./packages/gateway/CHANGELOG.md) - [@ai-sdk/google](./packages/google/CHANGELOG.md) - [@ai-sdk/google-vertex](./packages/google-vertex/CHANGELOG.md) - [@ai-sdk/groq](./packages/groq/CHANGELOG.md) @@ -24,6 +25,7 @@ You can find the changelogs for the individual packages in their respective `CHA - [@ai-sdk/openai-compatible](./packages/openai-compatible/CHANGELOG.md) - [@ai-sdk/perplexity](./packages/perplexity/CHANGELOG.md) - [@ai-sdk/togetherai](./packages/togetherai/CHANGELOG.md) +- [@ai-sdk/vercel](./packages/vercel/CHANGELOG.md) - [@ai-sdk/xai](./packages/xai/CHANGELOG.md) ### UI integrations diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000000..3085295e673d --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,3 @@ +# Vercel Community Code of Conduct + +Please see https://community.vercel.com/guidelines for the latest version. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d9644053c4a2..a9b513346955 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -32,7 +32,7 @@ We welcome your contributions to our code and documentation. Here's how you can ### Environment Setup -AI SDK development requires PNPM v9 (lockfile version) or higher and Node v20 or higher. +AI SDK development requires PNPM v9 (lockfile version) or higher and Node v22. ### Setting Up the Repository Locally @@ -40,8 +40,8 @@ To set up the repository on your local machine, follow these steps: 1. **Fork the Repository**: Make a copy of the repository to your GitHub account. 2. **Clone the Repository**: Clone the repository to your local machine, e.g. using `git clone`. -3. **Install Node**: If you haven't already, install Node v20. -4. **Install pnpm**: If you haven't already, install pnpm v9. You can do this by running `npm install -g pnpm@9` if you're using npm. Alternatively, if you're using Homebrew (Mac), you can run `brew install pnpm`. For more see [the pnpm site](https://pnpm.io/installation). +3. **Install Node**: If you haven't already, install Node v22. +4. **Install pnpm**: If you haven't already, install pnpm v10. You can do this by running `npm install -g pnpm@10` if you're using npm. Alternatively, if you're using Homebrew (Mac), you can run `brew install pnpm`. For more see [the pnpm site](https://pnpm.io/installation). 5. **Install Dependencies**: Navigate to the project directory and run `pnpm install` to install all necessary dependencies. 6. **Build the Project**: Run `pnpm build` in the root to build all packages. @@ -65,21 +65,34 @@ To test the package that you're working on, run `pnpm test` in the package folde You do not need to rebuild your package to test it (only dependencies need to be built). Some packages like `ai` also have more details tests and watch mode, see their `package.json` for more information. +#### Adding package dependencies + +Please run `pnpm update-references` in workspace root to update the `references` section in the `tsconfig.json` file. + ### Submitting Pull Requests We greatly appreciate your pull requests. Here are the steps to submit them: 1. **Create a New Branch**: Initiate your changes in a fresh branch. It's recommended to name the branch in a manner that signifies the changes you're implementing. -2. **Commit Your Changes**: Ensure your commits are succinct and clear, detailing what modifications have been made and the reasons behind them. -3. **Push the Changes to Your GitHub Repository**: After committing your changes, push them to your GitHub repository. -4. **Open a Pull Request**: Propose your changes for review. Furnish a lucid title and description of your contributions. Make sure to link any relevant issues your PR resolves. -5. **Respond to Feedback**: Stay receptive to and address any feedback or alteration requests from the project maintainers. +2. **Add a patch changeset**: If you're updating any packages and want to ensure they're released, add a **patch** changeset to your branch by running `pnpm changeset` in the workspace root. + + - **Please do not use minor or major changesets**, we'll let you know when you need to use a different changeset type than patch. + - You don't need to select any of the `examples/*` packages, as they are not released. -### Fixing Prettier Issues +3. **Add a codemod**: If the change introduces a deprecation or a breaking change, add a codemod if possible. See [how to contribute codemods](contributing/codemods.md) +4. **Commit Your Changes**: Ensure your commits are succinct and clear, detailing what modifications have been made and the reasons behind them. We don't require a specific commit message format, but please be descriptive. +5. **Fix prettier issues**: Run `pnpm prettier-fix` to fix any formatting issues in your code. +6. **Push the Changes to Your GitHub Repository**: After committing your changes, push them to your GitHub repository. +7. **Open a Pull Request**: Propose your changes for review. Furnish a lucid title and description of your contributions. Make sure to link any relevant issues your PR resolves. We use the following PR title format: -> [!TIP] -> Run `pnpm prettier-fix` before opening a pull request. + - `fix(package-name): description` or + - `feat(package-name): description` or + - `chore(package-name): description` etc. -If you encounter any prettier issues, you can fix them by running `pnpm prettier-fix`. This command will automatically fix any formatting issues in your code. +8. **Respond to Feedback**: Stay receptive to and address any feedback or alteration requests from the project maintainers. Thank you for contributing to the AI SDK! Your efforts help us improve the project for everyone. + +## Learn More + +We have additional contributor documentation in the `contributing/` folder. diff --git a/README.md b/README.md index f90be20a312e..ad93ed3ce3ec 120000 --- a/README.md +++ b/README.md @@ -1 +1 @@ -packages/ai/README.md \ No newline at end of file +packages/ai/README.md diff --git a/content/docs/02-guides/01-rag-chatbot.mdx b/content/cookbook/00-guides/01-rag-chatbot.mdx similarity index 75% rename from content/docs/02-guides/01-rag-chatbot.mdx rename to content/cookbook/00-guides/01-rag-chatbot.mdx index 7735064289a6..0e556554a04b 100644 --- a/content/docs/02-guides/01-rag-chatbot.mdx +++ b/content/cookbook/00-guides/01-rag-chatbot.mdx @@ -1,11 +1,22 @@ --- -title: RAG Chatbot -description: Learn how to build a RAG Chatbot with the AI SDK and Next.js +title: RAG Agent +description: Learn how to build a RAG Agent with the AI SDK and Next.js +tags: + [ + 'rag', + 'chatbot', + 'next', + 'embeddings', + 'database', + 'retrieval', + 'memory', + 'agent', + ] --- -# RAG Chatbot Guide +# RAG Agent Guide -In this guide, you will learn how to build a retrieval-augmented generation (RAG) chatbot application. +In this guide, you will learn how to build a retrieval-augmented generation (RAG) agent.