Skip to content

Commit a20501c

Browse files
committed
Split processUsage types into separate file
1 parent 567d7b0 commit a20501c

13 files changed

Lines changed: 207 additions & 183 deletions

File tree

src/app/admin/api/users/[id]/dev/insert-usage-record/route.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ import { randomUUID } from 'crypto';
55
import { getUserFromAuth } from '@/lib/user.server';
66
import { findUserById } from '@/lib/user';
77
import { forceImmediateExpirationRecomputation } from '@/lib/balanceCache';
8-
import { insertUsageRecord, type UsageMetaData } from '@/lib/processUsage';
8+
import { insertUsageRecord } from '@/lib/processUsage';
99
import type { MicrodollarUsage } from '@kilocode/db/schema';
10+
import type { UsageMetaData } from '@/lib/processUsage.types';
1011

1112
export async function POST(
1213
request: NextRequest,

src/app/api/dev/consume-credits/route.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ import { NextResponse } from 'next/server';
33
import { getUserFromAuth } from '@/lib/user.server';
44
import { findUserById } from '@/lib/user';
55
import { forceImmediateExpirationRecomputation } from '@/lib/balanceCache';
6-
import { countAndStoreUsage, type MicrodollarUsageContext } from '@/lib/processUsage';
6+
import { countAndStoreUsage } from '@/lib/processUsage';
77
import { captureException } from '@sentry/nextjs';
88
import { getFraudDetectionHeaders } from '@/lib/utils';
9+
import type { MicrodollarUsageContext } from '@/lib/processUsage.types';
910

1011
export async function POST(request: NextRequest): Promise<NextResponse> {
1112
// Check if we're in development mode

src/app/api/fim/completions/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { NextRequest } from 'next/server';
33
import { NextResponse } from 'next/server';
44
import z from 'zod';
55
import { captureException, setTag, startInactiveSpan } from '@sentry/nextjs';
6-
import type { MicrodollarUsageContext } from '@/lib/processUsage';
6+
import type { MicrodollarUsageContext } from '@/lib/processUsage.types';
77
import { validateFeatureHeader, FEATURE_HEADER } from '@/lib/feature-detection';
88
import { isFreeModel } from '@/lib/models';
99
import { sentryRootSpan } from '@/lib/getRootSpan';

src/app/api/openrouter/[...path]/route.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { NextResponse, type NextResponse as NextResponseType } from 'next/server
22
import { type NextRequest } from 'next/server';
33
import { isOpenCodeBasedClient, isRooCodeBasedClient, stripRequiredPrefix } from '@/lib/utils';
44
import { generateProviderSpecificHash } from '@/lib/providerHash';
5-
import { extractPromptInfo, type MicrodollarUsageContext } from '@/lib/processUsage';
5+
import { extractPromptInfo } from '@/lib/processUsage';
66
import { validateFeatureHeader, FEATURE_HEADER } from '@/lib/feature-detection';
77
import type { OpenRouterChatCompletionRequest } from '@/lib/providers/openrouter/types';
88
import { applyProviderSpecificLogic, getProvider, openRouterRequest } from '@/lib/providers';
@@ -60,6 +60,7 @@ import { isRateLimitedToDeath } from '@/lib/rate-limited-models';
6060
import { isActiveReviewPromo } from '@/lib/code-reviews/core/constants';
6161
import { isKiloAutoModel, resolveAutoModel } from '@/lib/kilo-auto-model';
6262
import { fixOpenCodeDuplicateReasoning } from '@/lib/providers/fixOpenCodeDuplicateReasoning';
63+
import type { MicrodollarUsageContext } from '@/lib/processUsage.types';
6364

6465
export const maxDuration = 800;
6566

src/lib/llm-proxy-helpers.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,6 @@
11
import { after, NextResponse, type NextRequest } from 'next/server';
22
import { FEATURE_HEADER, type FeatureValue } from '@/lib/feature-detection';
3-
import {
4-
type MicrodollarUsageContext,
5-
type PromptInfo,
6-
type MicrodollarUsageStats,
7-
countAndStoreUsage,
8-
logMicrodollarUsage,
9-
} from '@/lib/processUsage';
3+
import { countAndStoreUsage, logMicrodollarUsage } from '@/lib/processUsage';
104
import { startInactiveSpan, captureException, captureMessage } from '@sentry/nextjs';
115
import { APP_URL, FIRST_TOPUP_BONUS_AMOUNT } from '@/lib/constants';
126
import { summarizeUserPayments } from '@/lib/creditTransactions';
@@ -28,6 +22,11 @@ import type { OpenRouterChatCompletionRequest } from '@/lib/providers/openrouter
2822
import { createParser, type EventSourceMessage } from 'eventsource-parser';
2923
import { sentryRootSpan } from './getRootSpan';
3024
import { isKiloStealthModel, kiloFreeModels } from '@/lib/models';
25+
import type {
26+
MicrodollarUsageContext,
27+
MicrodollarUsageStats,
28+
PromptInfo,
29+
} from '@/lib/processUsage.types';
3130

3231
// FIM suffix markers for tracking purposes - used to wrap suffix in a fake system prompt format
3332
// This allows FIM requests to be tracked consistently with chat requests

src/lib/processUsage.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { test, describe, expect } from '@jest/globals';
2-
import type { MicrodollarUsageStats, MicrodollarUsageContext } from './processUsage';
2+
import type { MicrodollarUsageStats, MicrodollarUsageContext } from './processUsage.types';
33
import {
44
extractPromptInfo,
55
parseMicrodollarUsageFromStream,

src/lib/processUsage.ts

Lines changed: 17 additions & 164 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import { randomUUID } from 'crypto';
22
import { db } from './drizzle';
3-
import type { MicrodollarUsage, Organization } from '@kilocode/db/schema';
3+
import type { MicrodollarUsage } from '@kilocode/db/schema';
44
import { microdollar_usage } from '@kilocode/db/schema';
5-
import type { FeatureValue } from '@/lib/feature-detection';
65
import { createTimer } from '@/lib/timer';
76
import type { OpenAI } from 'openai';
87
import { createParser, type EventSourceMessage } from 'eventsource-parser';
@@ -11,7 +10,6 @@ import type {
1110
OpenRouterGeneration,
1211
} from './providers/openrouter/types';
1312
import { fetchGeneration, PROVIDERS } from './providers';
14-
import type { FraudDetectionHeaders } from './utils';
1513
import { toMicrodollars } from './utils';
1614
import { captureException, captureMessage, startSpan, startInactiveSpan } from '@sentry/nextjs';
1715
import type { Span } from '@sentry/nextjs';
@@ -30,45 +28,24 @@ import { appendKiloPassAuditLog } from '@/lib/kilo-pass/issuance';
3028
import { KiloPassAuditLogAction, KiloPassAuditLogResult } from '@/lib/kilo-pass/enums';
3129
import { reportAbuseCost } from '@/lib/abuse-service';
3230
import { isActiveReviewPromo } from '@/lib/code-reviews/core/constants';
31+
import type {
32+
BalanceUpdateResult,
33+
ChatCompletionChunk,
34+
CoreUsageWithMetaData,
35+
JustTheCostsUsageStats,
36+
MaybeHasOpenRouterUsage,
37+
MaybeHasVercelProviderMetaData,
38+
Message,
39+
MicrodollarUsageContext,
40+
MicrodollarUsageStats,
41+
NotYetCostedUsageStats,
42+
OpenRouterError,
43+
OpenRouterUsage,
44+
UsageMetaData,
45+
} from '@/lib/processUsage.types';
3346

3447
const posthogClient = PostHogClient();
3548

36-
export type OpenRouterUsage = {
37-
cost?: number;
38-
is_byok?: boolean | null;
39-
cost_details?: { upstream_inference_cost: number };
40-
completion_tokens: number;
41-
completion_tokens_details: { reasoning_tokens: number };
42-
prompt_tokens: number;
43-
prompt_tokens_details: { cached_tokens: number };
44-
total_tokens: number;
45-
}; //ref: https://openrouter.ai/docs/use-cases/usage-accounting#response-format
46-
47-
type VercelProviderMetaData = { gateway?: { routing?: { finalProvider?: string } } };
48-
49-
type MaybeHasVercelProviderMetaData = {
50-
choices?: {
51-
message?: {
52-
provider_metadata?: VercelProviderMetaData;
53-
};
54-
}[];
55-
};
56-
57-
type MaybeHasVercelProviderMetaDataChunk = {
58-
choices?: {
59-
delta?: { provider_metadata?: VercelProviderMetaData };
60-
}[];
61-
};
62-
63-
type MaybeHasOpenRouterUsage = {
64-
usage?: OpenRouterUsage | null;
65-
provider?: string | null;
66-
};
67-
68-
export type ChatCompletionChunk = OpenAI.Chat.Completions.ChatCompletionChunk &
69-
MaybeHasOpenRouterUsage &
70-
MaybeHasVercelProviderMetaDataChunk;
71-
7249
// For BYOK (Bring Your Own Key) requests, OpenRouter only reports 5% of the actual cost
7350
// because that's what they charge for the BYOK feature. Although we now use upstream_inference_cost, we still do some sanity checks.
7451
const OPENROUTER_BYOK_COST_MULTIPLIER = 20.0;
@@ -104,12 +81,6 @@ export function extractPromptInfo(body: OpenRouterChatCompletionRequest) {
10481
}
10582
}
10683

107-
interface Message {
108-
role: string;
109-
content?: string | { type?: string; text?: string }[];
110-
parts?: { text?: string }[];
111-
}
112-
11384
const extractMessageTextContent = (m: Message) =>
11485
typeof m.content === 'string'
11586
? m.content
@@ -120,76 +91,8 @@ const extractMessageTextContent = (m: Message) =>
12091
.join('\n')
12192
: '';
12293

123-
type NotYetCostedUsageStats = {
124-
messageId: string | null;
125-
model: string | null;
126-
responseContent: string;
127-
hasError: boolean;
128-
inference_provider: string | null;
129-
upstream_id: string | null;
130-
finish_reason: string | null;
131-
latency: number | null;
132-
moderation_latency: number | null;
133-
generation_time: number | null;
134-
streamed: boolean | null;
135-
cancelled: boolean | null;
136-
};
137-
138-
type JustTheCostsUsageStats = {
139-
cost_mUsd: number;
140-
cacheDiscount_mUsd?: number;
141-
/** The real cost before any free/BYOK/promo zeroing. Set by processTokenData. */
142-
market_cost?: number;
143-
inputTokens: number;
144-
outputTokens: number;
145-
cacheWriteTokens: number;
146-
cacheHitTokens: number;
147-
is_byok: boolean | null;
148-
};
149-
150-
export type MicrodollarUsageStats = NotYetCostedUsageStats & JustTheCostsUsageStats;
151-
152-
export type PromptInfo = {
153-
system_prompt_prefix: string;
154-
system_prompt_length: number;
155-
user_prompt_prefix: string;
156-
};
157-
158-
export type MicrodollarUsageContext = {
159-
kiloUserId: string;
160-
fraudHeaders: FraudDetectionHeaders;
161-
organizationId?: Organization['id'];
162-
provider: ProviderId;
163-
requested_model: string;
164-
promptInfo: PromptInfo;
165-
max_tokens: number | null;
166-
has_middle_out_transform: boolean | null;
167-
isStreaming: boolean;
168-
prior_microdollar_usage: number;
169-
/** User email for authenticated users - used as PostHog distinctId. Undefined for anonymous users. */
170-
posthog_distinct_id?: string;
171-
project_id: string | null;
172-
status_code: number | null;
173-
editor_name: string | null;
174-
machine_id: string | null;
175-
/** True if user/org is using their own API key - cost should be zeroed out */
176-
user_byok: boolean;
177-
has_tools: boolean;
178-
botId?: string;
179-
tokenSource?: string;
180-
/** Request ID from abuse service classify response, for cost tracking correlation. 0 means skip. */
181-
abuse_request_id?: number;
182-
/** Which product feature generated this API call. NULL if header not sent. */
183-
feature: FeatureValue | null;
184-
/** Client session/task identifier from X-KiloCode-TaskId header. */
185-
session_id: string | null;
186-
/** Client mode from x-kilocode-mode header (e.g. 'code', 'build', 'architect'). */
187-
mode: string | null;
188-
/** The auto model ID when one was requested (e.g. 'kilo-auto/free'). */
189-
auto_model: string | null;
190-
};
191-
19294
export type UsageContextInfo = ReturnType<typeof extractUsageContextInfo>;
95+
19396
export function extractUsageContextInfo(usageContext: MicrodollarUsageContext) {
19497
return {
19598
kilo_user_id: usageContext.kiloUserId,
@@ -213,11 +116,6 @@ export function extractUsageContextInfo(usageContext: MicrodollarUsageContext) {
213116
};
214117
}
215118

216-
export type CoreUsageWithMetaData = {
217-
core: MicrodollarUsage;
218-
metadata: UsageMetaData;
219-
};
220-
221119
export function toInsertableDbUsageRecord(
222120
usageStats: MicrodollarUsageStats,
223121
usageContextInfo: UsageContextInfo
@@ -344,8 +242,6 @@ async function sendFirstUsageEvent(usage: MicrodollarUsage, posthog_distinct_id:
344242
}
345243
}
346244

347-
type BalanceUpdateResult = { newMicrodollarsUsed: number } | null;
348-
349245
async function sendFirstMicrodollarUsageEventIfNeeded(
350246
balanceUpdateResult: BalanceUpdateResult,
351247
usage: MicrodollarUsage,
@@ -414,42 +310,6 @@ ${metaDataKindName}_cte AS (
414310
SELECT ${metaDataKindName}_id FROM ${metaDataKindName}_ins
415311
)`;
416312

417-
export type UsageMetaData = {
418-
id: string;
419-
message_id: string;
420-
created_at: string;
421-
http_x_forwarded_for: string | null;
422-
http_x_vercel_ip_city: string | null;
423-
http_x_vercel_ip_country: string | null;
424-
http_x_vercel_ip_latitude: number | null;
425-
http_x_vercel_ip_longitude: number | null;
426-
http_x_vercel_ja4_digest: string | null;
427-
user_prompt_prefix: string | null;
428-
system_prompt_prefix: string | null;
429-
system_prompt_length: number | null;
430-
http_user_agent: string | null;
431-
max_tokens: number | null;
432-
has_middle_out_transform: boolean | null;
433-
status_code: number | null;
434-
upstream_id: string | null;
435-
finish_reason: string | null;
436-
latency: number | null;
437-
moderation_latency: number | null;
438-
generation_time: number | null;
439-
is_byok: boolean | null;
440-
is_user_byok: boolean;
441-
streamed: boolean | null;
442-
cancelled: boolean | null;
443-
editor_name: string | null;
444-
has_tools: boolean | null;
445-
machine_id: string | null;
446-
feature: string | null;
447-
session_id: string | null;
448-
mode: string | null;
449-
auto_model: string | null;
450-
market_cost: number | null;
451-
};
452-
453313
export async function insertUsageRecord(
454314
coreUsageFields: MicrodollarUsage,
455315
metadataFields: UsageMetaData
@@ -684,13 +544,6 @@ export function countAndStoreUsage(
684544
return usageStatsPromise.then(usageStats => processTokenData(usageStats, usageContext));
685545
}
686546

687-
type OpenRouterError = {
688-
message: string;
689-
code: string;
690-
metadata?: Record<string, unknown>;
691-
provider_name?: string;
692-
};
693-
694547
export function processOpenRouterUsage(
695548
usage: OpenRouterUsage | null | undefined,
696549
coreProps: NotYetCostedUsageStats

0 commit comments

Comments
 (0)