diff --git a/.changeset/slimy-dancers-wait.md b/.changeset/slimy-dancers-wait.md new file mode 100644 index 00000000..b9423d15 --- /dev/null +++ b/.changeset/slimy-dancers-wait.md @@ -0,0 +1,11 @@ +--- +"@upstash/context7-tools-ai-sdk": minor +--- + +Initial release of `@upstash/context7-tools-ai-sdk` - Vercel AI SDK integration for Context7. + +### Features + +- **Tools**: `resolveLibrary()` and `getLibraryDocs()` tools compatible with AI SDK's `generateText` and `streamText` +- **Agent**: Pre-configured `Context7Agent` that handles the multi-step documentation retrieval workflow automatically + diff --git a/.github/workflows/canary-release.yml b/.github/workflows/canary-release.yml index d6388d1f..48b9d596 100644 --- a/.github/workflows/canary-release.yml +++ b/.github/workflows/canary-release.yml @@ -28,7 +28,7 @@ jobs: - name: Setup pnpm uses: pnpm/action-setup@v4 with: - version: 9 + version: 10 - name: Configure npm authentication run: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 760e15f8..40d04628 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,7 +27,7 @@ jobs: - name: Setup pnpm uses: pnpm/action-setup@v4 with: - version: 9 + version: 10 - name: Configure npm authentication run: | diff --git a/.github/workflows/check.yaml b/.github/workflows/test.yml similarity index 58% rename from .github/workflows/check.yaml rename to .github/workflows/test.yml index 678e4a68..77d41ca6 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/test.yml @@ -1,24 +1,22 @@ -name: Build Check +name: Test on: - push: - branches: [master] pull_request: + push: branches: [master] + workflow_dispatch: concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: - build: - name: Build + test: runs-on: ubuntu-latest steps: - - name: Checkout repository - uses: actions/checkout@v6 + - uses: actions/checkout@v4 - - name: Setup Node.js + - name: Setup Node uses: actions/setup-node@v4 with: node-version: "20" @@ -26,7 +24,7 @@ jobs: - name: Setup pnpm uses: pnpm/action-setup@v4 with: - version: 9 + version: 10 - name: Get pnpm store directory id: pnpm-cache @@ -42,14 +40,24 @@ jobs: restore-keys: | ${{ runner.os }}-pnpm-store- - - name: Install dependencies + - name: Install Dependencies run: pnpm install --frozen-lockfile - - name: Run linter - run: pnpm run lint:check + - name: Lint + run: pnpm lint:check + + - name: Format + run: pnpm format:check + + - name: Build + run: pnpm build - - name: Check formatting - run: pnpm run format:check + - name: Typecheck + run: pnpm typecheck - - name: Build project - run: pnpm run build + - name: Test + run: pnpm test + env: + AWS_REGION: ${{ secrets.AWS_REGION }} + AWS_BEARER_TOKEN_BEDROCK: ${{ secrets.AWS_BEARER_TOKEN_BEDROCK }} + CONTEXT7_API_KEY: ${{ secrets.CONTEXT7_API_KEY }} diff --git a/docs/agentic-tools/ai-sdk/agents/context7-agent.mdx b/docs/agentic-tools/ai-sdk/agents/context7-agent.mdx new file mode 100644 index 00000000..47817d22 --- /dev/null +++ b/docs/agentic-tools/ai-sdk/agents/context7-agent.mdx @@ -0,0 +1,206 @@ +--- +title: "Context7Agent" +sidebarTitle: "Context7Agent" +description: "Pre-built AI agent for documentation lookup workflows" +--- + +The `Context7Agent` class is a pre-configured AI agent that handles the complete documentation lookup workflow automatically. It combines both `resolveLibrary` and `getLibraryDocs` tools with an optimized system prompt. + +## Usage + +```typescript +import { Context7Agent } from "@upstash/context7-tools-ai-sdk"; +import { anthropic } from "@ai-sdk/anthropic"; + +const agent = new Context7Agent({ + model: anthropic("claude-sonnet-4-20250514"), +}); + +const { text } = await agent.generate({ + prompt: "How do I use React Server Components?", +}); + +console.log(text); +``` + +## Configuration + +```typescript +new Context7Agent(config?: Context7AgentConfig) +``` + +### Parameters + + + Configuration options for the agent. + + + + Language model to use. Must be a LanguageModel instance from an AI SDK provider. + + Examples: + - `anthropic('claude-sonnet-4-20250514')` + - `openai('gpt-4o')` + - `google('gemini-1.5-pro')` + + + Context7 API key. If not provided, uses the `CONTEXT7_API_KEY` environment variable. + + + Default maximum number of documentation results per request. + + + Custom system prompt. Overrides the default `AGENT_PROMPT`. + + + Condition for when the agent should stop. Defaults to stopping after 5 steps. + + + + + +### Returns + +`Context7Agent` extends the AI SDK `Agent` class and provides `generate()` and `stream()` methods. + +## Agent Workflow + +The agent follows a structured multi-step workflow: + +```mermaid +flowchart TD + A[User Query] --> B[Extract Library Name] + B --> C[Call resolveLibrary] + C --> D{Results Found?} + D -->|Yes| E[Select Best Match] + D -->|No| F[Report No Results] + E --> G[Call getLibraryDocs] + G --> H{Sufficient Context?} + H -->|Yes| I[Generate Response] + H -->|No| J[Fetch More Pages] + J --> H + I --> K[Return Answer with Examples] +``` + +### Step-by-Step + +1. **Extract library name** - Identifies the library/framework from the user's query +2. **Resolve library** - Calls `resolveLibrary` to find the Context7 library ID +3. **Select best match** - Analyzes results based on reputation, coverage, and relevance +4. **Fetch documentation** - Calls `getLibraryDocs` with the selected library ID and relevant topic +5. **Paginate if needed** - Fetches additional pages if initial context is insufficient +6. **Generate response** - Provides an answer with code examples from the documentation + +## Examples + +### Basic Usage + +```typescript +import { Context7Agent } from "@upstash/context7-tools-ai-sdk"; +import { anthropic } from "@ai-sdk/anthropic"; + +const agent = new Context7Agent({ + model: anthropic("claude-sonnet-4-20250514"), +}); + +const { text } = await agent.generate({ + prompt: "How do I set up authentication in Next.js?", +}); + +console.log(text); +``` + +### With OpenAI + +```typescript +import { Context7Agent } from "@upstash/context7-tools-ai-sdk"; +import { openai } from "@ai-sdk/openai"; + +const agent = new Context7Agent({ + model: openai("gpt-4o"), +}); + +const { text } = await agent.generate({ + prompt: "Explain Tanstack Query's useQuery hook", +}); +``` + +### Streaming Responses + +```typescript +import { Context7Agent } from "@upstash/context7-tools-ai-sdk"; +import { anthropic } from "@ai-sdk/anthropic"; + +const agent = new Context7Agent({ + model: anthropic("claude-sonnet-4-20250514"), +}); + +const { textStream } = await agent.stream({ + prompt: "How do I create a Supabase Edge Function?", +}); + +for await (const chunk of textStream) { + process.stdout.write(chunk); +} +``` + +### Custom Configuration + +```typescript +import { Context7Agent } from "@upstash/context7-tools-ai-sdk"; +import { anthropic } from "@ai-sdk/anthropic"; +import { stepCountIs } from "ai"; + +const agent = new Context7Agent({ + model: anthropic("claude-sonnet-4-20250514"), + apiKey: process.env.CONTEXT7_API_KEY, + defaultMaxResults: 5, + stopWhen: stepCountIs(8), // Allow more steps for complex queries +}); +``` + +### Custom System Prompt + +```typescript +import { Context7Agent, AGENT_PROMPT } from "@upstash/context7-tools-ai-sdk"; +import { openai } from "@ai-sdk/openai"; + +const agent = new Context7Agent({ + model: openai("gpt-4o"), + system: `${AGENT_PROMPT} + +Additional instructions: +- Always include TypeScript examples +- Mention version compatibility when relevant +- Suggest related documentation topics`, +}); +``` + +## Comparison: Agent vs Tools + +| Feature | Context7Agent | Individual Tools | +| ------------- | -------------------- | -------------------- | +| Setup | Single configuration | Configure each tool | +| Workflow | Automatic multi-step | Manual orchestration | +| System prompt | Optimized default | You provide | +| Customization | Limited | Full control | +| Best for | Quick integration | Custom workflows | + +### When to Use the Agent + +- Rapid prototyping +- Standard documentation lookup use cases +- When you want sensible defaults + +### When to Use Individual Tools + +- Custom agentic workflows +- Integration with other tools +- Fine-grained control over the process +- Custom system prompts with specific behavior + +## Related + +- [resolveLibrary](/agentic-tools/ai-sdk/tools/resolve-library) - The library search tool used by the agent +- [getLibraryDocs](/agentic-tools/ai-sdk/tools/get-library-docs) - The documentation fetch tool used by the agent +- [Getting Started](/agentic-tools/ai-sdk/getting-started) - Overview of the AI SDK integration diff --git a/docs/agentic-tools/ai-sdk/getting-started.mdx b/docs/agentic-tools/ai-sdk/getting-started.mdx new file mode 100644 index 00000000..ec8c95ae --- /dev/null +++ b/docs/agentic-tools/ai-sdk/getting-started.mdx @@ -0,0 +1,165 @@ +--- +title: "Getting Started" +sidebarTitle: "Getting Started" +description: "Add Context7 documentation tools to your Vercel AI SDK applications" +--- + +`@upstash/context7-tools-ai-sdk` provides [Vercel AI SDK](https://sdk.vercel.ai/) compatible tools and agents that give your AI applications access to up-to-date library documentation. + +When building AI-powered applications with the Vercel AI SDK, your models often need accurate information about libraries and frameworks. Instead of relying on potentially outdated training data, Context7 tools let your AI fetch current documentation on-demand, ensuring responses include correct API usage, current best practices, and working code examples. + +The package gives you two ways to integrate: + +1. **Individual tools** (`resolveLibrary` and `getLibraryDocs`) that you add to your existing `generateText` or `streamText` calls +2. **A pre-built agent** (`Context7Agent`) that handles the entire documentation lookup workflow automatically + +Both approaches work with any AI provider supported by the Vercel AI SDK, including OpenAI, Anthropic, Google, and others. + +## Installation + + +```bash npm +npm install @upstash/context7-tools-ai-sdk +``` + +```bash pnpm +pnpm add @upstash/context7-tools-ai-sdk +``` + +```bash yarn +yarn add @upstash/context7-tools-ai-sdk +``` + +```bash bun +bun add @upstash/context7-tools-ai-sdk +``` + + + +## Prerequisites + +You'll need: + +1. A Context7 API key from the [Context7 Dashboard](https://context7.com/dashboard) +2. An AI provider SDK (e.g., `@ai-sdk/openai`, `@ai-sdk/anthropic`) + +## Configuration + +Set your Context7 API key as an environment variable: + +```bash +CONTEXT7_API_KEY=ctx7sk-... +``` + +The tools and agents will automatically use this key. + +## Quick Start + +### Using Tools with generateText + +The simplest way to add documentation lookup to your AI application: + +```typescript +import { resolveLibrary, getLibraryDocs } from "@upstash/context7-tools-ai-sdk"; +import { generateText, stepCountIs } from "ai"; +import { openai } from "@ai-sdk/openai"; + +const { text } = await generateText({ + model: openai("gpt-4o"), + prompt: "How do I create a server action in Next.js?", + tools: { + resolveLibrary: resolveLibrary(), + getLibraryDocs: getLibraryDocs(), + }, + stopWhen: stepCountIs(5), +}); + +console.log(text); +``` + +### Using the Context7 Agent + +For a more streamlined experience, use the pre-configured agent: + +```typescript +import { Context7Agent } from "@upstash/context7-tools-ai-sdk"; +import { anthropic } from "@ai-sdk/anthropic"; + +const agent = new Context7Agent({ + model: anthropic("claude-sonnet-4-20250514"), +}); + +const { text } = await agent.generate({ + prompt: "How do I use React Server Components?", +}); + +console.log(text); +``` + +### Using Tools with streamText + +For streaming responses: + +```typescript +import { resolveLibrary, getLibraryDocs } from "@upstash/context7-tools-ai-sdk"; +import { streamText, stepCountIs } from "ai"; +import { openai } from "@ai-sdk/openai"; + +const { textStream } = streamText({ + model: openai("gpt-4o"), + prompt: "Explain how to use Tanstack Query for data fetching", + tools: { + resolveLibrary: resolveLibrary(), + getLibraryDocs: getLibraryDocs(), + }, + stopWhen: stepCountIs(5), +}); + +for await (const chunk of textStream) { + process.stdout.write(chunk); +} +``` + +## Explicit Configuration + +You can also pass the API key directly if needed: + +```typescript +import { resolveLibrary, getLibraryDocs } from "@upstash/context7-tools-ai-sdk"; + +const tools = { + resolveLibrary: resolveLibrary({ apiKey: "your-api-key" }), + getLibraryDocs: getLibraryDocs({ apiKey: "your-api-key" }), +}; +``` + +## How It Works + +The tools follow a two-step workflow: + +1. **`resolveLibrary`** - Searches Context7's database to find the correct library ID for a given query (e.g., "react" → `/reactjs/react.dev`) + +2. **`getLibraryDocs`** - Fetches documentation for the resolved library with options for: + - **mode** - `"code"` for API references and examples, `"info"` for conceptual guides + - **topic** - Filter documentation by topic (e.g., "hooks", "routing") + - **page** - Pagination for comprehensive results + +The AI model orchestrates these tools automatically based on the user's prompt, fetching relevant documentation before generating a response. + +## Next Steps + + + + Search for libraries and get Context7-compatible IDs + + + Fetch documentation for a specific library + + + Use the pre-built documentation agent + + diff --git a/docs/agentic-tools/ai-sdk/tools/get-library-docs.mdx b/docs/agentic-tools/ai-sdk/tools/get-library-docs.mdx new file mode 100644 index 00000000..50c54add --- /dev/null +++ b/docs/agentic-tools/ai-sdk/tools/get-library-docs.mdx @@ -0,0 +1,283 @@ +--- +title: "getLibraryDocs" +sidebarTitle: "getLibraryDocs" +description: "Fetch up-to-date documentation for a specific library" +--- + +The `getLibraryDocs` tool fetches documentation for a library using its Context7-compatible library ID. This tool is typically called after `resolveLibrary` has identified the correct library. + +## Usage + +```typescript +import { resolveLibrary, getLibraryDocs } from "@upstash/context7-tools-ai-sdk"; +import { generateText, stepCountIs } from "ai"; +import { openai } from "@ai-sdk/openai"; + +const { text } = await generateText({ + model: openai("gpt-4o"), + prompt: "How do I use React Server Components?", + tools: { + resolveLibrary: resolveLibrary(), + getLibraryDocs: getLibraryDocs(), + }, + stopWhen: stepCountIs(5), +}); +``` + +## Configuration + +```typescript +getLibraryDocs(config?: Context7ToolsConfig) +``` + +### Parameters + + + Configuration options for the tool. + + + + Context7 API key. If not provided, uses the `CONTEXT7_API_KEY` environment variable. + + + Default maximum number of documentation results per request. + + + + +### Returns + +Returns an AI SDK `tool` that can be used with `generateText`, `streamText`, or agents. + +## Tool Behavior + +When the AI model calls this tool, it: + +1. Takes a library ID and optional parameters from the model +2. Fetches documentation from Context7's API +3. Returns the documentation content with pagination info + +### Input Schema + +The tool accepts the following inputs from the AI model: + + + Context7-compatible library ID (e.g., `/reactjs/react.dev`, `/vercel/next.js`) + + + + Documentation mode: + - `"code"` - API references and code examples (default) + - `"info"` - Conceptual guides, narrative information, and architectural questions + + + + Topic to focus documentation on (e.g., "hooks", "routing", "authentication") + + + + Page number for pagination (1-10) + + +### Output Format + +The output structure depends on the `mode` parameter. + +#### Code Mode (default) + +When `mode="code"`, the tool returns structured code snippets: + +```typescript +{ + success: true, + libraryId: "/reactjs/react.dev", + snippets: [ + { + codeTitle: "Server Components", + codeDescription: "How to use React Server Components", + codeLanguage: "tsx", + codeTokens: 150, + codeId: "abc123", + pageTitle: "React Server Components", + codeList: [ + { + language: "tsx", + code: "async function ServerComponent() {\n const data = await fetchData();\n return
{data}
;\n}" + } + ] + }, + // ... more snippets + ], + pagination: { + page: 1, + limit: 10, + totalPages: 5, + hasNext: true, + hasPrev: false + }, + totalTokens: 1500 +} +``` + +#### Info Mode + +When `mode="info"`, the tool returns conceptual documentation content: + +```typescript +{ + success: true, + libraryId: "/reactjs/react.dev", + snippets: [ + { + pageId: "server-components-intro", + breadcrumb: "React > Advanced > Server Components", + content: "Server Components let you write UI that can be rendered and optionally cached on the server...", + contentTokens: 250 + }, + // ... more snippets + ], + pagination: { + page: 1, + limit: 10, + totalPages: 3, + hasNext: true, + hasPrev: false + }, + totalTokens: 2500 +} +``` + +#### On Failure + +```typescript +{ + success: false, + error: "Documentation not found or not finalized for this library.", + libraryId: "/invalid/library" +} +``` + +## Examples + +### Basic Usage with Both Tools + +```typescript +import { resolveLibrary, getLibraryDocs } from "@upstash/context7-tools-ai-sdk"; +import { generateText, stepCountIs } from "ai"; +import { openai } from "@ai-sdk/openai"; + +const { text } = await generateText({ + model: openai("gpt-4o"), + prompt: "Show me how to set up routing in Next.js App Router", + tools: { + resolveLibrary: resolveLibrary(), + getLibraryDocs: getLibraryDocs(), + }, + stopWhen: stepCountIs(5), +}); + +// The model will: +// 1. Call resolveLibrary("next.js") to get the library ID +// 2. Call getLibraryDocs({ libraryId: "/vercel/next.js", topic: "routing" }) +// 3. Generate a response using the fetched documentation +``` + +### With Custom Configuration + +```typescript +import { getLibraryDocs } from "@upstash/context7-tools-ai-sdk"; + +const tool = getLibraryDocs({ + apiKey: process.env.CONTEXT7_API_KEY, + defaultMaxResults: 5, // Fetch fewer results by default +}); +``` + +### Direct Library ID (Skip resolveLibrary) + +If the user provides a library ID directly, the model can skip the resolution step: + +```typescript +import { getLibraryDocs } from "@upstash/context7-tools-ai-sdk"; +import { generateText, stepCountIs } from "ai"; +import { openai } from "@ai-sdk/openai"; + +const { text } = await generateText({ + model: openai("gpt-4o"), + prompt: "Using /vercel/next.js, explain middleware", + tools: { + getLibraryDocs: getLibraryDocs(), + }, + stopWhen: stepCountIs(3), +}); + +// The model recognizes the /org/project format and calls getLibraryDocs directly +``` + +### Paginated Results + +For comprehensive documentation, the model can request multiple pages: + +```typescript +import { resolveLibrary, getLibraryDocs } from "@upstash/context7-tools-ai-sdk"; +import { generateText, stepCountIs } from "ai"; +import { anthropic } from "@ai-sdk/anthropic"; + +const { text } = await generateText({ + model: anthropic("claude-sonnet-4-20250514"), + prompt: "Give me a comprehensive guide to Supabase authentication", + tools: { + resolveLibrary: resolveLibrary(), + getLibraryDocs: getLibraryDocs(), + }, + stopWhen: stepCountIs(8), // Allow more steps for pagination +}); + +// The model may call getLibraryDocs multiple times with page=1, page=2, etc. +// to gather comprehensive documentation +``` + +## Topic Filtering + +The `topic` parameter helps focus the documentation results. The AI model extracts relevant topics from the user's query: + +| User Query | Extracted Topic | +| ---------------------------------- | ------------------------------ | +| "How do I use React hooks?" | "hooks" | +| "Set up authentication in Next.js" | "authentication" | +| "Supabase real-time subscriptions" | "real-time" or "subscriptions" | +| "Vue component lifecycle" | "lifecycle" | + +## Mode Selection + +The `mode` parameter determines the type of documentation returned: + +| Mode | Best For | Returns | +| ------ | ----------------------------------------------------- | ------------------------------------ | +| `code` | API references, code examples, implementation details | `CodeSnippet` with code blocks | +| `info` | Conceptual guides, architecture, explanations | `InfoSnippet` with narrative content | + +The AI model automatically selects the appropriate mode based on the user's question: +- "How do I implement..." → `mode: "code"` +- "What is the architecture of..." → `mode: "info"` +- "Show me an example of..." → `mode: "code"` +- "Explain how ... works" → `mode: "info"` + +## Version-Specific Documentation + +Library IDs can include version specifiers: + +```typescript +// Latest version +"/vercel/next.js"; + +// Specific version +"/vercel/next.js/v14.3.0-canary.87"; +``` + +The model can request documentation for specific versions when the user asks about a particular version. + +## Related + +- [resolveLibrary](/agentic-tools/ai-sdk/tools/resolve-library) - Search for libraries and get their IDs +- [Context7Agent](/agentic-tools/ai-sdk/agents/context7-agent) - Pre-built agent that handles the full workflow diff --git a/docs/agentic-tools/ai-sdk/tools/resolve-library.mdx b/docs/agentic-tools/ai-sdk/tools/resolve-library.mdx new file mode 100644 index 00000000..eb298aa3 --- /dev/null +++ b/docs/agentic-tools/ai-sdk/tools/resolve-library.mdx @@ -0,0 +1,175 @@ +--- +title: "resolveLibrary" +sidebarTitle: "resolveLibrary" +description: "Search for libraries and resolve them to Context7-compatible IDs" +--- + +The `resolveLibrary` tool searches Context7's library database and returns matching results with their Context7-compatible library IDs. This is typically the first step in a documentation lookup workflow. + +## Usage + +```typescript +import { resolveLibrary } from "@upstash/context7-tools-ai-sdk"; +import { generateText, stepCountIs } from "ai"; +import { openai } from "@ai-sdk/openai"; + +const { text } = await generateText({ + model: openai("gpt-4o"), + prompt: "Find documentation for React hooks", + tools: { + resolveLibrary: resolveLibrary(), + }, + stopWhen: stepCountIs(3), +}); +``` + +## Configuration + +```typescript +resolveLibrary(config?: Context7ToolsConfig) +``` + +### Parameters + + + Configuration options for the tool. + + + + Context7 API key. If not provided, uses the `CONTEXT7_API_KEY` environment variable. + + + + +### Returns + +Returns an AI SDK `tool` that can be used with `generateText`, `streamText`, or agents. + +## Tool Behavior + +When the AI model calls this tool, it: + +1. Takes a `libraryName` parameter from the model +2. Searches Context7's database for matching libraries +3. Returns formatted results including: + - Library ID (e.g., `/reactjs/react.dev`) + - Title and description + - Number of code snippets available + - Source reputation score + - Available versions + +### Input Schema + +The tool accepts the following input from the AI model: + + + Library name to search for (e.g., "react", "next.js", "vue") + + +### Output Format + +On success, the tool returns the search results as JSON: + +```typescript +{ + success: true, + results: [ + { + id: "/reactjs/react.dev", + title: "React Documentation", + description: "The library for web and native user interfaces", + totalSnippets: 1250, + trustScore: 0.95, + benchmarkScore: 98, + versions: ["19.0.0", "18.3.1", "18.2.0"] + }, + // ... more results + ], + totalResults: 5 +} +``` + +On failure: + +```typescript +{ + success: false, + error: "No libraries found matching your query.", + suggestions: "Try a different search term or check the library name." +} +``` + +## Examples + +### Basic Usage + +```typescript +import { resolveLibrary, getLibraryDocs } from "@upstash/context7-tools-ai-sdk"; +import { generateText, stepCountIs } from "ai"; +import { openai } from "@ai-sdk/openai"; + +const { text, toolCalls } = await generateText({ + model: openai("gpt-4o"), + prompt: "What libraries are available for React?", + tools: { + resolveLibrary: resolveLibrary(), + getLibraryDocs: getLibraryDocs(), + }, + stopWhen: stepCountIs(5), +}); + +// The model will call resolveLibrary("react") and receive a list of matching libraries +console.log(text); +``` + +### With Custom API Key + +```typescript +import { resolveLibrary } from "@upstash/context7-tools-ai-sdk"; + +const tool = resolveLibrary({ + apiKey: process.env.CONTEXT7_API_KEY, +}); +``` + +### Inspecting Tool Calls + +```typescript +import { resolveLibrary } from "@upstash/context7-tools-ai-sdk"; +import { generateText, stepCountIs } from "ai"; +import { openai } from "@ai-sdk/openai"; + +const { toolCalls, toolResults } = await generateText({ + model: openai("gpt-4o"), + prompt: "Find the official Next.js documentation", + tools: { + resolveLibrary: resolveLibrary(), + }, + stopWhen: stepCountIs(3), +}); + +// See what the model searched for +for (const call of toolCalls) { + console.log("Searched for:", call.args.libraryName); +} + +// See the results +for (const result of toolResults) { + console.log("Found:", result.result); +} +``` + +## Selection Guidance + +The tool's description instructs the AI model to select libraries based on: + +1. **Name similarity** - Exact matches are prioritized +2. **Description relevance** - How well the description matches the query intent +3. **Documentation coverage** - Libraries with more code snippets are preferred +4. **Source reputation** - High/Medium reputation sources are more authoritative +5. **Benchmark score** - Quality indicator (100 is the highest) + +## Related + +- [getLibraryDocs](/agentic-tools/ai-sdk/tools/get-library-docs) - Fetch documentation using the resolved library ID +- [Context7Agent](/agentic-tools/ai-sdk/agents/context7-agent) - Pre-built agent that handles the full workflow diff --git a/docs/agentic-tools/overview.mdx b/docs/agentic-tools/overview.mdx new file mode 100644 index 00000000..2594783e --- /dev/null +++ b/docs/agentic-tools/overview.mdx @@ -0,0 +1,133 @@ +--- +title: "Overview" +sidebarTitle: "Overview" +description: "Build AI agents with up-to-date library documentation" +--- + +# Agentic Tools + +Context7 provides tools and integrations that give your AI agents access to accurate, up-to-date library documentation. Instead of relying on potentially outdated training data, your agents can fetch real-time documentation to answer questions and generate code. + +## Why Agentic Tools? + +AI agents often struggle with: + +- **Outdated knowledge** - Training data becomes stale, leading to deprecated API usage +- **Hallucinated APIs** - Models confidently suggest methods that don't exist +- **Version mismatches** - Code examples from old versions that no longer work + +Context7's agentic tools solve these problems by giving your agents direct access to current documentation during inference. + +## Available Integrations + + + + Add Context7 tools to your AI SDK workflows with `generateText`, `streamText`, or use the + pre-built `Context7Agent` for automatic documentation lookup. + + + +## How It Works + +```mermaid +sequenceDiagram + participant User + participant Agent + participant Context7 + participant Docs + + User->>Agent: "How do I use React Server Components?" + Agent->>Context7: resolveLibrary("react") + Context7-->>Agent: Library ID: /reactjs/react.dev + Agent->>Context7: getLibraryDocs("/reactjs/react.dev", topic: "server components") + Context7->>Docs: Fetch latest documentation + Docs-->>Context7: Current docs with examples + Context7-->>Agent: Documentation content + Agent->>User: Answer with accurate, up-to-date code examples +``` + +## Use Cases + + + + Build chatbots that answer framework questions with accurate, version-specific information: + + ```typescript + import { generateText, stepCountIs } from "ai"; + import { openai } from "@ai-sdk/openai"; + import { resolveLibrary, getLibraryDocs } from "@upstash/context7-tools-ai-sdk"; + + const { text } = await generateText({ + model: openai("gpt-4o"), + prompt: "How do I set up authentication in Next.js 15?", + tools: { + resolveLibrary: resolveLibrary(), + getLibraryDocs: getLibraryDocs(), + }, + stopWhen: stepCountIs(5), + }); + ``` + + + + + Ensure generated code uses current APIs and best practices: + + ```typescript + import { Context7Agent } from "@upstash/context7-tools-ai-sdk"; + import { anthropic } from "@ai-sdk/anthropic"; + + const agent = new Context7Agent({ + model: anthropic("claude-sonnet-4-20250514"), + }); + + const { text } = await agent.generate({ + prompt: "Generate a Supabase Edge Function that handles webhooks", + }); + ``` + + + + + Build code review agents that verify implementations against current API documentation: + + ```typescript + import { generateText, stepCountIs } from "ai"; + import { anthropic } from "@ai-sdk/anthropic"; + import { resolveLibrary, getLibraryDocs } from "@upstash/context7-tools-ai-sdk"; + + const codeToReview = ` + const { data } = await supabase + .from('users') + .select('*') + .eq('id', userId) + .single(); + `; + + const { text } = await generateText({ + model: anthropic("claude-sonnet-4-20250514"), + prompt: `Review this Supabase code for correctness and best practices: + + ${codeToReview} + + Check against the latest Supabase documentation.`, + tools: { + resolveLibrary: resolveLibrary(), + getLibraryDocs: getLibraryDocs(), + }, + stopWhen: stepCountIs(5), + }); + + // Agent fetches current Supabase docs to verify: + // - Correct method signatures + // - Deprecated patterns + // - Security best practices + // - Error handling recommendations + ``` + + + diff --git a/docs/docs.json b/docs/docs.json index 666148eb..9b2a66c4 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -48,6 +48,31 @@ } ] }, + { + "group": "Agentic Tools", + "pages": [ + "agentic-tools/overview", + { + "group": "Vercel AI SDK", + "pages": [ + "agentic-tools/ai-sdk/getting-started", + { + "group": "Tools", + "pages": [ + "agentic-tools/ai-sdk/tools/resolve-library", + "agentic-tools/ai-sdk/tools/get-library-docs" + ] + }, + { + "group": "Agents", + "pages": [ + "agentic-tools/ai-sdk/agents/context7-agent" + ] + } + ] + } + ] + }, { "group": "API Reference", "openapi": "openapi.json" diff --git a/package.json b/package.json index a777d6fd..5d9aaf5f 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,11 @@ "build": "pnpm -r run build", "build:sdk": "pnpm --filter @upstash/context7-sdk build", "build:mcp": "pnpm --filter @upstash/context7-mcp build", + "build:ai-sdk": "pnpm --filter @upstash/context7-tools-ai-sdk build", + "typecheck": "pnpm -r run typecheck", "test": "pnpm -r run test", "test:sdk": "pnpm --filter @upstash/context7-sdk test", + "test:tools-ai-sdk": "pnpm --filter @upstash/context7-tools-ai-sdk test", "clean": "pnpm -r run clean && rm -rf node_modules", "lint": "pnpm -r run lint", "lint:check": "pnpm -r run lint:check", diff --git a/packages/mcp/package.json b/packages/mcp/package.json index 8425abb8..5cb232e6 100644 --- a/packages/mcp/package.json +++ b/packages/mcp/package.json @@ -5,7 +5,8 @@ "description": "MCP server for Context7", "scripts": { "build": "tsc && chmod 755 dist/index.js", - "test": "echo \"Error: no test specified\" && exit 1", + "test": "echo \"No tests yet\"", + "typecheck": "tsc --noEmit", "lint": "eslint .", "lint:check": "eslint .", "format": "prettier --write .", diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 9bc2006c..e556295c 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -6,6 +6,7 @@ "build": "tsup", "test": "vitest run", "test:watch": "vitest", + "typecheck": "tsc --noEmit", "dev": "tsup --watch", "clean": "rm -rf dist", "lint": "eslint .", diff --git a/packages/tools-ai-sdk/README.md b/packages/tools-ai-sdk/README.md new file mode 100644 index 00000000..6afb130e --- /dev/null +++ b/packages/tools-ai-sdk/README.md @@ -0,0 +1,101 @@ +# Upstash Context7 AI SDK + +`@upstash/context7-tools-ai-sdk` provides [Vercel AI SDK](https://ai-sdk.dev/) compatible tools and agents that give your AI applications access to up to date library documentation through Context7. + +Use this package to: + +- Add documentation lookup tools to your AI SDK workflows with `generateText` or `streamText` +- Create documentation aware agents using the pre-configured `Context7Agent` +- Build RAG pipelines that retrieve accurate, version specific code examples + +The package provides two main tools: + +- `resolveLibrary` - Searches Context7's database to find the correct library ID +- `getLibraryDocs` - Fetches documentation for a specific library with optional topic filtering + +## Quick Start + +### Install + +```bash +npm install @upstash/context7-tools-ai-sdk @upstash/context7-sdk ai zod +``` + +### Get API Key + +Get your API key from [Context7](https://context7.com) + +## Usage + +### Using Tools with `generateText` + +```typescript +import { resolveLibrary, getLibraryDocs } from "@upstash/context7-tools-ai-sdk"; +import { generateText, stepCountIs } from "ai"; +import { openai } from "@ai-sdk/openai"; + +const { text } = await generateText({ + model: openai("gpt-4o"), + prompt: "How do I use React Server Components?", + tools: { + resolveLibrary: resolveLibrary(), + getLibraryDocs: getLibraryDocs(), + }, + stopWhen: stepCountIs(5), +}); + +console.log(text); +``` + +### Using the Context7 Agent + +The package provides a pre-configured agent that handles the multi-step workflow automatically: + +```typescript +import { Context7Agent } from "@upstash/context7-tools-ai-sdk"; +import { anthropic } from "@ai-sdk/anthropic"; + +const agent = new Context7Agent({ + model: anthropic("claude-sonnet-4-20250514"), +}); + +const { text } = await agent.generate({ + prompt: "How do I set up routing in Next.js?", +}); + +console.log(text); +``` + +## Configuration + +### Environment Variables + +Set your API key via environment variable: + +```sh +CONTEXT7_API_KEY=ctx7sk-... +``` + +Then use tools and agents without explicit configuration: + +```typescript +const tool = resolveLibrary(); // Uses CONTEXT7_API_KEY automatically +``` + +## Docs + +See the [documentation](https://context7.com/docs/agentic-tools/ai-sdk/getting-started) for details. + +## Contributing + +### Running tests + +```sh +pnpm test +``` + +### Building + +```sh +pnpm build +``` diff --git a/packages/tools-ai-sdk/eslint.config.js b/packages/tools-ai-sdk/eslint.config.js new file mode 100644 index 00000000..aeb85ced --- /dev/null +++ b/packages/tools-ai-sdk/eslint.config.js @@ -0,0 +1,47 @@ +import { defineConfig } from "eslint/config"; +import tseslint from "typescript-eslint"; +import eslintPluginPrettier from "eslint-plugin-prettier"; + +export default defineConfig( + { + // Base ESLint configuration + ignores: ["node_modules/**", "build/**", "dist/**", ".git/**", ".github/**"], + }, + { + files: ["**/*.ts", "**/*.tsx"], + languageOptions: { + ecmaVersion: 2020, + sourceType: "module", + parser: tseslint.parser, + parserOptions: { + project: "./tsconfig.json", + tsconfigRootDir: import.meta.dirname, + }, + globals: { + // Add Node.js globals + process: "readonly", + require: "readonly", + module: "writable", + console: "readonly", + }, + }, + // Settings for all files + linterOptions: { + reportUnusedDisableDirectives: true, + }, + plugins: { + "@typescript-eslint": tseslint.plugin, + prettier: eslintPluginPrettier, + }, + rules: { + // TypeScript recommended rules + ...tseslint.configs.recommended.rules, + // TypeScript rules + "@typescript-eslint/explicit-module-boundary-types": "off", + "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }], + "@typescript-eslint/no-explicit-any": "warn", + // Prettier integration + "prettier/prettier": "error", + }, + } +); diff --git a/packages/tools-ai-sdk/package.json b/packages/tools-ai-sdk/package.json new file mode 100644 index 00000000..661cc1b7 --- /dev/null +++ b/packages/tools-ai-sdk/package.json @@ -0,0 +1,69 @@ +{ + "name": "@upstash/context7-tools-ai-sdk", + "version": "0.1.0", + "description": "Context7 tools for Vercel AI SDK", + "type": "module", + "main": "./dist/index.cjs", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "require": "./dist/index.cjs" + }, + "./agent": { + "types": "./dist/agent.d.ts", + "import": "./dist/agent.js", + "require": "./dist/agent.cjs" + } + }, + "files": [ + "dist" + ], + "scripts": { + "build": "tsup", + "dev": "tsup --watch", + "test": "vitest run", + "typecheck": "tsc --noEmit", + "clean": "rm -rf dist" + }, + "peerDependencies": { + "@upstash/context7-sdk": ">=0.1.0", + "ai": ">=5.0.0", + "zod": ">=3.24.0" + }, + "devDependencies": { + "@upstash/context7-sdk": "workspace:*", + "ai": "^5.0.0", + "zod": "^3.24.0", + "@ai-sdk/amazon-bedrock": "^3.0.55", + "@types/node": "^22.13.14", + "dotenv": "^17.2.3", + "tsup": "^8.5.1", + "typescript": "^5.8.2", + "vitest": "^4.0.13" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/upstash/context7.git", + "directory": "packages/tools-ai-sdk" + }, + "keywords": [ + "context7", + "ai-sdk", + "vercel", + "documentation", + "agent", + "upstash" + ], + "author": "Upstash", + "license": "MIT", + "bugs": { + "url": "https://github.com/upstash/context7/issues" + }, + "homepage": "https://github.com/upstash/context7#readme", + "publishConfig": { + "access": "public" + } +} diff --git a/packages/tools-ai-sdk/src/agents/context7.ts b/packages/tools-ai-sdk/src/agents/context7.ts new file mode 100644 index 00000000..817562da --- /dev/null +++ b/packages/tools-ai-sdk/src/agents/context7.ts @@ -0,0 +1,75 @@ +import { + Experimental_Agent as Agent, + type Experimental_AgentSettings as AgentSettings, + type ToolSet, + stepCountIs, +} from "ai"; +import { resolveLibrary, getLibraryDocs } from "@tools"; +import { AGENT_PROMPT } from "@prompts"; + +/** + * Configuration for Context7 agent. + */ +export interface Context7AgentConfig extends AgentSettings { + /** + * Context7 API key. If not provided, uses the CONTEXT7_API_KEY environment variable. + */ + apiKey?: string; + + /** + * Default maximum number of documentation results per request. + * @default 10 + */ + defaultMaxResults?: number; +} + +/** + * Context7 documentation search agent + * + * The agent follows a multi-step workflow: + * 1. Resolves library names to Context7 library IDs + * 2. Fetches documentation for the resolved library + * 3. Provides answers with code examples + * + * @example + * ```typescript + * import { Context7Agent } from '@upstash/context7-tools-ai-sdk'; + * import { anthropic } from '@ai-sdk/anthropic'; + * + * const agent = new Context7Agent({ + * model: anthropic('claude-sonnet-4-20250514'), + * apiKey: 'your-context7-api-key', + * }); + * + * const result = await agent.generate({ + * prompt: 'How do I use React Server Components?', + * }); + * ``` + */ +export class Context7Agent extends Agent { + constructor(config: Context7AgentConfig) { + const { + model, + stopWhen = stepCountIs(5), + system, + apiKey, + defaultMaxResults, + tools, + ...agentSettings + } = config; + + const context7Config = { apiKey, defaultMaxResults }; + + super({ + ...agentSettings, + model, + system: system || AGENT_PROMPT, + tools: { + ...tools, + resolveLibrary: resolveLibrary(context7Config), + getLibraryDocs: getLibraryDocs(context7Config), + }, + stopWhen, + }); + } +} diff --git a/packages/tools-ai-sdk/src/agents/index.ts b/packages/tools-ai-sdk/src/agents/index.ts new file mode 100644 index 00000000..d803381d --- /dev/null +++ b/packages/tools-ai-sdk/src/agents/index.ts @@ -0,0 +1 @@ +export { Context7Agent, type Context7AgentConfig } from "./context7"; diff --git a/packages/tools-ai-sdk/src/index.test.ts b/packages/tools-ai-sdk/src/index.test.ts new file mode 100644 index 00000000..58750d04 --- /dev/null +++ b/packages/tools-ai-sdk/src/index.test.ts @@ -0,0 +1,228 @@ +import { describe, test, expect } from "vitest"; +import { generateText, stepCountIs, tool } from "ai"; +import { createAmazonBedrock } from "@ai-sdk/amazon-bedrock"; +import { z } from "zod"; +import { + resolveLibrary, + getLibraryDocs, + Context7Agent, + SYSTEM_PROMPT, + AGENT_PROMPT, + RESOLVE_LIBRARY_DESCRIPTION, +} from "./index"; + +const bedrock = createAmazonBedrock({ + region: process.env.AWS_REGION, + apiKey: process.env.AWS_BEARER_TOKEN_BEDROCK, +}); + +describe("@upstash/context7-tools-ai-sdk", () => { + describe("Tool structure", () => { + test("resolveLibrary() should return a tool object with correct structure", () => { + const tool = resolveLibrary(); + + expect(tool).toBeDefined(); + expect(tool).toHaveProperty("execute"); + expect(tool).toHaveProperty("inputSchema"); + expect(tool).toHaveProperty("description"); + expect(tool.description).toContain("library"); + }); + + test("getLibraryDocs() should return a tool object with correct structure", () => { + const tool = getLibraryDocs(); + + expect(tool).toBeDefined(); + expect(tool).toHaveProperty("execute"); + expect(tool).toHaveProperty("inputSchema"); + expect(tool).toHaveProperty("description"); + expect(tool.description).toContain("documentation"); + }); + + test("tools should accept custom config", () => { + const resolveTool = resolveLibrary({ + apiKey: "ctx7sk-test-key", + }); + + const docsTool = getLibraryDocs({ + apiKey: "ctx7sk-test-key", + defaultMaxResults: 5, + }); + + expect(resolveTool).toHaveProperty("execute"); + expect(docsTool).toHaveProperty("execute"); + }); + }); + + describe("Tool usage with generateText", () => { + test("resolveLibrary tool should be called when searching for a library", async () => { + const result = await generateText({ + model: bedrock("anthropic.claude-3-haiku-20240307-v1:0"), + tools: { + resolveLibrary: resolveLibrary(), + }, + toolChoice: { type: "tool", toolName: "resolveLibrary" }, + stopWhen: stepCountIs(2), + prompt: "Search for 'react' library", + }); + + expect(result.toolCalls.length).toBeGreaterThan(0); + expect(result.toolCalls[0].toolName).toBe("resolveLibrary"); + expect(result.toolResults.length).toBeGreaterThan(0); + const toolResult = result.toolResults[0] as unknown as { output: { success: boolean } }; + expect(toolResult.output.success).toBe(true); + }, 30000); + + test("getLibraryDocs tool should fetch documentation", async () => { + const result = await generateText({ + model: bedrock("anthropic.claude-3-haiku-20240307-v1:0"), + tools: { + getLibraryDocs: getLibraryDocs(), + }, + toolChoice: { type: "tool", toolName: "getLibraryDocs" }, + stopWhen: stepCountIs(2), + prompt: "Fetch documentation for library ID '/facebook/react' with topic 'hooks'", + }); + + expect(result.toolCalls.length).toBeGreaterThan(0); + expect(result.toolCalls[0].toolName).toBe("getLibraryDocs"); + expect(result.toolResults.length).toBeGreaterThan(0); + const toolResult = result.toolResults[0] as unknown as { output: { success: boolean } }; + expect(toolResult.output.success).toBe(true); + }, 30000); + + test("both tools can work together in a multi-step flow", async () => { + const result = await generateText({ + model: bedrock("anthropic.claude-3-haiku-20240307-v1:0"), + tools: { + resolveLibrary: resolveLibrary(), + getLibraryDocs: getLibraryDocs(), + }, + stopWhen: stepCountIs(5), + prompt: + "First use resolveLibrary to find the Next.js library, then use getLibraryDocs to get documentation about routing", + }); + + const allToolCalls = result.steps.flatMap((step) => step.toolCalls); + const toolNames = allToolCalls.map((call) => call.toolName); + expect(toolNames).toContain("resolveLibrary"); + expect(toolNames).toContain("getLibraryDocs"); + }, 60000); + }); + + describe("Context7Agent class", () => { + test("should create an agent instance with model", () => { + const agent = new Context7Agent({ + model: bedrock("anthropic.claude-3-haiku-20240307-v1:0"), + }); + + expect(agent).toBeDefined(); + expect(agent).toHaveProperty("generate"); + expect(agent).toHaveProperty("stream"); + }); + + test("should accept custom stopWhen condition", () => { + const agent = new Context7Agent({ + model: bedrock("anthropic.claude-3-haiku-20240307-v1:0"), + stopWhen: stepCountIs(3), + }); + + expect(agent).toBeDefined(); + }); + + test("should accept custom system prompt", () => { + const agent = new Context7Agent({ + model: bedrock("anthropic.claude-3-haiku-20240307-v1:0"), + system: "Custom system prompt for testing", + }); + + expect(agent).toBeDefined(); + }); + + test("should accept Context7 config options", () => { + const agent = new Context7Agent({ + model: bedrock("anthropic.claude-3-haiku-20240307-v1:0"), + apiKey: "ctx7sk-test-key", + defaultMaxResults: 5, + }); + + expect(agent).toBeDefined(); + }); + + test("should accept additional tools alongside Context7 tools", () => { + const customTool = tool({ + description: "A custom test tool", + inputSchema: z.object({ + input: z.string().describe("Test input"), + }), + execute: async ({ input }) => ({ result: `processed: ${input}` }), + }); + + const agent = new Context7Agent({ + model: bedrock("anthropic.claude-3-haiku-20240307-v1:0"), + tools: { + customTool, + }, + }); + + expect(agent).toBeDefined(); + }); + + test("should generate response using agent workflow", async () => { + const agent = new Context7Agent({ + model: bedrock("anthropic.claude-3-haiku-20240307-v1:0"), + stopWhen: stepCountIs(5), + }); + + const result = await agent.generate({ + prompt: "Find the React library and get documentation about hooks", + }); + + expect(result).toBeDefined(); + expect(result.steps.length).toBeGreaterThan(0); + + const allToolCalls = result.steps.flatMap((step) => step.toolCalls); + const toolNames = allToolCalls.map((call) => call.toolName); + expect(toolNames).toContain("resolveLibrary"); + }, 60000); + + test("should include Context7 tools in generate result", async () => { + const agent = new Context7Agent({ + model: bedrock("anthropic.claude-3-haiku-20240307-v1:0"), + stopWhen: stepCountIs(5), + }); + + const result = await agent.generate({ + prompt: + "Use resolveLibrary to search for Next.js, then use getLibraryDocs to get routing documentation", + }); + + expect(result).toBeDefined(); + + const allToolCalls = result.steps.flatMap((step) => step.toolCalls); + const toolNames = allToolCalls.map((call) => call.toolName); + + expect(toolNames).toContain("resolveLibrary"); + expect(toolNames).toContain("getLibraryDocs"); + }, 60000); + }); + + describe("Prompt exports", () => { + test("should export SYSTEM_PROMPT", () => { + expect(SYSTEM_PROMPT).toBeDefined(); + expect(typeof SYSTEM_PROMPT).toBe("string"); + expect(SYSTEM_PROMPT.length).toBeGreaterThan(0); + }); + + test("should export AGENT_PROMPT", () => { + expect(AGENT_PROMPT).toBeDefined(); + expect(typeof AGENT_PROMPT).toBe("string"); + expect(AGENT_PROMPT).toContain("Context7"); + }); + + test("should export RESOLVE_LIBRARY_DESCRIPTION", () => { + expect(RESOLVE_LIBRARY_DESCRIPTION).toBeDefined(); + expect(typeof RESOLVE_LIBRARY_DESCRIPTION).toBe("string"); + expect(RESOLVE_LIBRARY_DESCRIPTION).toContain("library"); + }); + }); +}); diff --git a/packages/tools-ai-sdk/src/index.ts b/packages/tools-ai-sdk/src/index.ts new file mode 100644 index 00000000..8b24b1e3 --- /dev/null +++ b/packages/tools-ai-sdk/src/index.ts @@ -0,0 +1,23 @@ +// Agents +export { Context7Agent, type Context7AgentConfig } from "@agents"; + +// Tools +export { resolveLibrary, getLibraryDocs, type Context7ToolsConfig } from "@tools"; + +// Prompts +export { + SYSTEM_PROMPT, + AGENT_PROMPT, + RESOLVE_LIBRARY_DESCRIPTION, + GET_LIBRARY_DOCS_DESCRIPTION, +} from "@prompts"; + +// Re-export useful types from SDK +export type { + SearchResult, + SearchLibraryResponse, + CodeDocsResponse, + TextDocsResponse, + InfoDocsResponse, + Pagination, +} from "@upstash/context7-sdk"; diff --git a/packages/tools-ai-sdk/src/prompts/index.ts b/packages/tools-ai-sdk/src/prompts/index.ts new file mode 100644 index 00000000..407fb997 --- /dev/null +++ b/packages/tools-ai-sdk/src/prompts/index.ts @@ -0,0 +1,6 @@ +export { + SYSTEM_PROMPT, + AGENT_PROMPT, + RESOLVE_LIBRARY_DESCRIPTION, + GET_LIBRARY_DOCS_DESCRIPTION, +} from "./system"; diff --git a/packages/tools-ai-sdk/src/prompts/system.ts b/packages/tools-ai-sdk/src/prompts/system.ts new file mode 100644 index 00000000..44103ad6 --- /dev/null +++ b/packages/tools-ai-sdk/src/prompts/system.ts @@ -0,0 +1,81 @@ +/** + * System prompts for Context7 AI SDK agents + */ + +/** + * Basic documentation assistant prompt + */ +export const SYSTEM_PROMPT = `You are a documentation search assistant powered by Context7. + +Your role is to help users find accurate, up-to-date documentation for libraries and frameworks. + +When answering questions: +1. Search for the relevant library documentation +2. Provide code examples when available +3. Cite your sources by mentioning the library ID used`; + +/** + * Detailed multi-step workflow prompt for comprehensive documentation retrieval + */ +export const AGENT_PROMPT = `You are a documentation search assistant powered by Context7. + +CRITICAL WORKFLOW - YOU MUST FOLLOW THESE STEPS: + +Step 1: ALWAYS start by calling 'resolveLibrary' with the library name from the user's query + - Extract the main library/framework name (e.g., "React", "Next.js", "Vue") + - Call resolveLibrary with just the library name + - Review ALL the search results returned + +Step 2: Analyze the results from resolveLibrary and select the BEST library ID based on: + - Official sources (e.g., /reactjs/react.dev for React, /vercel/next.js for Next.js) + - Name similarity to what the user is looking for + - Description relevance + - Source reputation (High/Medium is better) + - Code snippet coverage (higher is better) + - Benchmark score (higher is better) + +Step 3: Call 'getLibraryDocs' with the selected library ID + - Use the exact library ID from the resolveLibrary results + - ALWAYS extract and include a relevant topic from the user's query (e.g., "Server-Side Rendering", "routing", "authentication") + - Start with page=1 (default) + +Step 4: If the documentation from page 1 isn't sufficient, call 'getLibraryDocs' again with page=2 + - Use the same library ID and the SAME topic from step 3 + - This gives you more comprehensive documentation + +Step 5: Provide a clear answer with code examples from the documentation + +IMPORTANT: +- You MUST call resolveLibrary first before calling getLibraryDocs +- Do NOT skip resolveLibrary - it helps you find the correct official documentation +- Always cite which library ID you used`; + +/** + * Library resolution tool description + */ +export const RESOLVE_LIBRARY_DESCRIPTION = `Resolves a package/product name to a Context7-compatible library ID and returns a list of matching libraries. + +You MUST call this function before 'getLibraryDocs' to obtain a valid Context7-compatible library ID UNLESS the user explicitly provides a library ID in the format '/org/project' or '/org/project/version' in their query. + +Selection Process: +1. Analyze the query to understand what library/package the user is looking for +2. Return the most relevant match based on: +- Name similarity to the query (exact matches prioritized) +- Description relevance to the query's intent +- Documentation coverage (prioritize libraries with higher Code Snippet counts) +- Source reputation (consider libraries with High or Medium reputation more authoritative) +- Benchmark Score: Quality indicator (100 is the highest score) + +Response Format: +- Return the selected library ID in a clearly marked section +- Provide a brief explanation for why this library was chosen +- If multiple good matches exist, acknowledge this but proceed with the most relevant one +- If no good matches exist, clearly state this and suggest query refinements + +For ambiguous queries, request clarification before proceeding with a best-guess match.`; + +/** + * Get library docs tool description + */ +export const GET_LIBRARY_DOCS_DESCRIPTION = + "Fetches up-to-date documentation for a library. You must call 'resolveLibrary' first to obtain the exact Context7-compatible library ID required to use this tool, UNLESS the user explicitly provides a library ID in the format '/org/project' or '/org/project/version' in their query. Use mode='code' (default) for API references and code examples, or mode='info' for conceptual guides, narrative information, and architectural questions."; diff --git a/packages/tools-ai-sdk/src/tools/get-library-docs.ts b/packages/tools-ai-sdk/src/tools/get-library-docs.ts new file mode 100644 index 00000000..04d28ab2 --- /dev/null +++ b/packages/tools-ai-sdk/src/tools/get-library-docs.ts @@ -0,0 +1,115 @@ +import { tool } from "ai"; +import { z } from "zod"; +import { Context7 } from "@upstash/context7-sdk"; +import type { Context7ToolsConfig } from "./types"; +import { GET_LIBRARY_DOCS_DESCRIPTION } from "@prompts"; + +/** + * Tool to fetch documentation for a library using its Context7 library ID. + * + * Can be called with or without configuration. Uses CONTEXT7_API_KEY environment + * variable for authentication when no API key is provided. + * + * @param config Optional configuration options + * @returns AI SDK tool for fetching library documentation + * + * @example + * ```typescript + * import { resolveLibrary, getLibraryDocs } from '@upstash/context7-tools-ai-sdk'; + * import { generateText, stepCountIs } from 'ai'; + * import { openai } from '@ai-sdk/openai'; + * + * const { text } = await generateText({ + * model: openai('gpt-4o'), + * prompt: 'Find React documentation about hooks', + * tools: { + * resolveLibrary: resolveLibrary(), + * getLibraryDocs: getLibraryDocs(), + * }, + * stopWhen: stepCountIs(5), + * }); + * ``` + */ +export function getLibraryDocs(config: Context7ToolsConfig = {}) { + const { apiKey, defaultMaxResults = 10 } = config; + const getClient = () => new Context7({ apiKey }); + + return tool({ + description: GET_LIBRARY_DOCS_DESCRIPTION, + inputSchema: z.object({ + libraryId: z + .string() + .describe( + "Exact Context7-compatible library ID (e.g., '/mongodb/docs', '/vercel/next.js', '/supabase/supabase', '/vercel/next.js/v14.3.0-canary.87') retrieved from 'resolveLibrary' or directly from user query in the format '/org/project' or '/org/project/version'." + ), + mode: z + .enum(["code", "info"]) + .optional() + .default("code") + .describe( + "Documentation mode: 'code' for API references and code examples (default), 'info' for conceptual guides, narrative information, and architectural questions." + ), + topic: z + .string() + .optional() + .describe("Topic to focus documentation on (e.g., 'hooks', 'routing')."), + page: z + .number() + .int() + .min(1) + .max(10) + .optional() + .describe( + "Page number for pagination (start: 1, default: 1). If the context is not sufficient, try page=2, page=3, page=4, etc. with the same topic." + ), + }), + execute: async ({ + libraryId, + mode = "code", + topic, + page = 1, + }: { + libraryId: string; + mode?: "code" | "info"; + topic?: string; + page?: number; + }) => { + try { + const client = getClient(); + const baseOptions = { + page, + limit: defaultMaxResults, + topic: topic?.trim() || undefined, + }; + + const response = + mode === "info" + ? await client.getDocs(libraryId, { ...baseOptions, mode: "info" }) + : await client.getDocs(libraryId, { ...baseOptions, mode: "code" }); + + if (!response.snippets?.length) { + return { + success: false, + error: + "Documentation not found or not finalized for this library. This might have happened because you used an invalid Context7-compatible library ID. To get a valid Context7-compatible library ID, use the 'resolveLibrary' with the package name you wish to retrieve documentation for.", + libraryId, + }; + } + + return { + success: true, + libraryId, + snippets: response.snippets, + pagination: response.pagination, + totalTokens: response.totalTokens, + }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : "Failed to fetch documentation", + libraryId, + }; + } + }, + }); +} diff --git a/packages/tools-ai-sdk/src/tools/index.ts b/packages/tools-ai-sdk/src/tools/index.ts new file mode 100644 index 00000000..5d93fe7c --- /dev/null +++ b/packages/tools-ai-sdk/src/tools/index.ts @@ -0,0 +1,3 @@ +export { resolveLibrary } from "./resolve-library"; +export { getLibraryDocs } from "./get-library-docs"; +export type { Context7ToolsConfig } from "./types"; diff --git a/packages/tools-ai-sdk/src/tools/resolve-library.ts b/packages/tools-ai-sdk/src/tools/resolve-library.ts new file mode 100644 index 00000000..1c9771b0 --- /dev/null +++ b/packages/tools-ai-sdk/src/tools/resolve-library.ts @@ -0,0 +1,71 @@ +import { tool } from "ai"; +import { z } from "zod"; +import { Context7 } from "@upstash/context7-sdk"; +import { RESOLVE_LIBRARY_DESCRIPTION } from "@prompts"; +import type { Context7ToolsConfig } from "./types"; + +/** + * Tool to resolve a library name to a Context7-compatible library ID. + * + * Can be called with or without configuration. Uses CONTEXT7_API_KEY environment + * variable for authentication when no API key is provided. + * + * @param config Optional configuration options + * @returns AI SDK tool for library resolution + * + * @example + * ```typescript + * import { resolveLibrary, getLibraryDocs } from '@upstash/context7-tools-ai-sdk'; + * import { generateText, stepCountIs } from 'ai'; + * import { openai } from '@ai-sdk/openai'; + * + * const { text } = await generateText({ + * model: openai('gpt-4o'), + * prompt: 'Find React documentation about hooks', + * tools: { + * resolveLibrary: resolveLibrary(), + * getLibraryDocs: getLibraryDocs(), + * }, + * stopWhen: stepCountIs(5), + * }); + * ``` + */ +export function resolveLibrary(config: Context7ToolsConfig = {}) { + const { apiKey } = config; + const getClient = () => new Context7({ apiKey }); + + return tool({ + description: RESOLVE_LIBRARY_DESCRIPTION, + inputSchema: z.object({ + libraryName: z + .string() + .describe("Library name to search for and retrieve a Context7-compatible library ID."), + }), + execute: async ({ libraryName }: { libraryName: string }) => { + try { + const client = getClient(); + const response = await client.searchLibrary(libraryName); + + if (!response.results || response.results.length === 0) { + return { + success: false, + error: "No libraries found matching your query.", + suggestions: "Try a different search term or check the library name.", + }; + } + + return { + success: true, + results: response.results, + totalResults: response.results.length, + }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : "Failed to search libraries", + suggestions: "Check your API key and try again, or try a different search term.", + }; + } + }, + }); +} diff --git a/packages/tools-ai-sdk/src/tools/types.ts b/packages/tools-ai-sdk/src/tools/types.ts new file mode 100644 index 00000000..380d012f --- /dev/null +++ b/packages/tools-ai-sdk/src/tools/types.ts @@ -0,0 +1,14 @@ +/** + * Configuration for Context7 tools + */ +export interface Context7ToolsConfig { + /** + * Context7 API key. If not provided, will use CONTEXT7_API_KEY environment variable. + */ + apiKey?: string; + /** + * Default maximum number of documentation results per page. + * @default 10 + */ + defaultMaxResults?: number; +} diff --git a/packages/tools-ai-sdk/tsconfig.json b/packages/tools-ai-sdk/tsconfig.json new file mode 100644 index 00000000..67cbe80f --- /dev/null +++ b/packages/tools-ai-sdk/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "lib": ["ESNext"], + "module": "esnext", + "target": "esnext", + "moduleResolution": "bundler", + "moduleDetection": "force", + "allowImportingTsExtensions": true, + "noEmit": true, + "strict": true, + "downlevelIteration": true, + "skipLibCheck": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "allowJs": true, + "baseUrl": ".", + "paths": { + "@tools": ["./src/tools/index.ts"], + "@agents": ["./src/agents/index.ts"], + "@prompts": ["./src/prompts/index.ts"] + } + }, + "include": ["src/**/*", "tsup.config.ts", "vitest.config.ts", "eslint.config.js"], + "exclude": ["node_modules", "dist", "examples"] +} diff --git a/packages/tools-ai-sdk/tsup.config.ts b/packages/tools-ai-sdk/tsup.config.ts new file mode 100644 index 00000000..590d55a0 --- /dev/null +++ b/packages/tools-ai-sdk/tsup.config.ts @@ -0,0 +1,18 @@ +import { defineConfig } from "tsup"; + +export default defineConfig({ + entry: { + index: "./src/index.ts", + agent: "./src/agents/index.ts", + }, + format: ["cjs", "esm"], + clean: true, + dts: true, + esbuildOptions(options) { + options.alias = { + "@tools": "./src/tools/index.ts", + "@agents": "./src/agents/index.ts", + "@prompts": "./src/prompts/index.ts", + }; + }, +}); diff --git a/packages/tools-ai-sdk/vitest.config.ts b/packages/tools-ai-sdk/vitest.config.ts new file mode 100644 index 00000000..2b71b676 --- /dev/null +++ b/packages/tools-ai-sdk/vitest.config.ts @@ -0,0 +1,20 @@ +import { defineConfig } from "vitest/config"; +import path from "path"; +import { config } from "dotenv"; + +config(); + +export default defineConfig({ + test: { + globals: true, + environment: "node", + include: ["src/**/*.test.ts"], + }, + resolve: { + alias: { + "@tools": path.resolve(__dirname, "./src/tools/index.ts"), + "@agents": path.resolve(__dirname, "./src/agents/index.ts"), + "@prompts": path.resolve(__dirname, "./src/prompts/index.ts"), + }, + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 58cde648..34d2b4b6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -83,10 +83,79 @@ importers: version: 5.9.3 vitest: specifier: ^4.0.13 - version: 4.0.14(@types/node@22.19.1) + version: 4.0.14(@opentelemetry/api@1.9.0)(@types/node@22.19.1) + + packages/tools-ai-sdk: + devDependencies: + '@ai-sdk/amazon-bedrock': + specifier: ^3.0.55 + version: 3.0.62(zod@3.25.76) + '@types/node': + specifier: ^22.13.14 + version: 22.19.1 + '@upstash/context7-sdk': + specifier: workspace:* + version: link:../sdk + ai: + specifier: ^5.0.0 + version: 5.0.104(zod@3.25.76) + dotenv: + specifier: ^17.2.3 + version: 17.2.3 + tsup: + specifier: ^8.5.1 + version: 8.5.1(postcss@8.5.6)(typescript@5.9.3) + typescript: + specifier: ^5.8.2 + version: 5.9.3 + vitest: + specifier: ^4.0.13 + version: 4.0.14(@opentelemetry/api@1.9.0)(@types/node@22.19.1) + zod: + specifier: ^3.24.0 + version: 3.25.76 packages: + '@ai-sdk/amazon-bedrock@3.0.62': + resolution: {integrity: sha512-vVtndaj5zfHmgw8NSqN4baFDbFDTBZP6qufhKfqSNLtygEm8+8PL9XQX9urgzSzU3zp+zi3AmNNemvKLkkqblg==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/anthropic@2.0.50': + resolution: {integrity: sha512-21PaHfoLmouOXXNINTsZJsMw+wE5oLR2He/1kq/sKokTVKyq7ObGT1LDk6ahwxaz/GoaNaGankMh+EgVcdv2Cw==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/gateway@2.0.17': + resolution: {integrity: sha512-oVAG6q72KsjKlrYdLhWjRO7rcqAR8CjokAbYuyVZoCO4Uh2PH/VzZoxZav71w2ipwlXhHCNaInGYWNs889MMDA==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/provider-utils@3.0.18': + resolution: {integrity: sha512-ypv1xXMsgGcNKUP+hglKqtdDuMg68nWHucPPAhIENrbFAI+xCHiqPVN8Zllxyv1TNZwGWUghPxJXU+Mqps0YRQ==} + 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'} + + '@aws-crypto/crc32@5.2.0': + resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/util@5.2.0': + resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} + + '@aws-sdk/types@3.936.0': + resolution: {integrity: sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg==} + engines: {node: '>=18.0.0'} + '@babel/runtime@7.28.4': resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} engines: {node: '>=6.9.0'} @@ -561,6 +630,10 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@opentelemetry/api@1.9.0': + resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} + engines: {node: '>=8.0.0'} + '@pkgr/core@0.2.9': resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} @@ -675,6 +748,42 @@ packages: cpu: [x64] os: [win32] + '@smithy/eventstream-codec@4.2.5': + resolution: {integrity: sha512-Ogt4Zi9hEbIP17oQMd68qYOHUzmH47UkK7q7Gl55iIm9oKt27MUGrC5JfpMroeHjdkOliOA4Qt3NQ1xMq/nrlA==} + engines: {node: '>=18.0.0'} + + '@smithy/is-array-buffer@2.2.0': + resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} + engines: {node: '>=14.0.0'} + + '@smithy/is-array-buffer@4.2.0': + resolution: {integrity: sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==} + engines: {node: '>=18.0.0'} + + '@smithy/types@4.9.0': + resolution: {integrity: sha512-MvUbdnXDTwykR8cB1WZvNNwqoWVaTRA0RLlLmf/cIFNMM2cKWz01X4Ly6SMC4Kks30r8tT3Cty0jmeWfiuyHTA==} + engines: {node: '>=18.0.0'} + + '@smithy/util-buffer-from@2.2.0': + resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} + engines: {node: '>=14.0.0'} + + '@smithy/util-buffer-from@4.2.0': + resolution: {integrity: sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==} + engines: {node: '>=18.0.0'} + + '@smithy/util-hex-encoding@4.2.0': + resolution: {integrity: sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-utf8@2.3.0': + resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} + engines: {node: '>=14.0.0'} + + '@smithy/util-utf8@4.2.0': + resolution: {integrity: sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==} + engines: {node: '>=18.0.0'} + '@standard-schema/spec@1.0.0': resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} @@ -788,6 +897,10 @@ packages: resolution: {integrity: sha512-SIV3/6eftCy1bNzCQoPmbWsRLujS8t5iDIZ4spZOBHqrM+yfX2ogg8Tt3PDTAVKw3sSCiUgg30uOAvK2r9zGjQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@vercel/oidc@3.0.5': + resolution: {integrity: sha512-fnYhv671l+eTTp48gB4zEsTW/YtRgRPnkI2nT7x6qw5rkI1Lq2hTmQIpHPgyThI0znLK+vX2n9XxKdXZ7BUbbw==} + engines: {node: '>= 20'} + '@vitest/expect@4.0.14': resolution: {integrity: sha512-RHk63V3zvRiYOWAV0rGEBRO820ce17hz7cI2kDmEdfQsBjT2luEKB5tCOc91u1oSQoUOZkSv3ZyzkdkSLD7lKw==} @@ -831,6 +944,12 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + ai@5.0.104: + resolution: {integrity: sha512-MZOkL9++nY5PfkpWKBR3Rv+Oygxpb9S16ctv8h91GvrSif7UnNEdPMVZe3bUyMd2djxf0AtBk/csBixP0WwWZQ==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + ajv-formats@3.0.1: resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} peerDependencies: @@ -874,6 +993,9 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} + aws4fetch@1.0.20: + resolution: {integrity: sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g==} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -1380,6 +1502,9 @@ packages: json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-schema@0.4.0: + resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} + json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} @@ -1840,6 +1965,9 @@ packages: ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + tsup@8.5.1: resolution: {integrity: sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==} engines: {node: '>=18'} @@ -2009,6 +2137,57 @@ packages: snapshots: + '@ai-sdk/amazon-bedrock@3.0.62(zod@3.25.76)': + dependencies: + '@ai-sdk/anthropic': 2.0.50(zod@3.25.76) + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.18(zod@3.25.76) + '@smithy/eventstream-codec': 4.2.5 + '@smithy/util-utf8': 4.2.0 + aws4fetch: 1.0.20 + zod: 3.25.76 + + '@ai-sdk/anthropic@2.0.50(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.18(zod@3.25.76) + zod: 3.25.76 + + '@ai-sdk/gateway@2.0.17(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.18(zod@3.25.76) + '@vercel/oidc': 3.0.5 + zod: 3.25.76 + + '@ai-sdk/provider-utils@3.0.18(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 2.0.0 + '@standard-schema/spec': 1.0.0 + eventsource-parser: 3.0.6 + zod: 3.25.76 + + '@ai-sdk/provider@2.0.0': + dependencies: + json-schema: 0.4.0 + + '@aws-crypto/crc32@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.936.0 + tslib: 2.8.1 + + '@aws-crypto/util@5.2.0': + dependencies: + '@aws-sdk/types': 3.936.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-sdk/types@3.936.0': + dependencies: + '@smithy/types': 4.9.0 + tslib: 2.8.1 + '@babel/runtime@7.28.4': {} '@changesets/apply-release-plan@7.0.13': @@ -2435,6 +2614,8 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.1 + '@opentelemetry/api@1.9.0': {} + '@pkgr/core@0.2.9': {} '@rollup/rollup-android-arm-eabi@4.53.3': @@ -2503,6 +2684,49 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.53.3': optional: true + '@smithy/eventstream-codec@4.2.5': + dependencies: + '@aws-crypto/crc32': 5.2.0 + '@smithy/types': 4.9.0 + '@smithy/util-hex-encoding': 4.2.0 + tslib: 2.8.1 + + '@smithy/is-array-buffer@2.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/is-array-buffer@4.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/types@4.9.0': + dependencies: + tslib: 2.8.1 + + '@smithy/util-buffer-from@2.2.0': + dependencies: + '@smithy/is-array-buffer': 2.2.0 + tslib: 2.8.1 + + '@smithy/util-buffer-from@4.2.0': + dependencies: + '@smithy/is-array-buffer': 4.2.0 + tslib: 2.8.1 + + '@smithy/util-hex-encoding@4.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/util-utf8@2.3.0': + dependencies: + '@smithy/util-buffer-from': 2.2.0 + tslib: 2.8.1 + + '@smithy/util-utf8@4.2.0': + dependencies: + '@smithy/util-buffer-from': 4.2.0 + tslib: 2.8.1 + '@standard-schema/spec@1.0.0': {} '@types/body-parser@1.19.6': @@ -2660,6 +2884,8 @@ snapshots: '@typescript-eslint/types': 8.47.0 eslint-visitor-keys: 4.2.1 + '@vercel/oidc@3.0.5': {} + '@vitest/expect@4.0.14': dependencies: '@standard-schema/spec': 1.0.0 @@ -2710,6 +2936,14 @@ snapshots: acorn@8.15.0: {} + ai@5.0.104(zod@3.25.76): + dependencies: + '@ai-sdk/gateway': 2.0.17(zod@3.25.76) + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.18(zod@3.25.76) + '@opentelemetry/api': 1.9.0 + zod: 3.25.76 + ajv-formats@3.0.1(ajv@8.17.1): optionalDependencies: ajv: 8.17.1 @@ -2748,6 +2982,8 @@ snapshots: assertion-error@2.0.1: {} + aws4fetch@1.0.20: {} + balanced-match@1.0.2: {} better-path-resolve@1.0.0: @@ -3296,6 +3532,8 @@ snapshots: json-schema-traverse@1.0.0: {} + json-schema@0.4.0: {} + json-stable-stringify-without-jsonify@1.0.1: {} jsonfile@4.0.0: @@ -3724,6 +3962,8 @@ snapshots: ts-interface-checker@0.1.13: {} + tslib@2.8.1: {} + tsup@8.5.1(postcss@8.5.6)(typescript@5.9.3): dependencies: bundle-require: 5.1.0(esbuild@0.27.0) @@ -3803,7 +4043,7 @@ snapshots: '@types/node': 22.19.1 fsevents: 2.3.3 - vitest@4.0.14(@types/node@22.19.1): + vitest@4.0.14(@opentelemetry/api@1.9.0)(@types/node@22.19.1): dependencies: '@vitest/expect': 4.0.14 '@vitest/mocker': 4.0.14(vite@7.2.4(@types/node@22.19.1)) @@ -3826,6 +4066,7 @@ snapshots: vite: 7.2.4(@types/node@22.19.1) why-is-node-running: 2.3.0 optionalDependencies: + '@opentelemetry/api': 1.9.0 '@types/node': 22.19.1 transitivePeerDependencies: - jiti