-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathai-pricing.ts
More file actions
117 lines (105 loc) · 3.55 KB
/
Copy pathai-pricing.ts
File metadata and controls
117 lines (105 loc) · 3.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
export const AI_PRICING_VERSION = "openai-2026-08-02-standard" as const;
export const LEGACY_AI_PRICING_VERSION = "sandevistan-legacy-category-rates" as const;
export type AiUsageCharge = {
readonly estimatedNanoUsd: number;
readonly unpricedEvents: number;
readonly pricingVersions: readonly string[];
};
export type RealtimePricingInput = {
readonly model: string;
readonly textInputTokens: number;
readonly cachedTextInputTokens: number;
readonly audioInputTokens: number;
readonly cachedAudioInputTokens: number;
readonly textOutputTokens: number;
};
export type TranscriptionPricingInput = {
readonly model: string;
readonly audioInputTokens: number;
readonly textOutputTokens: number;
};
export type SearchPricingInput = {
readonly model: string;
readonly inputTokens: number;
readonly cachedInputTokens: number;
readonly outputTokens: number;
readonly webSearchCalls: number;
};
const NANO_USD_PER_SEARCH_ACTION = 10_000_000;
function count(value: number): number {
return Number.isFinite(value) ? Math.max(0, Math.floor(value)) : 0;
}
function charge(estimatedNanoUsd: number, unpricedEvents = 0): AiUsageCharge {
return {
estimatedNanoUsd: Math.max(0, Math.round(estimatedNanoUsd)),
unpricedEvents: count(unpricedEvents),
pricingVersions: [AI_PRICING_VERSION],
};
}
export function emptyAiUsageCharge(): AiUsageCharge {
return { estimatedNanoUsd: 0, unpricedEvents: 0, pricingVersions: [] };
}
export function mergeAiUsageCharge(
left: AiUsageCharge,
right: AiUsageCharge,
): AiUsageCharge {
return {
estimatedNanoUsd: count(left.estimatedNanoUsd) + count(right.estimatedNanoUsd),
unpricedEvents: count(left.unpricedEvents) + count(right.unpricedEvents),
pricingVersions: [...new Set([
...left.pricingVersions,
...right.pricingVersions,
])],
};
}
export function priceRealtimeUsage(input: RealtimePricingInput): AiUsageCharge {
if (input.model !== "gpt-realtime" && !input.model.startsWith("gpt-realtime-")) {
const hasUsage = [
input.textInputTokens,
input.cachedTextInputTokens,
input.audioInputTokens,
input.cachedAudioInputTokens,
input.textOutputTokens,
].some((value) => count(value) > 0);
return charge(0, hasUsage ? 1 : 0);
}
return charge(
count(input.textInputTokens) * 4_000
+ count(input.cachedTextInputTokens) * 400
+ count(input.audioInputTokens) * 32_000
+ count(input.cachedAudioInputTokens) * 400
+ count(input.textOutputTokens) * 16_000,
);
}
export function priceTranscriptionUsage(
input: TranscriptionPricingInput,
): AiUsageCharge {
if (
input.model !== "gpt-4o-mini-transcribe"
&& !input.model.startsWith("gpt-4o-mini-transcribe-")
) {
const hasUsage = count(input.audioInputTokens) > 0
|| count(input.textOutputTokens) > 0;
return charge(0, hasUsage ? 1 : 0);
}
return charge(
count(input.audioInputTokens) * 1_250
+ count(input.textOutputTokens) * 5_000,
);
}
export function priceSearchUsage(input: SearchPricingInput): AiUsageCharge {
const actionCost = count(input.webSearchCalls) * NANO_USD_PER_SEARCH_ACTION;
const totalInput = count(input.inputTokens);
const cachedInput = Math.min(totalInput, count(input.cachedInputTokens));
const output = count(input.outputTokens);
const supported = input.model === "gpt-5.5" || input.model.startsWith("gpt-5.5-");
if (!supported) {
return charge(actionCost, totalInput > 0 || output > 0 ? 1 : 0);
}
return charge(
(totalInput - cachedInput) * 5_000
+ cachedInput * 500
+ output * 30_000
+ actionCost,
);
}