Skip to content

Commit

Permalink
fix(limits): merge window and rate limits, fix streaming headers (#134)
Browse files Browse the repository at this point in the history
Signed-off-by: Tomas Pilar <[email protected]>
  • Loading branch information
pilartomas authored Dec 13, 2024
1 parent dbefee0 commit e997180
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/rate-limit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const redis = createRedisClient({
export const rateLimitPlugin: FastifyPluginAsync = fp.default(async (app) => {
await app.register(rateLimit, {
global: true,
enableDraftSpec: true,
max: (request: FastifyRequest) => {
const authType = determineAuthType(request);
switch (authType.type) {
Expand Down
2 changes: 2 additions & 0 deletions src/runs/runs.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,14 @@ import { ensureRequestContextData } from '@/context.js';
import { getProjectPrincipal } from '@/administration/helpers.js';
import { RUNS_QUOTA_DAILY } from '@/config.js';
import { dayjs, getLatestDailyFixedTime } from '@/utils/datetime.js';
import { updateRateLimitHeadersWithDailyQuota } from '@/utils/rate-limit.js';

export async function assertRunsQuota(newRuns = 1) {
const count = await ORM.em.getRepository(Run).count({
createdBy: getProjectPrincipal(),
createdAt: { $gte: getLatestDailyFixedTime().toDate() }
});
updateRateLimitHeadersWithDailyQuota({ quota: RUNS_QUOTA_DAILY, used: count });
if (count + newRuns > RUNS_QUOTA_DAILY) {
throw new APIError({
message: 'Your daily runs quota has been exceeded',
Expand Down
5 changes: 5 additions & 0 deletions src/streaming/sse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,17 @@
*/

import { FastifyReply } from 'fastify';
import { entries } from 'remeda';

import { Event } from './dtos/event.js';

export const init = (res: FastifyReply) => {
res.hijack();
if (!res.raw.headersSent) {
const headers = res.getHeaders();
entries(headers).forEach(([key, value]) => {
if (value) res.raw.setHeader(key, value);
});
res.raw.setHeader('Content-Type', 'text/event-stream');
res.raw.setHeader('Connection', 'keep-alive');
res.raw.setHeader('Cache-Control', 'no-cache,no-transform');
Expand Down
45 changes: 45 additions & 0 deletions src/utils/rate-limit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { FastifyReply } from 'fastify';

import { dayjs, getLatestDailyFixedTime } from './datetime';

import { ensureRequestContextData } from '@/context';

function getNumericHeader(res: FastifyReply, header: string, fallback: number) {
const value = res.getHeader(header);
if (value === undefined) return fallback;
if (typeof value !== 'number') throw new Error('Invalid header type');
return value;
}

const RateLimitHeaders = {
LIMIT: 'ratelimit-limit',
REMAINING: 'ratelimit-remaining',
RESET: 'ratelimit-reset',
RETRY: 'retry-after'
} as const;

export function updateRateLimitHeadersWithDailyQuota({
quota,
used
}: {
quota: number;
used: number;
}) {
const res = ensureRequestContextData('res');
res.header(
RateLimitHeaders.LIMIT,
Math.min(getNumericHeader(res, RateLimitHeaders.LIMIT, Infinity), quota)
);
res.header(
RateLimitHeaders.REMAINING,
Math.min(getNumericHeader(res, RateLimitHeaders.REMAINING, Infinity), quota - used)
);
if (quota === used) {
const reset = Math.max(
getNumericHeader(res, RateLimitHeaders.RESET, 0),
getLatestDailyFixedTime().add(1, 'day').unix() - dayjs().unix()
);
res.header(RateLimitHeaders.RESET, reset);
res.header(RateLimitHeaders.RETRY, reset);
}
}
2 changes: 2 additions & 0 deletions src/vector-store-files/vector-store-files.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import { QueueName } from '@/jobs/constants.js';
import { getProjectPrincipal } from '@/administration/helpers.js';
import { VECTOR_STORE_FILE_QUOTA_DAILY } from '@/config.js';
import { dayjs, getLatestDailyFixedTime } from '@/utils/datetime.js';
import { updateRateLimitHeadersWithDailyQuota } from '@/utils/rate-limit.js';

const getFileLogger = (vectorStoreFileIds?: string[]) =>
getServiceLogger('vector-store-files').child({ vectorStoreFileIds });
Expand All @@ -62,6 +63,7 @@ export async function assertVectorStoreFilesQuota(newFilesCount = 1) {
createdBy: getProjectPrincipal(),
createdAt: { $gte: getLatestDailyFixedTime().toDate() }
});
updateRateLimitHeadersWithDailyQuota({ quota: VECTOR_STORE_FILE_QUOTA_DAILY, used: count });
if (count + newFilesCount > VECTOR_STORE_FILE_QUOTA_DAILY) {
throw new APIError({
message: 'Your daily vector store file quota has been exceeded',
Expand Down

0 comments on commit e997180

Please sign in to comment.