From 78b69d773d35f7f36010a40d2414a467307be32c Mon Sep 17 00:00:00 2001 From: enesgules Date: Thu, 27 Aug 2026 01:08:40 +0300 Subject: [PATCH 1/2] Fix generated llms page inventory --- llms-full.txt | 932 ------------------------------------------ llms.txt | 34 -- llms/README.md | 9 +- llms/src/build.ts | 21 +- llms/src/generator.ts | 3 + 5 files changed, 24 insertions(+), 975 deletions(-) diff --git a/llms-full.txt b/llms-full.txt index 6a318dc28..3129f376f 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -280,9 +280,6 @@ upstash qstash enable-prodpack --qstash-id $QSTASH_ID upstash qstash disable-prodpack --qstash-id $QSTASH_ID ``` -# llms.txt -Source: https://upstash.com/docs/agent-resources/llms-txt - # MCP Server Source: https://upstash.com/docs/agent-resources/mcp @@ -3904,24 +3901,6 @@ Ready-to-run examples for popular open-source agents: * The command must be a binary name from `PATH` or an absolute path under `/workspace/home/` or `/home/boxuser/`. * The command runs as `boxuser` inside the existing box sandbox. -# Aider -Source: https://upstash.com/docs/box/overall/custom-harness/aider - -# CrewAI -Source: https://upstash.com/docs/box/overall/custom-harness/crewai - -# Gemini -Source: https://upstash.com/docs/box/overall/custom-harness/gemini - -# Goose -Source: https://upstash.com/docs/box/overall/custom-harness/goose - -# Pi -Source: https://upstash.com/docs/box/overall/custom-harness/pi - -# Pydantic AI -Source: https://upstash.com/docs/box/overall/custom-harness/pydantic-ai - # Ephemeral Box Source: https://upstash.com/docs/box/overall/ephemeral-box @@ -6716,11 +6695,6 @@ box.delete() The SDK still supports `getPreviewUrl`, `listPreviews`, and `deletePreview`, but they are deprecated aliases for `getPublicURL`, `listPublicURLs`, and `deletePublicURL`. -# Pricing & Limits -Source: https://upstash.com/docs/box/overall/pricing - -Please check our [pricing page](https://upstash.com/pricing/box) for the most up-to-date information on pricing and limits. - # Quickstart Source: https://upstash.com/docs/box/overall/quickstart @@ -9763,9 +9737,6 @@ func main() { ``` -# Terraform -Source: https://upstash.com/docs/devops/terraform - # upstash_qstash_endpoint_data Source: https://upstash.com/docs/devops/terraform/data_sources/upstash_qstash_endpoint_data @@ -15876,21 +15847,6 @@ Learn more about different states [here](/docs/qstash/howto/debug-logs). -# llms.txt -Source: https://upstash.com/docs/qstash/overall/llms-txt - -# Pricing & Limits -Source: https://upstash.com/docs/qstash/overall/pricing - -Please check our [pricing page](https://upstash.com/pricing/qstash) for the most up-to-date information on pricing and limits. - -# Roadmap -Source: https://upstash.com/docs/qstash/overall/roadmap - - - We have moved the roadmap and the changelog to [Github Discussions](https://github.com/orgs/upstash/discussions) starting from October 2025.Now you can follow `In Progress` features. You can see that your `Feature Requests` are recorded. You can vote for them and comment your specific use-cases to shape the feature to your needs. - - # Use Cases Source: https://upstash.com/docs/qstash/overall/usecases @@ -73474,24 +73430,6 @@ Manage Upstash Redis databases from Claude and other AI tools by using our [MCP -# llms.txt -Source: https://upstash.com/docs/redis/overall/llms-txt - -# Pricing & Limits -Source: https://upstash.com/docs/redis/overall/pricing - -# Pricing & Limits -Source: https://upstash.com/docs/redis/overall/pricingold - -# Python SDK -Source: https://upstash.com/docs/redis/overall/pythonredis - -# Rate Limit SDK -Source: https://upstash.com/docs/redis/overall/ratelimit - -# Typescript SDK -Source: https://upstash.com/docs/redis/overall/redis - # Use Cases Source: https://upstash.com/docs/redis/overall/usecases @@ -73615,297 +73553,6 @@ We believe that Upstash is the best storage for your Lambda Functions because: - [Pages Router](https://upstash.com/docs/redis/quickstarts/vercel-functions-pages-router.md) - [Vercel Python Runtime](https://upstash.com/docs/redis/quickstarts/vercel-python-runtime.md) -# Agent Analytics -Source: https://upstash.com/docs/redis/sdks/agent-analytics - -# Vercel AI SDK Memory, RAG & Chat History with Redis -Source: https://upstash.com/docs/redis/sdks/agentkit/ai-sdk - -[Upstash AgentKit](https://github.com/upstash/agentkit) builds AI agents on Upstash Redis: memory, -conversation history, caching, and RAG, with no separate vector database. The semantic features run on -[Upstash Redis Search](/docs/redis/search/introduction) and its `$smart` fuzzy operator. - -`@upstash/agentkit-ai-sdk` is the [Vercel AI SDK](https://ai-sdk.dev) adapter — drop-ins for -`generateText` / `streamText`. `redis` defaults to `Redis.fromEnv()`, so you import only from this -package. - -| Import | Feature | -| --- | --- | -| `createChatHistory` | Durable chat history on Redis Search — save, list, and `$smart`-search a user's transcripts. | -| `createMemoryTools` | `recall_memory` + `save_memory` tools so the model reads and writes long-term memory. | -| `createSearchTools` | `search` / `aggregate` / `count` tools over a Redis Search index (this is how you do RAG). | -| `createRateLimit` | A configured Upstash Ratelimit to call before the model. | -| `cachedTools` | Memoize a map of AI SDK tools' results in Redis. | - -```bash -npm install @upstash/agentkit-ai-sdk @upstash/redis ai -``` - - - AgentKit reads `UPSTASH_REDIS_REST_URL` / `UPSTASH_REDIS_REST_TOKEN` from the environment by default. - Pass your own `@upstash/redis` client as `redis` to any helper to override. - - -## How to store chat history with the AI SDK - -A Redis-backed `ChatHistory`, the durable source of truth for your conversations. `userId` -comes from your auth session; `chatId` is the `useChat` id that the client posts. Save the full -transcript from your route's `onFinish`: - -```ts -// app/api/chat/route.ts -import { convertToModelMessages, createUIMessageStreamResponse, streamText, toUIMessageStream } from "ai"; -import { createChatHistory } from "@upstash/agentkit-ai-sdk"; - -const history = createChatHistory(); - -export async function POST(req: Request) { - const userId = await getSessionUserId(req); // your auth session, never a client-sent id - const { id: chatId, messages } = await req.json(); // useChat posts its chat id + the full transcript - - const result = streamText({ model, messages: convertToModelMessages(messages) }); - - return createUIMessageStreamResponse({ - stream: toUIMessageStream({ - stream: result.stream, - originalMessages: messages, - onFinish: ({ messages }) => - history.saveChat({ userId, sessionId: chatId, messages, title: "New chat" }), - }), - }); -} -``` - -To load a chat, take `chatId` from the page route and `userId` from the session, then seed `useChat`: - -```ts -const chat = await history.getChat({ userId, sessionId: chatId }); // full transcript, or null -const chats = await history.listChats({ userId, limit: 50 }); // summaries, no messages -const hits = await history.searchChats({ userId, query: "headphones", target: "both", limit: 20 }); -// client: useChat({ id: chatId, messages: chat?.messages ?? [] }) -``` - - - - ```ts - createChatHistory({ - redis, // optional: defaults to Redis.fromEnv() - prefix: "agentkit:chat", // optional: base key prefix - indexName: "agentkit_chat", // optional: index name (defaults to the prefix) - ttlSeconds: 60 * 60 * 24 * 30, // optional: per-chat TTL (default: no expiry) - }); - ``` - - Each chat is one JSON doc at `agentkit:chat::` (keyed per user, so two users - can't collide on a `sessionId`), indexed over `userId` + `sessionId` (filters) and `userMessages` + - `modelMessages` (`$smart` fuzzy text); the raw `messages` array rides along unindexed. `saveChat` - overwrites the **whole** array (no delta merge) — `useChat` sends the full conversation. Other - methods: `getChat` / `deleteChat` (`{ userId, sessionId }`), `listChats` / `searchChats` - (`{ userId }`). - - - Every method takes a single object; `userId` is **required, non-empty, and may not contain `:`**. - Derive it from a **verified server-side auth source** — the subject/user id from your auth provider - (Clerk, Auth.js/NextAuth, Supabase Auth, Auth0, …) — and **never from a client-supplied header, - query param, or body** (read it from the session in your route). A chat can't be read or - overwritten under a different `userId`. - - - -## How to add agent memory with the AI SDK - -`recall_memory` and `save_memory` tools so the model reads and writes its own long-term memory. - -```ts -import { createMemoryTools } from "@upstash/agentkit-ai-sdk"; -import { generateText, stepCountIs } from "ai"; - -const tools = createMemoryTools({ userId }); - -await generateText({ model, tools, stopWhen: stepCountIs(5), prompt: "What do you know about me?" }); -``` - - - * **`userId`** _(required)_ — a string, or `(input, options) => string`. - * `redis` — defaults to `Redis.fromEnv()`. - * `topK` — max memories `recall` returns. - * `minScore` — BM25 relevance floor. - * `recallToolName` / `saveToolName` — override the tool names. - - `userId` is the only tenant boundary (required, non-empty, no `:`). Derive it from a **verified - server-side auth source** (Clerk, Auth.js/NextAuth, Supabase Auth, Auth0, …) — never a - client-supplied value. Memories are stored at `agentkit:memory::`. - - -## How to add RAG with the AI SDK - -`search` / `aggregate` / `count` tools over an Upstash Redis Search index; the model-facing -descriptions are generated from your schema. - -```ts -import { s } from "@upstash/redis"; -import { createSearchTools } from "@upstash/agentkit-ai-sdk"; -import { generateText, stepCountIs } from "ai"; - -const schema = s.object({ name: s.string(), age: s.number(), city: s.string().noTokenize() }); -const tools = createSearchTools({ schema, indexName: "users" }); - -await generateText({ model, tools, stopWhen: stepCountIs(5), prompt: "How many users named Ada live in London?" }); -``` - - - * **`schema`** _(required)_ — built with `s` from `@upstash/redis`. - * `redis` — defaults to `Redis.fromEnv()`. - * `indexName` — defaults to `"agentkit:search"`. - * `prefix` — key prefix for indexed JSON docs (defaults to `":"`). - * `defaultLimit` — default page size for `search` (10). - - The index is created (and `waitIndexing`-ed) reactively on first use — no setup step. - - -## How to add rate limiting with the AI SDK - -A configured [Upstash Ratelimit](/docs/redis/sdks/ratelimit-ts/overview). Call `.limit(identifier)` before -the model and short-circuit when over the limit. - -```ts -import { createRateLimit, Ratelimit } from "@upstash/agentkit-ai-sdk"; - -const ratelimit = createRateLimit({ limiter: Ratelimit.slidingWindow(20, "1 m") }); - -const { success } = await ratelimit.limit(userId); -if (!success) throw new Error("rate limited"); // or return a 429 from your route -``` - - - * **`limiter`** _(required)_ — e.g. `Ratelimit.slidingWindow(20, "1 m")` or `fixedWindow(...)`. - * `redis` — defaults to `Redis.fromEnv()`. - * `prefix` — base key prefix; keys are `:` (default `agentkit:rateLimit`). - - There's no model wrapper. Pass a per-user `identifier` to `.limit()` to throttle per user. - - -## How to cache tools with the AI SDK - -Memoize a map of AI SDK tools' results in Redis. Each tool is cached under its map key, scoped to -`userId`. - -```ts -import { z } from "zod"; -import { generateText, tool } from "ai"; -import { cachedTools } from "@upstash/agentkit-ai-sdk"; - -const tools = cachedTools( - { - getWeather: tool({ - description: "Get the weather for a city", - inputSchema: z.object({ city: z.string() }), - execute: async ({ city }) => fetchWeather(city), - }), - }, - { userId }, -); - -await generateText({ model, tools, prompt: "What's the weather in Paris?" }); -``` - - - Pass tools built with the AI SDK's `tool()` (so each keeps full input/output inference). Second arg: - - * **`userId`** _(required)_ — a string, or `(input, options) => string`; scopes every entry to this user. - * `redis` — defaults to `Redis.fromEnv()`. - * `ttlSeconds` — default per-result TTL for every tool. - - Cache keys are `agentkit:toolCache:::` — the `toolName` is the map - key, so you never pass a name yourself. - - -## How to put it all together with the AI SDK - -A single `streamText` route can wire every feature: rate limit first, then memory, search, and cached -tools, persisting the whole conversation in `onFinish`: - -```ts -// app/api/chat/route.ts -import { openai } from "@ai-sdk/openai"; -import { - convertToModelMessages, - createUIMessageStreamResponse, - streamText, - stepCountIs, - toUIMessageStream, - tool, - type UIMessage, -} from "ai"; -import { z } from "zod"; -import { s, Redis } from "@upstash/redis"; -import { - Ratelimit, - cachedTools, - createChatHistory, - createMemoryTools, - createRateLimit, - createSearchTools, -} from "@upstash/agentkit-ai-sdk"; - -export async function POST(req: Request) { - const { id, messages } = (await req.json()) as { id: string; messages: UIMessage[] }; - const redis = Redis.fromEnv(); - // Derive this from your verified auth session in production (Clerk, Auth.js, …), never a client value. - const userId = "user-123"; - - // 1. Rate limit (by user) before any model work. - const ratelimit = createRateLimit({ redis, limiter: Ratelimit.slidingWindow(30, "1 m") }); - const { success } = await ratelimit.limit(userId); - if (!success) return new Response("Rate limited", { status: 429 }); - - // 2. Memory, search, and cached tools — all scoped to this user. - const tools = { - ...createMemoryTools({ redis, userId }), - ...createSearchTools({ schema: s.object({ title: s.string(), author: s.string() }), redis, indexName: "books" }), - ...cachedTools( - { - convert_price: tool({ - description: "Convert a USD price to another currency.", - inputSchema: z.object({ usd: z.number(), currency: z.string() }), - execute: async ({ usd, currency }) => ({ currency, amount: usd * 0.92 }), - }), - }, - { userId, redis }, - ), - }; - - const result = streamText({ - model: openai("gpt-5.4-mini"), - messages: await convertToModelMessages(messages), - tools, - stopWhen: stepCountIs(5), - }); - - // 3. Persist the whole conversation when the stream finishes. - const history = createChatHistory({ redis }); - return createUIMessageStreamResponse({ - stream: toUIMessageStream({ - stream: result.stream, - originalMessages: messages, - onFinish: ({ messages }) => history.saveChat({ userId, sessionId: id, messages, title: "New chat" }), - }), - }); -} -``` - -A complete, runnable Next.js demo (`useChat` UI, chat sidebar with fuzzy search, inline tool calls) -lives in [`examples/ai-sdk-demo`](https://github.com/upstash/agentkit/tree/main/examples/ai-sdk-demo). - - - - Source, packages, and the full example apps. - - - The AI SDK this adapter plugs into. - - - # Vercel AI SDK Memory, RAG & Chat History with Redis Source: https://upstash.com/docs/redis/sdks/agentkit/ai-sdk @@ -74714,526 +74361,6 @@ Two complete `eve` apps live in the AgentKit repo: -# Memory, Chat History, RAG, Rate Limiting & Sandboxes for the Vercel Eve Agent Framework -Source: https://upstash.com/docs/redis/sdks/agentkit/eve - -[Upstash AgentKit](https://github.com/upstash/agentkit) builds AI agents on Upstash Redis: memory, -conversation history, caching, and RAG, with no separate vector database. The semantic features run on -[Upstash Redis Search](/docs/redis/search/introduction) and its `$smart` fuzzy operator. - -Two packages bring AgentKit to **Eve, the Vercel agent framework**. They work together in one agent: - -| Package | What it is | -| --- | --- | -| `@upstash/agentkit-eve-extension` | An [Eve extension](https://eve.dev/docs/extensions): one mount file adds memory, searchable chat history, and RAG. | -| `@upstash/agentkit-eve` | Per-file building blocks, plus the two things an extension cannot contribute: rate limiting and a sandbox backend. | - -Mount the extension for the bundled setup. Add the package when you need a rate-limit gate, an Upstash -Box sandbox, or control over an individual tool file. - -Start from an eve project (**0.43.0 or later** — the published extension is prebuilt, and its -compatibility manifest requires eve ≥ 0.43's contribution formats). Scaffold one, which installs `eve` -and an AI-SDK provider for you: - -```bash -npx eve@latest init my-agent -# or, to start with a Next.js app: -npx eve@latest init my-agent --channel-web-nextjs -``` - - - AgentKit reads `UPSTASH_REDIS_REST_URL` / `UPSTASH_REDIS_REST_TOKEN` from the environment by default. - Pass your own `@upstash/redis` client as `redis` to any helper to override. - - -## The Eve extension: memory, chat history, and RAG - -Everything in this part comes from `@upstash/agentkit-eve-extension`, configured in one mount file. - -### How to mount the Upstash extension in Vercel Eve - -```bash -npm install @upstash/agentkit-eve-extension -``` - -One file under `agent/extensions/` mounts everything. Every config field is optional, and the smallest -mount gives the model long-term memory: - -```ts -// agent/extensions/agentkit.ts -import agentkit from "@upstash/agentkit-eve-extension"; - -export default agentkit(); -``` - -The filename supplies the namespace, so contributions compose as `agentkit__recall_memory`, -`agentkit__save_memory`, and so on. The extension also merges a short instructions fragment into your -system prompt telling the model when to save and recall. - -The three feature groups below are independent of each other. Memory holds facts the model decided to -keep. Chat history is the transcript of what was said. Search is retrieval over documents you write -into your own index. Each has its own Redis keyspace and its own search index. - -### How to add memory to Vercel Eve - -Long-term memory the model reads and writes itself. A bare mount already has it; the `memory` field -only tunes recall: - -```ts -// agent/extensions/agentkit.ts -import agentkit from "@upstash/agentkit-eve-extension"; - -export default agentkit({ - memory: { topK: 5, minScore: 1 }, -}); -``` - - - * `agentkit__recall_memory` — searches the user's memories; called with no `query` it returns all of them. - * `agentkit__save_memory` — stores one durable fact about the user. - * `memory.topK` — max memories a recall returns. - * `memory.minScore` — relevance floor. Scores are unbounded BM25 values, not `[0,1]`. - - `userId` is the only tenant boundary. It defaults to Eve's verified session auth - (`auth.current?.principalId`, then `auth.initiator?.principalId`, then the session id), so - configure a real authenticator (`vercelOidc()`, an OIDC/JWT provider like Clerk, …) if you want the - principal to be trustworthy. You can also set it to a string, which puts every caller in one shared - scope, or derive it per call: - - ```ts - export default agentkit({ - userId: (ctx) => ctx.session.auth.current?.principalId ?? ctx.session.id, - }); - ``` - - Memories are stored at `agentkit:memory::`. - - -### How to add searchable chat history to Vercel Eve - -`chatHistory: true` persists every user and assistant message to Redis as the session streams, and -gives the model two tools over that store. A user can then ask about something settled in a previous -conversation: - -```ts -// agent/extensions/agentkit.ts -import agentkit from "@upstash/agentkit-eve-extension"; - -export default agentkit({ chatHistory: true }); -``` - -* `agentkit__search_chat_history` runs a `$smart` (typo-tolerant) search over what was said and returns - the matching chats as summaries: `sessionId`, `title`, `updatedAt`, `messageCount`, `score`. The - current conversation is excluded, since it's already in context. -* `agentkit__read_chat_history` reads one of those chats back by `sessionId`, newest messages last. - -Both tools take `userId` from the session, so the model cannot widen a lookup past the current user's -own transcripts. - -Your own code reads the same store with `ChatHistory` from `@upstash/agentkit-sdk`, which is how you'd -build a history sidebar or run evals over past sessions: - -```bash -npm install @upstash/agentkit-sdk @upstash/redis -``` - -```ts -// app/api/chats/route.ts — server-side only; `userId` comes from your session, never the client -import { Redis } from "@upstash/redis"; -import { ChatHistory } from "@upstash/agentkit-sdk"; - -// `ChatHistory` is generic over the message type, and the extension doesn't export the shape its -// hook writes — declare it to get a typed `messages` array back. -type StoredMessage = { role: "user" | "assistant"; content: string; createdAt: number }; - -// Pass the same prefix / indexName / ttlSeconds you set on `chatHistory` in the mount file. -const history = new ChatHistory({ redis: Redis.fromEnv() }); - -// List: summaries only (no messages), newest-updated first. -const chats = await history.listChats({ userId, limit: 50 }); - -// Search: `$smart` fuzzy match, summaries plus a BM25 `score`. target: "user" | "model" | "both". -const hits = await history.searchChats({ userId, query: "the retry schema", target: "both", limit: 20 }); - -// Read: one full transcript, or null if this user has no chat with that sessionId. -const chat = await history.getChat({ userId, sessionId: hits[0].sessionId }); -chat?.messages.forEach((m) => console.log(m.role, m.content)); -``` - -`userId` must be the same value the extension resolved for those sessions, and both ids are Redis key -parts, so `:` in a derived value is replaced with `_`. `deleteChat({ userId, sessionId })` removes a -chat and its index entry. - - - Pass an object in place of `true` to tune storage: - - * `chatHistory.prefix` — base key prefix (default `agentkit:chat`). - * `chatHistory.indexName` — [Redis Search](/docs/redis/search/introduction) index name (defaults to the - identifier-safe `prefix`). - * `chatHistory.ttlSeconds` — per-chat TTL. Omit for no expiry. - - Each session is one JSON document at `agentkit:chat::`, holding the raw transcript - plus `$smart`-indexed user and model text. A search returns summaries, and a read is capped at 50 - messages per call with a `truncated` flag, so neither can flood the context window. - - Redis is the durable record here, since Eve's own workflow store is pruned after a run completes. - - These tools look history up on demand. They don't resume a session: Eve does that through its own - session cursor. - - -### How to add RAG to Vercel Eve - -Point the extension at an [Upstash Redis Search](/docs/redis/search/introduction) index and the model gets -`search`, `search_aggregate`, and `search_count` tools over it. You build the schema with `s` from -`@upstash/redis`, so your mount file imports it. Add the package to your app: - -```bash -npm install @upstash/redis -``` - -```ts -// agent/extensions/agentkit.ts -import { s } from "@upstash/redis"; -import agentkit from "@upstash/agentkit-eve-extension"; - -export default agentkit({ - search: { - schema: s.object({ title: s.string(), author: s.string().noTokenize(), year: s.number() }), - indexName: "books", - }, -}); -``` - - - * **`search.schema`** _(required)_ — built with `s` from `@upstash/redis`. - * `search.indexName` — defaults to `"agentkit:search"`; ties all three tools to one index. - * `search.prefix` — key prefix for indexed JSON docs (defaults to `":"`). - * `search.defaultLimit` — default page size for `search` (10). - - Tool descriptions are generated from your schema (field names, types, and the filter operators that - apply to each), so the model learns the index without any prompt text from you. You write the - documents yourself with `redis.json.set` under the prefix, and the index is created on first read. - - Omit `search` and these three tools don't exist at all. Like the chat-history tools, they resolve at - session start, which is why they don't appear in a static tool listing. - - -### Extension configuration reference - -```ts -// agent/extensions/agentkit.ts -import { s } from "@upstash/redis"; -import agentkit from "@upstash/agentkit-eve-extension"; - -export default agentkit({ - // optional: string, or (ctx) => string. Defaults to the verified principal, then the session id. - userId: (ctx) => ctx.session.auth.current?.principalId ?? ctx.session.id, - // optional: an explicit client; defaults to Redis.fromEnv() - // redis: new Redis({ url, token }), - // optional: tune memory recall - memory: { topK: 5, minScore: 1 }, - // optional: omit and the search tools don't exist - search: { - schema: s.object({ title: s.string(), author: s.string().noTokenize(), year: s.number() }), - indexName: "books", - prefix: "books:", // optional - defaultLimit: 10, // optional - }, - // optional: off by default. `true`, or an object to tune storage - chatHistory: { ttlSeconds: 60 * 60 * 24 * 30 }, -}); -``` - - - Mount as a directory and override a slot by filename. This is how you drop a tool you don't want, - for example to capture chat history without letting the model read it back: - - ``` - agent/extensions/agentkit/ - extension.ts # the mount: export default agentkit({ ... }) - tools/read_chat_history.ts # your override for agentkit__read_chat_history - ``` - - ```ts - // agent/extensions/agentkit/tools/read_chat_history.ts - import { disableTool } from "eve/tools"; - - export default disableTool(); - ``` - - You can also re-define the memory tools, say to gate saves behind approval, by importing them from - `@upstash/agentkit-eve-extension/tools` and spreading them into your own `defineTool`. - - - - `@upstash/agentkit-eve` below offers memory and RAG as standalone tool files too. Use those when you - want to configure one tool at a time, and use the package alongside the extension for rate limiting - and sandboxes. - - -## The Eve package: rate limiting, sandboxes, and tool files - -Everything in this part comes from `@upstash/agentkit-eve`, written as individual `agent/` files. -Rate limiting and the sandbox backend live here because an extension cannot contribute a channel or -a sandbox. - -### How to add rate limiting to Vercel Eve - -```bash -npm install @upstash/agentkit-eve @upstash/redis -``` - -`createRateLimitAuth` is a ready `AuthFn` that throttles inbound requests. Drop it into your channel's -[auth walk](https://eve.dev/docs/guides/auth-and-route-protection) ahead of your real authenticators. - -```ts -// agent/channels/eve.ts -import { createRateLimitAuth, Ratelimit } from "@upstash/agentkit-eve"; -import { localDev, vercelOidc } from "eve/channels/auth"; -import { eveChannel } from "eve/channels/eve"; - -export default eveChannel({ - auth: [ - createRateLimitAuth({ - limiter: Ratelimit.slidingWindow(20, "1 m"), - identifier: (req) => req.headers.get("x-forwarded-for") ?? "anonymous", - }), - localDev(), - vercelOidc(), - ], -}); -``` - - - - * **`limiter`** _(required)_ — e.g. `Ratelimit.slidingWindow(20, "1 m")` or `fixedWindow(...)`. - * **`identifier`** _(required)_ — a string, or `(request) => string`. There's no implicit `"global"`: - one shared bucket lets a single abusive caller exhaust the window for everyone, so derive it per - request (an auth user id, an API key, or `x-forwarded-for` for per-IP). - * `prefix` — base key prefix; keys are `:` (default `agentkit:rateLimit`). - * `message` — 403 body when over the limit. - * `redis` — defaults to `Redis.fromEnv()`. - - It's a gate: under the limit it returns `null` to fall through to the next `AuthFn`; over it throws - a 403. - - - Eve runs each turn as two authenticated requests: the message `POST` (which invokes the model) and a - follow-up `GET …/stream` that opens the reply stream. The auth walk runs on both, so counting both - would charge every turn twice. `createRateLimitAuth` counts only the `POST`s, so one turn costs one - token: a `Ratelimit.slidingWindow(20, "1 m")` allows 20 turns per minute, not 10. The session-read - `GET`s pass through unthrottled. - - - -### How to add a sandbox to Vercel Eve - -A drop-in replacement for Eve's `vercel()` backend, powered by -[Upstash Box](https://github.com/upstash/box). Swap the import and keep the rest of your -[sandbox file](https://eve.dev/docs/sandbox) the same. - -```bash -npm install @upstash/box -``` - -```ts -// agent/sandbox.ts -import { defineSandbox } from "eve/sandbox"; -import { upstash } from "@upstash/agentkit-eve/sandbox"; // was: eve/sandbox/vercel - -export default defineSandbox({ - backend: upstash({ runtime: "node", size: "medium" }), - revalidationKey: () => "repo-bootstrap-v1", - async bootstrap({ use }) { - const sandbox = await use({ networkPolicy: "allow-all" }); // open egress to install packages - await sandbox.run({ command: "apt-get install -y jq" }); - }, - async onSession({ use }) { - await use(); // inherits the secure deny-all default - }, -}); -``` - - - - `upstash(config)` takes the `@upstash/box` `BoxConfig` verbatim, meaning whatever you'd pass to - `Box.create({...})`: `runtime`, `size`, `apiKey` (defaults to `UPSTASH_BOX_API_KEY`), `keepAlive`, - `initCommand`, `env`, `skills`, `mcpServers`, `timeout`, and so on. It also takes an optional - `redis` (defaults to `Redis.fromEnv()`). `networkPolicy` is **not** a config knob (see below). - `@upstash/box` is an optional peer dependency, needed only when you import - `@upstash/agentkit-eve/sandbox`. - - - The sandbox runs untrusted, model-generated code, so open egress would mean SSRF / data - exfiltration / reaching your own infrastructure from inside the box. Open it per-session, in - `bootstrap`'s `use(...)` or the session `use(...)`, and never as a config knob. Note that `env` passed - to `upstash({ env })` is readable by code running in the box; don't pass secrets you wouldn't want - it to see. - - - Box network policies are plain domain/CIDR allow-lists. Eve's per-domain firewall rules (`transform` - header injection, `forwardURL`) have no Box equivalent, so passing them in `use({ networkPolicy })` - **throws** rather than silently sending the request unauthenticated: - - ```ts - // ❌ throws — Box can't inject headers via a per-session policy - export default defineSandbox({ - backend: upstash({ runtime: "node" }), - async onSession({ use }) { - await use({ - networkPolicy: { - allow: { "api.example.com": [{ transform: [{ headers: { authorization: "Bearer …" } }] }] }, - }, - }); - }, - }); - ``` - - Broker credentials with Box's `attachHeaders` instead (set at backend creation; a proxy on the box - injects them), and open the domain with a plain allow-list: - - ```ts - // ✅ headers injected at the firewall; the secret never enters the box - export default defineSandbox({ - backend: upstash({ - runtime: "node", - attachHeaders: { "api.example.com": { Authorization: "Bearer …" } }, - }), - async onSession({ use }) { - await use({ networkPolicy: { allow: ["api.example.com"] } }); - }, - }); - ``` - - - **Reuse.** Eve re-opens a session several times per turn, and the backend reattaches to the same Box - instead of creating a new one each time. Boxes default to Box's pause-based idle lifecycle - (`keepAlive: false`): auto-paused when idle, resumed on reattach, reaped by Box. Pass - `keepAlive: true` only for an always-running box you manage yourself. - - **Template registry.** Eve builds your template (seed files + `bootstrap`) at build/startup, but - session creation runs per request in a different process, so the snapshot id is stored in a durable - Redis registry (`redis`, defaulting to `Redis.fromEnv()`). Eve roots its tools at `/workspace` while - a Box session lives at `/workspace/home`; the backend bridges the two automatically. - - - -### How to cache tools in Vercel Eve - -Like Eve's `defineTool`, but the `execute` result is memoized in Redis. - -```ts -// agent/tools/get_weather.ts -import { z } from "zod"; -import { defineCachedTool } from "@upstash/agentkit-eve"; - -export default defineCachedTool({ - description: "Get the current weather for a city.", - inputSchema: z.object({ city: z.string() }), - execute: async ({ city }) => fetchWeather(city), - toolName: "get_weather", - userId: (_, ctx) => ctx.session.auth.current?.principalId ?? ctx.session.id, -}); -``` - - - * `description` / `inputSchema` / `execute` — the usual `defineTool` fields; `execute`'s result is memoized. - * **`toolName`** _(required)_ — the tool segment of the cache key. - * **`userId`** _(required)_ — a string, or `(input, ctx) => string`; scopes the cache per user. - * `ttlSeconds` — per-result TTL (default: no expiry). - * `redis` — defaults to `Redis.fromEnv()`. - - Keys are `agentkit:toolCache:::`. - - -### Memory and RAG as individual tool files - -The same two features the extension mounts, written as standalone `agent/tools/` files. Use these when -you want to configure each tool on its own. - -Memory takes one file per tool: - -```ts -// agent/tools/recall_memory.ts -import { defineMemoryRecallTool } from "@upstash/agentkit-eve"; - -export default defineMemoryRecallTool({ - userId: (_, ctx) => ctx.session.auth.current?.principalId ?? ctx.session.id, -}); -``` - -```ts -// agent/tools/save_memory.ts -import { defineMemorySaveTool } from "@upstash/agentkit-eve"; - -export default defineMemorySaveTool({ - userId: (_, ctx) => ctx.session.auth.current?.principalId ?? ctx.session.id, -}); -``` - -RAG is `defineSearchTools`, the counterpart to the -[AI SDK adapter's](/docs/redis/sdks/agentkit/ai-sdk#how-to-add-rag-with-the-ai-sdk) `createSearchTools`: - -```ts -// agent/tools/search_books.ts -import { s } from "@upstash/redis"; -import { defineSearchTools } from "@upstash/agentkit-eve"; - -export default defineSearchTools({ - schema: s.object({ title: s.string(), author: s.string().noTokenize(), year: s.number() }), - indexName: "books", -}).search; // aggregate_books.ts → .aggregate, count_books.ts → .count -``` - - - `defineMemoryRecallTool` / `defineMemorySaveTool` take a required `userId` (string or - `(input, ctx) => string`), plus `topK`, `minScore`, and `redis`. `defineSearchTools` takes a required - `schema`, plus `indexName`, `prefix`, `defaultLimit`, and `redis`. - - Each tool file must be self-contained, so call `defineSearchTools` in each one and export the member - you want, repeating the same `schema` and `indexName` across `search_books.ts`, `aggregate_books.ts`, - and `count_books.ts`. The index is created on first use, and every returned tool is already - `defineTool`-branded. - - This package has no chat history. It comes from the extension, or from - [`ChatHistory` in `@upstash/agentkit-sdk`](#how-to-add-searchable-chat-history-to-vercel-eve) if - you're writing the code yourself. - - -## Working with Eve's `agent/` files - -Eve's runtime snapshots each tool/channel/hook file and resolves only **package** imports from it. It -does **not** include shared `agent/`-source modules such as an `agent/lib/redis.ts`. So inside `agent/`: - -* Import only from packages, never from other `agent/` files. -* Lean on the defaults. `redis` falls back to `Redis.fromEnv()` in every helper, so you almost never pass it. -* Repeat config (schema, names) in each file instead of sharing a module. - -Shared app code, like a seeder a page calls, belongs in your project `lib/` and is imported by the app, -not by `agent/` files. Extensions are exempt from all of this. The extension package ships as one -compiled unit, which is why its whole configuration fits in a single mount file. - -## How to run the Vercel Eve example apps - -Two complete `eve` apps live in the AgentKit repo: - -* [`examples/eve-extension-demo`](https://github.com/upstash/agentkit/tree/main/examples/eve-extension-demo) - is a minimal agent whose whole configuration is one extension mount, with memory, chat history, and - book search turned on. -* [`examples/eve-demo`](https://github.com/upstash/agentkit/tree/main/examples/eve-demo) uses the - file-by-file package (memory, search, cached tools, a rate-limit gate, and an Upstash Box sandbox), - with a chat UI that renders tool calls inline. - - - - Source, packages, and the full example apps. - - - The Vercel agent framework this adapter targets. - - - # Upstash Redis MCP Source: https://upstash.com/docs/redis/sdks/mcp @@ -77537,12 +76664,6 @@ If you prefer to disable the Auto IP Deny List feature while still using the deny lists, you can do so via the [Ratelimit dashboard on the Upstash Console](https://console.upstash.com/ratelimit). -# Realtime -Source: https://upstash.com/docs/redis/sdks/realtime - -# Redis Analytics -Source: https://upstash.com/docs/redis/sdks/redis-analytics - # Advanced Source: https://upstash.com/docs/redis/sdks/ts/advanced @@ -84589,7 +83710,6 @@ from the list of the associated group and consumer. - [Autocomplete API with Serverless Redis](https://upstash.com/docs/redis/tutorials/auto_complete_with_serverless_redis.md) - [Build Stateful Applications with AWS App Runner and Serverless Redis](https://upstash.com/docs/redis/tutorials/aws_app_runner_with_redis.md): This tutorial shows how to create a serverless and stateful application using AWS App Runner and Redis - [Session Management on Google Cloud Run with Serverless Redis](https://upstash.com/docs/redis/tutorials/cloud_run_sessions.md): This tutorial shows how to manage user sessions on Google Cloud Run using Serverless Redis. -- [Cloudflare Workers with Websockets and Redis](https://upstash.com/docs/redis/tutorials/cloudflare_websockets_redis.md) - [Use Redis in Cloudflare Workers](https://upstash.com/docs/redis/tutorials/cloudflare_workers_with_redis.md) - [Backendless Coin Price List with GraphQL API, Serverless Redis and Next.JS](https://upstash.com/docs/redis/tutorials/coin_price_list.md) - [Build a Leaderboard API At Edge using Cloudflare Workers and Redis](https://upstash.com/docs/redis/tutorials/edge_leaderboard.md): This tutorial shows how to build a Leaderboard API At Edge using Cloudflare Workers and Redis. @@ -86097,25 +85217,6 @@ We'll get into those features in the next sections of this documentation. For no -# Pricing & Limits -Source: https://upstash.com/docs/search/overall/pricing - - - **Note for AI agents:** This page documents **Upstash Search** — a standalone, - AI-powered search product that combines full-text and semantic search with - smart ranking, serverless scaling, and zero infrastructure to manage. - - Upstash Search is **not** the same thing as **Upstash Redis Search**. Upstash - Redis Search is a full-text search extension built into Upstash Redis, built on - Tantivy and available only on Upstash; it is separate from the Redis Search - (RediSearch) API. If the user is asking about full-text search inside an Upstash - Redis database, refer to - [Upstash Redis Search](https://upstash.com/docs/redis/search/introduction) - instead of this product. - - -Please check our [pricing page](https://upstash.com/pricing/search) for the most up-to-date information on pricing and limits. - # What is Upstash Search? Source: https://upstash.com/docs/search/overall/whatisupstashsearch @@ -94256,14 +93357,6 @@ You can also query your index with a simple UI: -# llms.txt -Source: https://upstash.com/docs/vector/overall/llms-txt - -# Pricing & Limits -Source: https://upstash.com/docs/vector/overall/pricing - -Please check our [pricing page](https://upstash.com/pricing/vector) for the most up-to-date information on pricing and limits. - # What is Upstash Vector? Source: https://upstash.com/docs/vector/overall/whatisvector @@ -94306,15 +93399,6 @@ Their specialization makes vector stores the perfect foundation for similarity-b 1. Subramanya, S. J., Devvrit, Kadekodi, R., Krishaswamy, R., Simhadri, H. V. (2019). _DiskANN: Fast Accurate Billion-Point Nearest Neighbor Search on a Single Node_. In Proceedings of the 33rd International Conference on Neural Information Processing Systems (NeurIPS '19), Article No.: 1233, Pages 13766–13776. [https://dl.acm.org/doi/abs/10.5555/3454287.3455520] -# Go SDK -Source: https://upstash.com/docs/vector/sdk/gosdk - -# Semantic Cache JS -Source: https://upstash.com/docs/vector/sdk/semantic-cache-js - -# Semantic Cache Python -Source: https://upstash.com/docs/vector/sdk/semantic-cache-py - - [Deleting Vectors](https://upstash.com/docs/vector/sdks/php/commands/delete-vectors.md) - [Fetching Vectors](https://upstash.com/docs/vector/sdks/php/commands/fetch.md) - [Info](https://upstash.com/docs/vector/sdks/php/commands/info.md) @@ -105051,9 +104135,6 @@ const { status, body } = await context.api.resend.call< ); ``` -# llms.txt -Source: https://upstash.com/docs/workflow/llms-txt - # Pricing Source: https://upstash.com/docs/workflow/pricing @@ -105089,19 +104170,6 @@ For detailed pricing information based on different plans, visit our [Workflow p - [TanStack Start](https://upstash.com/docs/workflow/quickstarts/tanstack-start.md) - [Next.js](https://upstash.com/docs/workflow/quickstarts/vercel-nextjs.md) -# Roadmap -Source: https://upstash.com/docs/workflow/roadmap - - - We have moved the roadmap and the changelog to [Github Discussions](https://github.com/orgs/upstash/discussions) starting from October 2025.Now you can follow `In Progress` features. You can see that your `Feature Requests` are recorded. You can vote for them and comment your specific use-cases to shape the feature to your needs. - - -# JavaScript SDK -Source: https://upstash.com/docs/workflow/sdk/workflow-js - -# Python SDK -Source: https://upstash.com/docs/workflow/sdk/workflow-py - # Overview Source: https://upstash.com/docs/workflow/steps diff --git a/llms.txt b/llms.txt index c6e0b2d9c..fe664511b 100644 --- a/llms.txt +++ b/llms.txt @@ -10,7 +10,6 @@ - [README](https://upstash.com/docs/README.md) - [CLI](https://upstash.com/docs/agent-resources/cli.md) -- [llms.txt](https://upstash.com/docs/agent-resources/llms-txt.md) - [MCP Server](https://upstash.com/docs/agent-resources/mcp.md) - [Skills](https://upstash.com/docs/agent-resources/skills.md) - [Disable Production Pack](https://upstash.com/docs/api-reference/qstash/disable-production-pack.md): Disables the production pack for a QStash instance. @@ -56,12 +55,6 @@ - [Recordings](https://upstash.com/docs/box/overall/browser/recordings.md) - [Tabs & Navigation](https://upstash.com/docs/box/overall/browser/tabs.md) - [How to Add a Custom Agent](https://upstash.com/docs/box/overall/custom-agent.md) -- [Aider](https://upstash.com/docs/box/overall/custom-harness/aider.md) -- [CrewAI](https://upstash.com/docs/box/overall/custom-harness/crewai.md) -- [Gemini](https://upstash.com/docs/box/overall/custom-harness/gemini.md) -- [Goose](https://upstash.com/docs/box/overall/custom-harness/goose.md) -- [Pi](https://upstash.com/docs/box/overall/custom-harness/pi.md) -- [Pydantic AI](https://upstash.com/docs/box/overall/custom-harness/pydantic-ai.md) - [Ephemeral Box](https://upstash.com/docs/box/overall/ephemeral-box.md) - [Filesystem](https://upstash.com/docs/box/overall/files.md) - [Git](https://upstash.com/docs/box/overall/git.md) @@ -70,7 +63,6 @@ - [Live Sessions](https://upstash.com/docs/box/overall/live-sessions.md) - [Network Policy](https://upstash.com/docs/box/overall/network-policy.md) - [Public URLs](https://upstash.com/docs/box/overall/preview.md) -- [Pricing & Limits](https://upstash.com/docs/box/overall/pricing.md) - [Quickstart](https://upstash.com/docs/box/overall/quickstart.md) - [Schedules](https://upstash.com/docs/box/overall/schedules.md) - [Security & Secrets](https://upstash.com/docs/box/overall/security.md) @@ -143,7 +135,6 @@ - [Set Index Plan](https://upstash.com/docs/devops/developer-api/vector/set_index_plan.md): This endpoint is used to change the plan of an index. - [Transfer Index](https://upstash.com/docs/devops/developer-api/vector/transfer_index.md): This endpoint is used to transfer an index to another team. Transferring to a personal account is not supported. However, transferring an index from a personal account to a team is allowed. - [Overview](https://upstash.com/docs/devops/pulumi/overview.md) -- [Terraform](https://upstash.com/docs/devops/terraform.md) - [upstash_qstash_endpoint_data](https://upstash.com/docs/devops/terraform/data_sources/upstash_qstash_endpoint_data.md) - [upstash_qstash_schedule_data](https://upstash.com/docs/devops/terraform/data_sources/upstash_qstash_schedule_data.md) - [upstash_qstash_topic_data](https://upstash.com/docs/devops/terraform/data_sources/upstash_qstash_topic_data.md) @@ -240,9 +231,6 @@ - [Compare](https://upstash.com/docs/qstash/overall/compare.md) - [Prod Pack & Enterprise](https://upstash.com/docs/qstash/overall/enterprise.md) - [Getting Started](https://upstash.com/docs/qstash/overall/getstarted.md) -- [llms.txt](https://upstash.com/docs/qstash/overall/llms-txt.md) -- [Pricing & Limits](https://upstash.com/docs/qstash/overall/pricing.md) -- [Roadmap](https://upstash.com/docs/qstash/overall/roadmap.md) - [Use Cases](https://upstash.com/docs/qstash/overall/usecases.md) - [AWS Lambda (Node)](https://upstash.com/docs/qstash/quickstarts/aws-lambda/nodejs.md) - [AWS Lambda (Python)](https://upstash.com/docs/qstash/quickstarts/aws-lambda/python.md) @@ -644,12 +632,6 @@ - [Redis® API Compatibility: supported commands, modules, and protocols](https://upstash.com/docs/redis/overall/compatibility.md) - [Prod Pack & Enterprise](https://upstash.com/docs/redis/overall/enterprise.md) - [Getting Started](https://upstash.com/docs/redis/overall/getstarted.md): Create an Upstash Redis database in seconds -- [llms.txt](https://upstash.com/docs/redis/overall/llms-txt.md) -- [Pricing & Limits](https://upstash.com/docs/redis/overall/pricing.md) -- [Pricing & Limits](https://upstash.com/docs/redis/overall/pricingold.md) -- [Python SDK](https://upstash.com/docs/redis/overall/pythonredis.md) -- [Rate Limit SDK](https://upstash.com/docs/redis/overall/ratelimit.md) -- [Typescript SDK](https://upstash.com/docs/redis/overall/redis.md) - [Use Cases](https://upstash.com/docs/redis/overall/usecases.md) - [AWS Lambda](https://upstash.com/docs/redis/quickstarts/aws-lambda.md) - [Azure Functions](https://upstash.com/docs/redis/quickstarts/azure-functions.md) @@ -674,10 +656,7 @@ - [App Router](https://upstash.com/docs/redis/quickstarts/vercel-functions-app-router.md) - [Pages Router](https://upstash.com/docs/redis/quickstarts/vercel-functions-pages-router.md) - [Vercel Python Runtime](https://upstash.com/docs/redis/quickstarts/vercel-python-runtime.md) -- [Agent Analytics](https://upstash.com/docs/redis/sdks/agent-analytics.md) - [Vercel AI SDK Memory, RAG & Chat History with Redis](https://upstash.com/docs/redis/sdks/agentkit/ai-sdk.md): Add long-term memory, RAG, and chat history to the Vercel AI SDK with Upstash Redis — drop-in tools for generateText and streamText, no separate vector database. -- [Vercel AI SDK Memory, RAG & Chat History with Redis](https://upstash.com/docs/redis/sdks/agentkit/ai-sdk.md): Add long-term memory, RAG, and chat history to the Vercel AI SDK with Upstash Redis — drop-in tools for generateText and streamText, no separate vector database. -- [Memory, Chat History, RAG, Rate Limiting & Sandboxes for the Vercel Eve Agent Framework](https://upstash.com/docs/redis/sdks/agentkit/eve.md): Add long-term memory, searchable chat history, RAG, rate limiting, tool caching, and sandboxes to Vercel's Eve agent framework with Upstash Redis — no separate vector database. - [Memory, Chat History, RAG, Rate Limiting & Sandboxes for the Vercel Eve Agent Framework](https://upstash.com/docs/redis/sdks/agentkit/eve.md): Add long-term memory, searchable chat history, RAG, rate limiting, tool caching, and sandboxes to Vercel's Eve agent framework with Upstash Redis — no separate vector database. - [Upstash Redis MCP](https://upstash.com/docs/redis/sdks/mcp.md) - [ECHO](https://upstash.com/docs/redis/sdks/py/commands/auth/echo.md) @@ -877,8 +856,6 @@ - [Methods](https://upstash.com/docs/redis/sdks/ratelimit-ts/methods.md) - [Overview](https://upstash.com/docs/redis/sdks/ratelimit-ts/overview.md) - [Traffic Protection](https://upstash.com/docs/redis/sdks/ratelimit-ts/traffic-protection.md) -- [Realtime](https://upstash.com/docs/redis/sdks/realtime.md) -- [Redis Analytics](https://upstash.com/docs/redis/sdks/redis-analytics.md) - [Advanced](https://upstash.com/docs/redis/sdks/ts/advanced.md) - [ECHO](https://upstash.com/docs/redis/sdks/ts/commands/auth/echo.md) - [PING](https://upstash.com/docs/redis/sdks/ts/commands/auth/ping.md): Send a ping to the server and get a response if the server is alive. @@ -1133,7 +1110,6 @@ - [Autocomplete API with Serverless Redis](https://upstash.com/docs/redis/tutorials/auto_complete_with_serverless_redis.md) - [Build Stateful Applications with AWS App Runner and Serverless Redis](https://upstash.com/docs/redis/tutorials/aws_app_runner_with_redis.md): This tutorial shows how to create a serverless and stateful application using AWS App Runner and Redis - [Session Management on Google Cloud Run with Serverless Redis](https://upstash.com/docs/redis/tutorials/cloud_run_sessions.md): This tutorial shows how to manage user sessions on Google Cloud Run using Serverless Redis. -- [Cloudflare Workers with Websockets and Redis](https://upstash.com/docs/redis/tutorials/cloudflare_websockets_redis.md) - [Use Redis in Cloudflare Workers](https://upstash.com/docs/redis/tutorials/cloudflare_workers_with_redis.md) - [Backendless Coin Price List with GraphQL API, Serverless Redis and Next.JS](https://upstash.com/docs/redis/tutorials/coin_price_list.md) - [Build a Leaderboard API At Edge using Cloudflare Workers and Redis](https://upstash.com/docs/redis/tutorials/edge_leaderboard.md): This tutorial shows how to build a Leaderboard API At Edge using Cloudflare Workers and Redis. @@ -1170,7 +1146,6 @@ - [FAQ](https://upstash.com/docs/search/help/faq.md): Questions And Answers About Upstash Search - [Docusaurus Integration](https://upstash.com/docs/search/integrations/docusaurus.md): AI-powered search component for Docusaurus using Upstash Search. - [Getting Started](https://upstash.com/docs/search/overall/getstarted.md): Creating an Upstash Search Database -- [Pricing & Limits](https://upstash.com/docs/search/overall/pricing.md) - [What is Upstash Search?](https://upstash.com/docs/search/overall/whatisupstashsearch.md): Lightweight, AI-powered search for developers - [Delete](https://upstash.com/docs/search/sdks/py/commands/delete.md) - [Fetch](https://upstash.com/docs/search/sdks/py/commands/fetch.md) @@ -1233,12 +1208,7 @@ - [Changelog](https://upstash.com/docs/vector/overall/changelog.md) - [Compare](https://upstash.com/docs/vector/overall/compare.md) - [Getting Started](https://upstash.com/docs/vector/overall/getstarted.md) -- [llms.txt](https://upstash.com/docs/vector/overall/llms-txt.md) -- [Pricing & Limits](https://upstash.com/docs/vector/overall/pricing.md) - [What is Upstash Vector?](https://upstash.com/docs/vector/overall/whatisvector.md) -- [Go SDK](https://upstash.com/docs/vector/sdk/gosdk.md) -- [Semantic Cache JS](https://upstash.com/docs/vector/sdk/semantic-cache-js.md) -- [Semantic Cache Python](https://upstash.com/docs/vector/sdk/semantic-cache-py.md) - [Deleting Vectors](https://upstash.com/docs/vector/sdks/php/commands/delete-vectors.md) - [Fetching Vectors](https://upstash.com/docs/vector/sdks/php/commands/fetch.md) - [Info](https://upstash.com/docs/vector/sdks/php/commands/info.md) @@ -1387,7 +1357,6 @@ - [OpenAI](https://upstash.com/docs/workflow/integrations/openai.md) - [Prometheus - Upstash QStash Integration](https://upstash.com/docs/workflow/integrations/prometheus.md) - [Resend](https://upstash.com/docs/workflow/integrations/resend.md) -- [llms.txt](https://upstash.com/docs/workflow/llms-txt.md) - [Pricing](https://upstash.com/docs/workflow/pricing.md) - [Astro](https://upstash.com/docs/workflow/quickstarts/astro.md) - [Cloudflare Workers](https://upstash.com/docs/workflow/quickstarts/cloudflare-workers.md) @@ -1403,9 +1372,6 @@ - [SvelteKit](https://upstash.com/docs/workflow/quickstarts/svelte.md) - [TanStack Start](https://upstash.com/docs/workflow/quickstarts/tanstack-start.md) - [Next.js](https://upstash.com/docs/workflow/quickstarts/vercel-nextjs.md) -- [Roadmap](https://upstash.com/docs/workflow/roadmap.md) -- [JavaScript SDK](https://upstash.com/docs/workflow/sdk/workflow-js.md) -- [Python SDK](https://upstash.com/docs/workflow/sdk/workflow-py.md) - [Overview](https://upstash.com/docs/workflow/steps.md) - [context.api](https://upstash.com/docs/workflow/steps/api.md) - [context.call](https://upstash.com/docs/workflow/steps/call.md) diff --git a/llms/README.md b/llms/README.md index 55f20fc98..83fed2454 100644 --- a/llms/README.md +++ b/llms/README.md @@ -46,7 +46,7 @@ one item per node: type WalkItem = | { type: "group"; group: string; depth: number } | { type: "openapi"; source: string; directory?: string } - | { type: "page"; path: string; metadata: { title; description? }; + | { type: "page"; path: string; metadata: { title; description?; url? }; content: string }; ``` @@ -79,11 +79,14 @@ Collects entries from three sources: 1. `README.mdx` at the docs root (Mintlify includes it even though it isn't in `docs.json`). -2. `walkNavigation` — every MDX page and every OpenAPI operation. +2. `walkNavigation` — every content MDX page and every OpenAPI operation. 3. An orphan-MDX scan — any `.mdx` file in the tree that isn't referenced from `docs.json`, since Mintlify still publishes them. -Entries are sorted alphabetically by site-relative path (without `.md`). +MDX files with `url` frontmatter are navigation links or redirects, not +content pages, so both collection paths skip them. Repeated navigation entries +keep their first occurrence. The remaining unique entries are sorted +alphabetically by site-relative path (without `.md`). Two files are then written: - **`llms.txt`** — flat `## Docs` list, then a `## OpenAPI Specs` footer diff --git a/llms/src/build.ts b/llms/src/build.ts index 833f7337b..49cef22d0 100644 --- a/llms/src/build.ts +++ b/llms/src/build.ts @@ -77,6 +77,7 @@ interface Entry { } const entries: Entry[] = []; +const entryPaths = new Set(); const openApiSpecs: { url: string }[] = []; addMdxEntry("README"); @@ -104,7 +105,7 @@ function handle(item: WalkItem): void { if (item.type === "openapi") { for (const op of expandOpenApi(DOCS_ROOT, item.source, item.directory)) { - entries.push({ + addEntry({ title: op.title, path: op.path, shortDescription: firstParagraph(op.description), @@ -119,7 +120,8 @@ function handle(item: WalkItem): void { } // type === "page" - entries.push({ + if (item.metadata.url) return; + addEntry({ title: item.metadata.title, path: item.path, shortDescription: firstParagraph(item.metadata.description), @@ -128,12 +130,21 @@ function handle(item: WalkItem): void { }); } +function addEntry(entry: Entry): void { + if (entryPaths.has(entry.path)) return; + entryPaths.add(entry.path); + entries.push(entry); +} + function addMdxEntry(path: string): void { const filePath = join(DOCS_ROOT, `${path}.mdx`); if (!existsSync(filePath)) return; const raw = readFileSync(filePath, "utf-8"); const { metadata, body } = parseFrontmatter(raw); - entries.push({ + // Mintlify uses `url` files as redirects or external navigation links. + // They are not documentation pages and do not belong in either llms file. + if (metadata.url) return; + addEntry({ title: metadata.title || titleFromBasename(path), path, shortDescription: firstParagraph(metadata.description), @@ -143,7 +154,6 @@ function addMdxEntry(path: string): void { } function addOrphanMdxFiles(): void { - const seen = new Set(entries.map((e) => e.path)); walkMdx(DOCS_ROOT); function walkMdx(dir: string): void { @@ -157,8 +167,7 @@ function addOrphanMdxFiles(): void { } if (!entry.name.endsWith(".mdx")) continue; const rel = relative(DOCS_ROOT, abs).replace(/\.mdx$/, ""); - if (seen.has(rel)) continue; - seen.add(rel); + if (entryPaths.has(rel)) continue; addMdxEntry(rel); } } diff --git a/llms/src/generator.ts b/llms/src/generator.ts index 8f7df13d1..111bf9ec6 100644 --- a/llms/src/generator.ts +++ b/llms/src/generator.ts @@ -36,6 +36,8 @@ interface DocsJson { export interface PageMetadata { title: string; description?: string; + /** Mintlify treats this file as a navigation link, not a content page. */ + url?: string; } export type WalkItem = @@ -149,6 +151,7 @@ function readPage(path: string, docsRoot: string): WalkItem | null { metadata: { title: metadata.title || deriveTitleFromPath(path), description: metadata.description, + url: metadata.url, }, content: body, }; From c6422b85c747ecf4e280f5b6e99cf576930e0fe1 Mon Sep 17 00:00:00 2001 From: enesgules Date: Thu, 27 Aug 2026 01:17:57 +0300 Subject: [PATCH 2/2] Name llms deduplication explicitly --- llms/src/build.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/llms/src/build.ts b/llms/src/build.ts index 49cef22d0..1abff2b6b 100644 --- a/llms/src/build.ts +++ b/llms/src/build.ts @@ -105,7 +105,7 @@ function handle(item: WalkItem): void { if (item.type === "openapi") { for (const op of expandOpenApi(DOCS_ROOT, item.source, item.directory)) { - addEntry({ + addUniqueEntry({ title: op.title, path: op.path, shortDescription: firstParagraph(op.description), @@ -121,7 +121,7 @@ function handle(item: WalkItem): void { // type === "page" if (item.metadata.url) return; - addEntry({ + addUniqueEntry({ title: item.metadata.title, path: item.path, shortDescription: firstParagraph(item.metadata.description), @@ -130,7 +130,7 @@ function handle(item: WalkItem): void { }); } -function addEntry(entry: Entry): void { +function addUniqueEntry(entry: Entry): void { if (entryPaths.has(entry.path)) return; entryPaths.add(entry.path); entries.push(entry); @@ -144,7 +144,7 @@ function addMdxEntry(path: string): void { // Mintlify uses `url` files as redirects or external navigation links. // They are not documentation pages and do not belong in either llms file. if (metadata.url) return; - addEntry({ + addUniqueEntry({ title: metadata.title || titleFromBasename(path), path, shortDescription: firstParagraph(metadata.description),