refactor: standardize runtime guard patterns with lodash-es (#154)

* refactor(ui): replace runtime type guards with lodash-es predicates

* refactor(runtime): migrate typeof checks to lodash-es in core modules

* refactor(ipc): standardize runtime guards with lodash-es

* test(refactor): align runtime guard checks with lodash-es

* docs(agents): add lodash-es runtime guard skill summary
This commit is contained in:
vin authored and GitHub committed 2026-04-12 11:41:56 +08:00
1 parent 0223e8d807
commit bc35b96b5d
33 files changed
+303 -208

No files matched your search

@@ -1,6 +1,6 @@
import { Injectable, Logger } from '@nestjs/common';
import axios, { AxiosProxyConfig, AxiosRequestConfig, AxiosResponse } from 'axios';
import { isNil } from 'lodash-es';
import { isEmpty, isFunction, isNil, isObjectLike, isString } from 'lodash-es';
import { GeminiRequest, GeminiResponse } from '../interfaces/request-interfaces';
import { GeminiInternalRequest } from '../../../../lib/antigravity/types';
import { getServerConfig } from '../../../server-config';
@@ -131,10 +131,10 @@ export class GeminiClient {
extraHeaders,
);
const payload = response.data;
if (payload && typeof payload === 'object' && 'response' in payload) {
return payload.response;
if (isObjectLike(payload) && 'response' in payload) {
return (payload as { response: GeminiResponse }).response;
}
return payload;
return payload as GeminiResponse;
}
private getInternalBaseUrls(): string[] {
@@ -255,7 +255,7 @@ export class GeminiClient {
return fromObject;
}
if (typeof responseData === 'string') {
if (isString(responseData)) {
return this.extractAxiosErrorMessageFromText(responseData);
}
@@ -272,20 +272,20 @@ export class GeminiClient {
}
private extractAxiosErrorMessageFromObject(responseData: unknown): string | null {
if (!responseData || typeof responseData !== 'object' || this.isReadableStream(responseData)) {
if (!isObjectLike(responseData) || this.isReadableStream(responseData)) {
return null;
}
const errorRecord = (responseData as { error?: unknown }).error;
if (errorRecord && typeof errorRecord === 'object') {
if (isObjectLike(errorRecord)) {
const message = (errorRecord as { message?: unknown }).message;
if (typeof message === 'string' && message.trim() !== '') {
if (isString(message) && !isEmpty(message.trim())) {
return message.trim();
}
}
const message = (responseData as { message?: unknown }).message;
if (typeof message === 'string' && message.trim() !== '') {
if (isString(message) && !isEmpty(message.trim())) {
return message.trim();
}
@@ -333,11 +333,7 @@ export class GeminiClient {
}
private isReadableStream(value: unknown): value is NodeJS.ReadableStream {
return (
Boolean(value) &&
typeof value === 'object' &&
typeof (value as { pipe?: unknown }).pipe === 'function'
);
return isObjectLike(value) && isFunction((value as { pipe?: unknown }).pipe);
}
private async readStreamAsText(stream: NodeJS.ReadableStream): Promise<string | null> {
@@ -407,17 +403,17 @@ export class GeminiClient {
}
private extractRetryAfterHeader(headers: unknown): string | undefined {
if (!headers || typeof headers !== 'object') {
if (!isObjectLike(headers)) {
return undefined;
}
const retryAfter = (headers as Record<string, unknown>)['retry-after'];
if (typeof retryAfter === 'string' && retryAfter.trim() !== '') {
if (isString(retryAfter) && !isEmpty(retryAfter.trim())) {
return retryAfter.trim();
}
if (Array.isArray(retryAfter) && retryAfter.length > 0) {
const first = retryAfter[0];
if (typeof first === 'string' && first.trim() !== '') {
if (isString(first) && !isEmpty(first.trim())) {
return first.trim();
}
}
@@ -439,7 +435,7 @@ export class GeminiClient {
try {
const seen = new WeakSet();
return JSON.stringify(obj, (key, value) => {
if (typeof value === 'object' && value !== null) {
if (isObjectLike(value)) {
if (seen.has(value)) return '[Circular]';
seen.add(value);
}
@@ -1,3 +1,5 @@
import { isNumber, isObjectLike, isString } from 'lodash-es';
export interface UpstreamErrorHeaders {
retryAfter?: string;
}
@@ -16,10 +18,10 @@ export class UpstreamRequestError extends Error {
super(params.message);
this.name = 'UpstreamRequestError';
if (params.status && typeof params.status !== 'number') {
if (params.status && !isNumber(params.status)) {
throw new TypeError('status must be a number');
}
if (params.headers && typeof params.headers !== 'object') {
if (params.headers && !isObjectLike(params.headers)) {
throw new TypeError('headers must be an object');
}
@@ -27,7 +29,7 @@ export class UpstreamRequestError extends Error {
this.headers = params.headers;
// Sanitize and limit body size
if (typeof params.body === 'string') {
if (isString(params.body)) {
const sanitized = params.body.replace(/<[^>]*>?/gm, '');
this.body = sanitized.substring(0, 1000);
} else {
@@ -11,7 +11,7 @@ import {
Optional,
} from '@nestjs/common';
import { FastifyReply } from 'fastify';
import { isEmpty } from 'lodash-es';
import { isEmpty, isFunction, isNumber, isString } from 'lodash-es';
import { Observable } from 'rxjs';
import { ProxyGuard } from './proxy.guard';
@@ -198,7 +198,7 @@ export class GeminiController {
const candidates = (response.candidates ?? []).map((candidate, index) => ({
content: candidate.content,
finishReason: candidate.finishReason,
index: typeof candidate.index === 'number' ? candidate.index : index,
index: isNumber(candidate.index) ? candidate.index : index,
}));
const normalized: GeminiResponse = {
@@ -246,8 +246,8 @@ export class GeminiController {
private writeObservableSseResponse(res: FastifyReply, stream: Observable<unknown>): void {
if (
!res.raw ||
typeof res.raw.writeHead !== 'function' ||
typeof res.raw.write !== 'function'
!isFunction(res.raw.writeHead) ||
!isFunction(res.raw.write)
) {
res.header('Content-Type', 'text/event-stream');
res.header('Cache-Control', 'no-cache');
@@ -271,7 +271,7 @@ export class GeminiController {
if (res.raw.writableEnded) {
return;
}
const payload = typeof chunk === 'string' ? chunk : String(chunk ?? '');
const payload = isString(chunk) ? chunk : String(chunk ?? '');
res.raw.write(payload);
},
error: (error) => {
@@ -302,6 +302,6 @@ export class GeminiController {
}
private supportsReplyHijack(reply: FastifyReply): reply is FastifyReply & { hijack: () => void } {
return typeof (reply as { hijack?: unknown }).hijack === 'function';
return isFunction((reply as { hijack?: unknown }).hijack);
}
}
+13 -17
View File
@@ -12,7 +12,7 @@ import {
Optional,
} from '@nestjs/common';
import { FastifyReply, FastifyRequest } from 'fastify';
import { isNil, isPlainObject, isString } from 'lodash-es';
import { isEmpty, isFunction, isNil, isObjectLike, isPlainObject, isString } from 'lodash-es';
import { ProxyService } from './proxy.service';
import { Observable } from 'rxjs';
import {
@@ -339,7 +339,7 @@ export class ProxyController {
private toLegacyTextCompletionsResponse(response: OpenAIChatResponse): Record<string, unknown> {
const choice = response.choices?.[0];
const content = choice?.message?.content;
const text = typeof content === 'string' ? content : '';
const text = isString(content) ? content : '';
return {
id: response.id,
@@ -397,7 +397,7 @@ export class ProxyController {
stream?: boolean;
}): OpenAIChatRequest {
const messages: OpenAIChatRequest['messages'] = [];
if (body.instructions && body.instructions.trim() !== '') {
if (isString(body.instructions) && !isEmpty(body.instructions.trim())) {
messages.push({
role: 'system',
content: body.instructions,
@@ -484,7 +484,7 @@ export class ProxyController {
continue;
}
}
} else if (typeof body.input === 'string') {
} else if (isString(body.input)) {
messages.push({
role: 'user',
content: body.input,
@@ -716,18 +716,14 @@ export class ProxyController {
}
private isObservableLike(value: unknown): value is Observable<unknown> {
return (
Boolean(value) &&
typeof value === 'object' &&
typeof (value as { subscribe?: unknown }).subscribe === 'function'
);
return isObjectLike(value) && isFunction((value as { subscribe?: unknown }).subscribe);
}
private writeSseResponse(res: FastifyReply, stream: Observable<unknown>): void {
if (
!res.raw ||
typeof res.raw.writeHead !== 'function' ||
typeof res.raw.write !== 'function'
!isFunction(res.raw.writeHead) ||
!isFunction(res.raw.write)
) {
res.header('Content-Type', 'text/event-stream');
res.header('Cache-Control', 'no-cache');
@@ -736,7 +732,7 @@ export class ProxyController {
return;
}
if (typeof (res as { hijack?: () => void }).hijack === 'function') {
if (isFunction((res as { hijack?: () => void }).hijack)) {
(res as { hijack: () => void }).hijack();
}
@@ -751,7 +747,7 @@ export class ProxyController {
if (res.raw.writableEnded) {
return;
}
const payload = typeof chunk === 'string' ? chunk : String(chunk ?? '');
const payload = isString(chunk) ? chunk : String(chunk ?? '');
res.raw.write(payload);
},
error: (error) => {
@@ -804,7 +800,7 @@ export class ProxyController {
}
const content = result.choices?.[0]?.message?.content;
const image = this.extractInlineBase64Image(typeof content === 'string' ? content : '');
const image = this.extractInlineBase64Image(isString(content) ? content : '');
if (!image) {
this.logProxyEndpointError(
'/v1/images/generations',
@@ -908,7 +904,7 @@ export class ProxyController {
if (!userMessage) {
parts.push({ text: fallbackPrompt || 'Please generate an image based on this request.' });
} else if (typeof userMessage.content === 'string') {
} else if (isString(userMessage.content)) {
parts.push({
text:
userMessage.content ||
@@ -917,7 +913,7 @@ export class ProxyController {
});
} else {
for (const block of userMessage.content) {
if (block.type === 'text' && typeof block.text === 'string' && block.text.trim() !== '') {
if (block.type === 'text' && isString(block.text) && !isEmpty(block.text.trim())) {
textParts.push(block.text);
}
if (block.type === 'image_url') {
@@ -964,7 +960,7 @@ export class ProxyController {
private hasMultipartBoundary(req: FastifyRequest): boolean {
const contentType = req.headers['content-type'];
if (typeof contentType !== 'string') {
if (!isString(contentType)) {
return false;
}
+20 -24
View File
@@ -1,5 +1,5 @@
import { Injectable, Logger, Inject } from '@nestjs/common';
import { isEmpty, isNil, isPlainObject, isString } from 'lodash-es';
import { isEmpty, isNil, isNumber, isPlainObject, isString } from 'lodash-es';
import { TokenManagerService } from './token-manager.service';
import { GeminiClient } from './clients/gemini.client';
import { v4 as uuidv4 } from 'uuid';
@@ -511,7 +511,7 @@ export class ProxyService {
private getModelOutputCap(accountId: string, model: string): number {
const normalizedModel = this.normalizeModelIdentifier(model);
const dynamicCap = this.tokenManager.getModelOutputLimitForAccount(accountId, normalizedModel);
if (typeof dynamicCap === 'number' && Number.isFinite(dynamicCap) && dynamicCap > 0) {
if (isNumber(dynamicCap) && Number.isFinite(dynamicCap) && dynamicCap > 0) {
return Math.floor(dynamicCap);
}
return getMaxOutputTokens(normalizedModel);
@@ -523,7 +523,7 @@ export class ProxyService {
accountId,
normalizedModel,
);
if (typeof dynamicBudget === 'number' && Number.isFinite(dynamicBudget) && dynamicBudget >= 0) {
if (isNumber(dynamicBudget) && Number.isFinite(dynamicBudget) && dynamicBudget >= 0) {
return Math.floor(dynamicBudget);
}
return getThinkingBudget(normalizedModel);
@@ -548,12 +548,12 @@ export class ProxyService {
| undefined;
const adaptiveSentinel =
thinkingConfig &&
(typeof thinkingConfig.thinkingLevel === 'string' ||
(isString(thinkingConfig.thinkingLevel) ||
thinkingConfig.thinkingBudget === -1 ||
thinkingConfig.thinkingBudget === 32768);
if (thinkingConfig) {
if (!isClaudeModel && typeof thinkingConfig.thinkingLevel === 'string') {
if (!isClaudeModel && isString(thinkingConfig.thinkingLevel)) {
const converted = this.resolveThinkingLevelBudget(thinkingConfig.thinkingLevel);
if (converted !== undefined) {
thinkingConfig.thinkingBudget = converted;
@@ -561,12 +561,12 @@ export class ProxyService {
delete thinkingConfig.thinkingLevel;
}
if (typeof thinkingConfig.thinkingBudget === 'number' && thinkingConfig.thinkingBudget < 0) {
if (isNumber(thinkingConfig.thinkingBudget) && thinkingConfig.thinkingBudget < 0) {
thinkingConfig.thinkingBudget = Math.min(thinkingBudgetCap, 24576);
}
if (
typeof thinkingConfig.thinkingBudget === 'number' &&
isNumber(thinkingConfig.thinkingBudget) &&
Number.isFinite(thinkingConfig.thinkingBudget)
) {
thinkingConfig.thinkingBudget = Math.min(
@@ -595,7 +595,7 @@ export class ProxyService {
}
if (
typeof generationConfig.maxOutputTokens === 'number' &&
isNumber(generationConfig.maxOutputTokens) &&
Number.isFinite(generationConfig.maxOutputTokens)
) {
generationConfig.maxOutputTokens = Math.min(
@@ -634,7 +634,7 @@ export class ProxyService {
? response.candidates.map((candidate, index) => ({
content: candidate?.content,
finishReason: candidate?.finishReason,
index: typeof candidate?.index === 'number' ? candidate.index : index,
index: isNumber(candidate?.index) ? candidate.index : index,
}))
: [];
@@ -1115,7 +1115,7 @@ export class ProxyService {
const choice = response.choices?.[0];
const finishReason = choice?.finish_reason ?? 'stop';
const content =
choice?.message && typeof choice.message.content === 'string' ? choice.message.content : '';
choice?.message && isString(choice.message.content) ? choice.message.content : '';
const chunkSize = 80;
if (content.length === 0) {
@@ -1216,7 +1216,7 @@ export class ProxyService {
systemInstruction: request.systemInstruction
? {
parts: request.systemInstruction.parts
.filter((part): part is { text: string } => typeof part.text === 'string')
.filter((part): part is { text: string } => isString(part.text))
.map((part) => ({ text: part.text })),
}
: undefined,
@@ -1294,7 +1294,7 @@ export class ProxyService {
private convertOpenAIPartsToAnthropicContent(
content: OpenAIChatRequest['messages'][number]['content'],
): AnthropicContent[] {
if (typeof content === 'string') {
if (isString(content)) {
return content.trim() ? [{ type: 'text', text: content }] : [];
}
@@ -1328,7 +1328,7 @@ export class ProxyService {
private extractOpenAITextContent(
content: OpenAIChatRequest['messages'][number]['content'],
): string {
if (typeof content === 'string') {
if (isString(content)) {
return content;
}
@@ -1339,14 +1339,14 @@ export class ProxyService {
}
private parseOpenAIFunctionArguments(argumentsString: string): Record<string, unknown> {
if (!argumentsString || argumentsString.trim() === '') {
if (isEmpty(argumentsString.trim())) {
return {};
}
try {
const parsed = JSON.parse(argumentsString);
if (this.isRecord(parsed)) {
return parsed;
if (isPlainObject(parsed)) {
return parsed as Record<string, unknown>;
}
return { value: parsed };
} catch {
@@ -1446,7 +1446,7 @@ export class ProxyService {
}
private normalizeToolCallArguments(input: unknown): string {
if (typeof input === 'string') {
if (isString(input)) {
return input;
}
if (isNil(input)) {
@@ -1730,7 +1730,7 @@ export class ProxyService {
const metadata = request.metadata;
const sessionCandidate =
metadata?.session_id ?? metadata?.sessionId ?? metadata?.user_id ?? metadata?.userId;
if (!isString(sessionCandidate) || sessionCandidate.trim() === '') {
if (!isString(sessionCandidate) || isEmpty(sessionCandidate.trim())) {
return undefined;
}
return `anthropic:${sessionCandidate.trim()}`;
@@ -1740,17 +1740,13 @@ export class ProxyService {
const extra = request.extra;
const sessionCandidate =
extra?.session_id ?? extra?.sessionId ?? extra?.user_id ?? extra?.userId;
if (!isString(sessionCandidate) || sessionCandidate.trim() === '') {
if (!isString(sessionCandidate) || isEmpty(sessionCandidate.trim())) {
return undefined;
}
return `openai:${sessionCandidate.trim()}`;
}
private isRecord(value: unknown): value is Record<string, unknown> {
private isGeminiPart(value: unknown): value is InternalGeminiPart {
return isPlainObject(value);
}
private isGeminiPart(value: unknown): value is InternalGeminiPart {
return this.isRecord(value);
}
}
+12 -10
View File
@@ -1,3 +1,5 @@
import { isEmpty, isNumber, isObjectLike, isString } from 'lodash-es';
export enum RateLimitReason {
QuotaExhausted = 'quota_exhausted',
RateLimitExceeded = 'rate_limit_exceeded',
@@ -68,7 +70,7 @@ function tryParseGoogleErrorBody(body: string | undefined): ParsedGoogleErrorBod
try {
const parsed = JSON.parse(trimmed) as ParsedGoogleErrorBody;
if (!parsed || typeof parsed !== 'object') {
if (!isObjectLike(parsed)) {
return null;
}
return parsed;
@@ -96,7 +98,7 @@ export class RateLimitTracker {
private readonly failureCounts = new Map<string, FailureCountEntry>();
private buildLockoutKey(accountId: string, model?: string): string {
if (model && model.trim() !== '') {
if (!isEmpty(model?.trim() ?? '')) {
return `${accountId}:${model}`;
}
return accountId;
@@ -150,7 +152,7 @@ export class RateLimitTracker {
model?: string,
): void {
const retryAfterSec = Math.max(2, Math.ceil((resetTimeMs - Date.now()) / 1000));
const key = model && model.trim() !== '' ? this.buildLockoutKey(accountId, model) : accountId;
const key = !isEmpty(model?.trim() ?? '') ? this.buildLockoutKey(accountId, model) : accountId;
this.lockoutByKey.set(key, {
resetTimeMs,
retryAfterSec,
@@ -240,7 +242,7 @@ export class RateLimitTracker {
const parsedBody = tryParseGoogleErrorBody(body);
const details = Array.isArray(parsedBody?.error?.details) ? parsedBody?.error?.details : [];
for (const detail of details) {
if (typeof detail.reason !== 'string' || detail.reason.trim() === '') {
if (!isString(detail.reason) || isEmpty(detail.reason.trim())) {
continue;
}
const mappedReason = mapGoogleReasonToTrackerReason(detail.reason);
@@ -354,7 +356,7 @@ export class RateLimitTracker {
const parsedBody = tryParseGoogleErrorBody(body);
const details = Array.isArray(parsedBody?.error?.details) ? parsedBody.error.details : [];
for (const detail of details) {
if (typeof detail.retryDelay === 'string' && detail.retryDelay.trim() !== '') {
if (isString(detail.retryDelay) && !isEmpty(detail.retryDelay.trim())) {
const parsedDelay = parseDurationToSeconds(detail.retryDelay);
if (parsedDelay !== null) {
return parsedDelay;
@@ -362,8 +364,8 @@ export class RateLimitTracker {
}
if (
typeof detail.metadata?.retryDelay === 'string' &&
detail.metadata.retryDelay.trim() !== ''
isString(detail.metadata?.retryDelay) &&
!isEmpty(detail.metadata.retryDelay.trim())
) {
const parsedDelay = parseDurationToSeconds(detail.metadata.retryDelay);
if (parsedDelay !== null) {
@@ -372,8 +374,8 @@ export class RateLimitTracker {
}
if (
typeof detail.metadata?.quotaResetDelay === 'string' &&
detail.metadata.quotaResetDelay.trim() !== ''
isString(detail.metadata?.quotaResetDelay) &&
!isEmpty(detail.metadata.quotaResetDelay.trim())
) {
const parsedDelay = parseDurationToSeconds(detail.metadata.quotaResetDelay);
if (parsedDelay !== null) {
@@ -383,7 +385,7 @@ export class RateLimitTracker {
}
const retryAfter = parsedBody?.error?.retry_after;
if (typeof retryAfter === 'number' && retryAfter > 0) {
if (isNumber(retryAfter) && retryAfter > 0) {
return Math.ceil(retryAfter);
}
@@ -1,4 +1,5 @@
import axios from 'axios';
import { isString } from 'lodash-es';
import { getAntigravityVersion } from '../../../utils/antigravityVersion';
import { logger } from '../../../utils/logger';
@@ -20,7 +21,7 @@ let cachedUserAgentResolution: UserAgentResolution | null = null;
let pendingUserAgentResolution: Promise<UserAgentResolution> | null = null;
function normalizeNonEmptyString(value: string | null | undefined): string | null {
if (typeof value !== 'string') {
if (!isString(value)) {
return null;
}
@@ -125,7 +126,7 @@ async function fetchTextPayload(url: string): Promise<string | null> {
},
});
if (typeof response.data === 'string') {
if (isString(response.data)) {
return response.data;
}
@@ -1,4 +1,5 @@
import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
import { isEmpty, isNumber, isString } from 'lodash-es';
import { CloudAccountRepo } from '../../../ipc/database/cloudHandler';
import { CloudAccount, CloudQuotaData } from '../../../types/cloudAccount';
import { GoogleAPIService } from '../../../services/GoogleAPIService';
@@ -36,7 +37,7 @@ interface GetNextTokenOptions {
type TokenEntry = [string, TokenData];
function normalizeProjectId(projectId: string | null | undefined): string | undefined {
if (typeof projectId !== 'string') {
if (!isString(projectId)) {
return undefined;
}
@@ -53,7 +54,7 @@ function normalizeProjectId(projectId: string | null | undefined): string | unde
}
function normalizeModelId(modelId: string | null | undefined): string | undefined {
if (typeof modelId !== 'string') {
if (!isString(modelId)) {
return undefined;
}
const normalized = modelId.replace(/^models\//i, '').trim();
@@ -61,7 +62,7 @@ function normalizeModelId(modelId: string | null | undefined): string | undefine
}
function normalizeClientKey(clientKey: string | undefined): string | undefined {
if (typeof clientKey !== 'string') {
if (!isString(clientKey)) {
return undefined;
}
const normalized = clientKey.trim().toLowerCase();
@@ -190,7 +191,7 @@ export class TokenManagerService implements OnModuleInit {
const accountId = this.resolveAccountId(params.accountIdOrEmail) ?? params.accountIdOrEmail;
const normalizedModel = normalizeModelId(params.model);
const hasExplicitRetryWindow =
Boolean(params.retryAfter && params.retryAfter.trim() !== '') ||
Boolean(isString(params.retryAfter) && !isEmpty(params.retryAfter.trim())) ||
Boolean(params.body && params.body.includes('quotaResetDelay'));
if (!hasExplicitRetryWindow && (params.status ?? 0) === 429) {
@@ -215,7 +216,7 @@ export class TokenManagerService implements OnModuleInit {
if (
parsed.reason !== RateLimitReason.QuotaExhausted ||
!parsed.model ||
parsed.model.trim() === ''
isEmpty(parsed.model.trim())
) {
this.accountCooldowns.set(accountId, Date.now() + parsed.retryAfterSec * 1000);
}
@@ -258,7 +259,7 @@ export class TokenManagerService implements OnModuleInit {
if (
parsed.reason !== RateLimitReason.QuotaExhausted ||
!parsed.model ||
parsed.model.trim() === ''
isEmpty(parsed.model.trim())
) {
this.accountCooldowns.set(accountId, Date.now() + parsed.retryAfterSec * 1000);
}
@@ -661,14 +662,14 @@ export class TokenManagerService implements OnModuleInit {
const limitCandidate = modelInfo.max_output_tokens ?? modelInfo.max_tokens;
if (
typeof limitCandidate === 'number' &&
isNumber(limitCandidate) &&
Number.isFinite(limitCandidate) &&
limitCandidate > 0
) {
modelLimits[normalizedModel] = Math.floor(limitCandidate);
}
if (typeof modelInfo.resetTime === 'string' && modelInfo.resetTime.trim() !== '') {
if (isString(modelInfo.resetTime) && !isEmpty(modelInfo.resetTime.trim())) {
modelResetTimes[normalizedModel] = modelInfo.resetTime;
}
}
@@ -692,7 +693,7 @@ export class TokenManagerService implements OnModuleInit {
}
private findEarliestQuotaResetTime(modelResetTimes: Record<string, string>): string | null {
const validTimes = Object.values(modelResetTimes).filter((value) => value.trim() !== '');
const validTimes = Object.values(modelResetTimes).filter((value) => !isEmpty(value.trim()));
if (validTimes.length === 0) {
return null;
}
@@ -1084,7 +1085,7 @@ export class TokenManagerService implements OnModuleInit {
continue;
}
const budget = modelInfo?.thinking_budget;
if (typeof budget === 'number' && Number.isFinite(budget) && budget >= 0) {
if (isNumber(budget) && Number.isFinite(budget) && budget >= 0) {
return Math.floor(budget);
}
}