diff --git a/app/(chat)/api/chat/route.ts b/app/(chat)/api/chat/route.ts index 391faad83e..676ccb836c 100644 --- a/app/(chat)/api/chat/route.ts +++ b/app/(chat)/api/chat/route.ts @@ -1,4 +1,3 @@ -import { checkBotId } from "botid/server"; import { geolocation, ipAddress } from "@vercel/functions"; import { convertToModelMessages, @@ -8,12 +7,13 @@ import { stepCountIs, streamText, } from "ai"; +import { checkBotId } from "botid/server"; import { after } from "next/server"; import { createResumableStreamContext } from "resumable-stream"; import { auth, type UserType } from "@/app/(auth)/auth"; import { entitlementsByUserType } from "@/lib/ai/entitlements"; -import { type RequestHints, systemPrompt } from "@/lib/ai/prompts"; import { allowedModelIds } from "@/lib/ai/models"; +import { type RequestHints, systemPrompt } from "@/lib/ai/prompts"; import { getLanguageModel } from "@/lib/ai/providers"; import { createDocument } from "@/lib/ai/tools/create-document"; import { getWeather } from "@/lib/ai/tools/get-weather"; @@ -85,10 +85,10 @@ export async function POST(request: Request) { const messageCount = await getMessageCountByUserId({ id: session.user.id, - differenceInHours: 24, + differenceInHours: 1, }); - if (messageCount > entitlementsByUserType[userType].maxMessagesPerDay) { + if (messageCount > entitlementsByUserType[userType].maxMessagesPerHour) { return new ChatbotError("rate_limit:chat").toResponse(); } @@ -186,7 +186,7 @@ export async function POST(request: Request) { }); dataStream.merge( - result.toUIMessageStream({ sendReasoning: isReasoningModel }), + result.toUIMessageStream({ sendReasoning: isReasoningModel }) ); if (titlePromise) { @@ -237,7 +237,7 @@ export async function POST(request: Request) { if ( error instanceof Error && error.message?.includes( - "AI Gateway requires a valid credit card on file to service requests", + "AI Gateway requires a valid credit card on file to service requests" ) ) { return "AI Gateway requires a valid credit card on file to service requests. Please visit https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fai%3Fmodal%3Dadd-credit-card to add a card and unlock your free credits."; diff --git a/components/ai-elements/web-preview.tsx b/components/ai-elements/web-preview.tsx index 5245995199..cffa2dd944 100644 --- a/components/ai-elements/web-preview.tsx +++ b/components/ai-elements/web-preview.tsx @@ -238,7 +238,7 @@ export const WebPreviewConsole = ({ {logs.length === 0 ? (

No console output

) : ( - logs.map((log, index) => ( + logs.map((log) => (
{log.timestamp.toLocaleTimeString()} diff --git a/components/console.tsx b/components/console.tsx index ddaaf6882b..7b7f7a1917 100644 --- a/components/console.tsx +++ b/components/console.tsx @@ -162,9 +162,9 @@ export function Console({ consoleOutputs, setConsoleOutputs }: ConsoleProps) {
) : (
- {consoleOutput.contents.map((content, contentIndex) => + {consoleOutput.contents.map((content) => content.type === "image" ? ( - + output {content.value}
diff --git a/components/elements/web-preview.tsx b/components/elements/web-preview.tsx index 5181a18afa..588d63bc15 100644 --- a/components/elements/web-preview.tsx +++ b/components/elements/web-preview.tsx @@ -227,7 +227,7 @@ export const WebPreviewConsole = ({ {logs.length === 0 ? (

No console output

) : ( - logs.map((log, index) => ( + logs.map((log) => (
{log.timestamp.toLocaleTimeString()} diff --git a/lib/ai/entitlements.ts b/lib/ai/entitlements.ts index 6afbeebc54..fc456f288b 100644 --- a/lib/ai/entitlements.ts +++ b/lib/ai/entitlements.ts @@ -1,7 +1,7 @@ import type { UserType } from "@/app/(auth)/auth"; type Entitlements = { - maxMessagesPerDay: number; + maxMessagesPerHour: number; }; export const entitlementsByUserType: Record = { @@ -9,14 +9,14 @@ export const entitlementsByUserType: Record = { * For users without an account */ guest: { - maxMessagesPerDay: 10, + maxMessagesPerHour: 10, }, /* * For users with an account */ regular: { - maxMessagesPerDay: 10, + maxMessagesPerHour: 10, }, /* diff --git a/lib/ratelimit.ts b/lib/ratelimit.ts index cc75ab781d..ca417e92b4 100644 --- a/lib/ratelimit.ts +++ b/lib/ratelimit.ts @@ -3,15 +3,15 @@ import { createClient } from "redis"; import { isProductionEnvironment } from "@/lib/constants"; import { ChatbotError } from "@/lib/errors"; -const MAX_MESSAGES_PER_DAY = 10; -const TTL_SECONDS = 60 * 60 * 24; +const MAX_MESSAGES = 10; +const TTL_SECONDS = 60 * 60; let client: ReturnType | null = null; function getClient() { if (!client && process.env.REDIS_URL) { client = createClient({ url: process.env.REDIS_URL }); - client.on("error", () => {}); + client.on("error", () => undefined); client.connect().catch(() => { client = null; }); @@ -20,10 +20,14 @@ function getClient() { } export async function checkIpRateLimit(ip: string | undefined) { - if (!isProductionEnvironment || !ip) return; + if (!isProductionEnvironment || !ip) { + return; + } const redis = getClient(); - if (!redis?.isReady) return; + if (!redis?.isReady) { + return; + } try { const key = `ip-rate-limit:${ip}`; @@ -33,10 +37,12 @@ export async function checkIpRateLimit(ip: string | undefined) { .expire(key, TTL_SECONDS, "NX") .exec(); - if (typeof count === "number" && count > MAX_MESSAGES_PER_DAY) { + if (typeof count === "number" && count > MAX_MESSAGES) { throw new ChatbotError("rate_limit:chat"); } } catch (error) { - if (error instanceof ChatbotError) throw error; + if (error instanceof ChatbotError) { + throw error; + } } } diff --git a/next.config.ts b/next.config.ts index e643ad8a53..f0596a54aa 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,5 +1,5 @@ -import type { NextConfig } from "next"; import { withBotId } from "botid/next/config"; +import type { NextConfig } from "next"; const nextConfig: NextConfig = { cacheComponents: true, diff --git a/package.json b/package.json index ca66ed5a1e..15224e88e2 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "@xyflow/react": "^12.10.0", "ai": "6.0.37", "bcrypt-ts": "^5.0.2", - "botid": "1.5.6", + "botid": "1.5.11", "class-variance-authority": "^0.7.1", "classnames": "^2.5.1", "clsx": "^2.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ed5cba28c8..a4228af3f8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -102,8 +102,8 @@ importers: specifier: ^5.0.2 version: 5.0.3 botid: - specifier: 1.5.6 - version: 1.5.6(next@16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.51.0)(react-dom@19.0.1(react@19.0.1))(react@19.0.1))(react@19.0.1) + specifier: 1.5.11 + version: 1.5.11(next@16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.51.0)(react-dom@19.0.1(react@19.0.1))(react@19.0.1))(react@19.0.1) class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -2366,6 +2366,9 @@ packages: '@types/node@22.13.10': resolution: {integrity: sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==} + '@types/node@22.19.13': + resolution: {integrity: sha512-akNQMv0wW5uyRpD2v2IEyRSZiR+BeGuoB6L310EgGObO44HSMNT8z1xzio28V8qOrgYaopIDNA18YgdXd+qTiw==} + '@types/papaparse@5.3.15': resolution: {integrity: sha512-JHe6vF6x/8Z85nCX4yFdDslN11d+1pr12E526X8WAfhadOeaOTx5AuIkvDKIBopfvlzpzkdMx4YyvSKCM9oqtw==} @@ -2479,8 +2482,8 @@ packages: peerDependencies: acorn: ^8 - acorn@8.15.0: - resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} engines: {node: '>=0.4.0'} hasBin: true @@ -2507,8 +2510,8 @@ packages: resolution: {integrity: sha512-2FcgD12xPbwCoe5i9/HK0jJ1xA1m+QfC1e6htG9Bl/hNOnLyaFmQSlqLKcfe3QdnoMPKpKEGFCbESBTg+SJNOw==} engines: {node: '>=18'} - botid@1.5.6: - resolution: {integrity: sha512-KElecPjc1z6WJ6sCJMw6CvLo/CQclwlLJdobqmtOjVqvCXZZmaWvNafVDwBk0Kf5ordIaorDa3YkIx2OlGx7pg==} + botid@1.5.11: + resolution: {integrity: sha512-KOO1A3+/vFVJk5aFoG3sNwiogKFPVR+x4aw4gQ1b2e0XoE+i5xp48/EZn+WqR07jRHeDGwHWQUOtV5WVm7xiww==} peerDependencies: next: '*' react: ^18.0.0 || ^19.0.0 @@ -2521,8 +2524,8 @@ packages: buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - bufferutil@4.0.9: - resolution: {integrity: sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==} + bufferutil@4.1.0: + resolution: {integrity: sha512-ZMANVnAixE6AWWnPzlW2KpUrxhm9woycYvPOo67jWHyFowASTEd9s+QN1EIMsSDtwhIxN4sWE1jotpuDUIgyIw==} engines: {node: '>=6.14.2'} bytes@3.1.2: @@ -3509,11 +3512,11 @@ packages: resolution: {integrity: sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==} engines: {node: '>=4'} - pg-protocol@1.8.0: - resolution: {integrity: sha512-jvuYlEkL03NRvOoyoRktBK7+qU5kOvlAwvmrH8sr3wbLrOdVWsRxQfz8mMy9sZFsqJ1hEWNfdWKI4SAmoL+j7g==} + pg-protocol@1.12.0: + resolution: {integrity: sha512-uOANXNRACNdElMXJ0tPz6RBM0XQ61nONGAwlt8da5zs/iUOOCLBQOHSXnrC6fMsvtjxbOJrZZl5IScGv+7mpbg==} - pg-types@4.0.2: - resolution: {integrity: sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng==} + pg-types@4.1.0: + resolution: {integrity: sha512-o2XFanIMy/3+mThw69O8d4n1E5zsLhdO+OPqswezu7Z5ekP4hYDqlDjlmOpYMbzY2Br0ufCwJLdDIXeNVwcWFg==} engines: {node: '>=10'} picocolors@1.1.1: @@ -3786,8 +3789,8 @@ packages: resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - resolve@1.22.10: - resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + resolve@1.22.11: + resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} engines: {node: '>= 0.4'} hasBin: true @@ -3809,6 +3812,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.7.4: + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + engines: {node: '>=10'} + hasBin: true + server-only@0.0.1: resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==} @@ -3972,6 +3980,9 @@ packages: undici-types@6.20.0: resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + undici@5.28.5: resolution: {integrity: sha512-zICwjrDrcrUE0pyyJc1I2QzBkLM8FINsgOrt6WjA+BgajVq9Nxu2PbFFXUrAggLfDXlZGZBVZYw7WNV5KiBiBA==} engines: {node: '>=14.0'} @@ -4059,8 +4070,8 @@ packages: web-namespaces@2.0.1: resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} - ws@8.18.1: - resolution: {integrity: sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==} + ws@8.19.0: + resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -4791,7 +4802,7 @@ snapshots: '@types/shimmer': 1.2.0 import-in-the-middle: 1.15.0 require-in-the-middle: 7.5.2 - semver: 7.7.3 + semver: 7.7.4 shimmer: 1.2.1 transitivePeerDependencies: - supports-color @@ -5861,6 +5872,11 @@ snapshots: dependencies: undici-types: 6.20.0 + '@types/node@22.19.13': + dependencies: + undici-types: 6.21.0 + optional: true + '@types/papaparse@5.3.15': dependencies: '@types/node': 22.13.10 @@ -5869,9 +5885,9 @@ snapshots: '@types/pg@8.11.6': dependencies: - '@types/node': 22.13.10 - pg-protocol: 1.8.0 - pg-types: 4.0.2 + '@types/node': 22.19.13 + pg-protocol: 1.12.0 + pg-types: 4.1.0 optional: true '@types/prop-types@15.7.14': {} @@ -5933,8 +5949,8 @@ snapshots: '@vercel/postgres@0.10.0': dependencies: '@neondatabase/serverless': 0.9.5 - bufferutil: 4.0.9 - ws: 8.18.1(bufferutil@4.0.9) + bufferutil: 4.1.0 + ws: 8.19.0(bufferutil@4.1.0) transitivePeerDependencies: - utf-8-validate optional: true @@ -5962,11 +5978,11 @@ snapshots: d3-selection: 3.0.0 d3-zoom: 3.0.0 - acorn-import-attributes@1.9.5(acorn@8.15.0): + acorn-import-attributes@1.9.5(acorn@8.16.0): dependencies: - acorn: 8.15.0 + acorn: 8.16.0 - acorn@8.15.0: {} + acorn@8.16.0: {} ai@6.0.37(zod@3.25.76): dependencies: @@ -5990,14 +6006,14 @@ snapshots: bcrypt-ts@5.0.3: {} - botid@1.5.6(next@16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.51.0)(react-dom@19.0.1(react@19.0.1))(react@19.0.1))(react@19.0.1): + botid@1.5.11(next@16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.51.0)(react-dom@19.0.1(react@19.0.1))(react@19.0.1))(react@19.0.1): optionalDependencies: next: 16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.51.0)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) react: 19.0.1 buffer-from@1.1.2: {} - bufferutil@4.0.9: + bufferutil@4.1.0: dependencies: node-gyp-build: 4.8.4 optional: true @@ -6519,8 +6535,8 @@ snapshots: import-in-the-middle@1.15.0: dependencies: - acorn: 8.15.0 - acorn-import-attributes: 1.9.5(acorn@8.15.0) + acorn: 8.16.0 + acorn-import-attributes: 1.9.5(acorn@8.16.0) cjs-module-lexer: 1.4.3 module-details-from-path: 1.0.4 @@ -7201,10 +7217,10 @@ snapshots: pg-numeric@1.0.2: optional: true - pg-protocol@1.8.0: + pg-protocol@1.12.0: optional: true - pg-types@4.0.2: + pg-types@4.1.0: dependencies: pg-int8: 1.0.1 pg-numeric: 1.0.2 @@ -7617,13 +7633,13 @@ snapshots: dependencies: debug: 4.4.3 module-details-from-path: 1.0.4 - resolve: 1.22.10 + resolve: 1.22.11 transitivePeerDependencies: - supports-color resolve-pkg-maps@1.0.0: {} - resolve@1.22.10: + resolve@1.22.11: dependencies: is-core-module: 2.16.1 path-parse: 1.0.7 @@ -7637,7 +7653,10 @@ snapshots: scheduler@0.25.0: {} - semver@7.7.3: {} + semver@7.7.3: + optional: true + + semver@7.7.4: {} server-only@0.0.1: {} @@ -7823,6 +7842,9 @@ snapshots: undici-types@6.20.0: {} + undici-types@6.21.0: + optional: true + undici@5.28.5: dependencies: '@fastify/busboy': 2.1.1 @@ -7923,9 +7945,9 @@ snapshots: web-namespaces@2.0.1: {} - ws@8.18.1(bufferutil@4.0.9): + ws@8.19.0(bufferutil@4.1.0): optionalDependencies: - bufferutil: 4.0.9 + bufferutil: 4.1.0 optional: true xtend@4.0.2: {}