Repository navigation
Expand file tree
/
Copy pathai-realtime-usage.ts
More file actions
97 lines (93 loc) · 3.08 KB
/
Copy pathai-realtime-usage.ts
File metadata and controls
97 lines (93 loc) · 3.08 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
import { EMPTY_AI_USAGE, type AiUsage } from "./ai-cost";
export type RealtimeServerEvent = {
readonly type?: string;
readonly item_id?: string;
readonly response_id?: string;
readonly transcript?: string;
readonly delta?: string;
readonly text?: string;
readonly error?: { readonly message?: string };
readonly usage?: {
readonly input_tokens?: number;
readonly output_tokens?: number;
readonly input_token_details?: {
readonly text_tokens?: number;
readonly audio_tokens?: number;
};
readonly output_token_details?: { readonly text_tokens?: number };
};
readonly response?: {
readonly id?: string;
readonly status?: string;
readonly status_details?: { readonly error?: { readonly message?: string } };
readonly usage?: {
readonly input_tokens?: number;
readonly output_tokens?: number;
readonly input_token_details?: {
readonly text_tokens?: number;
readonly audio_tokens?: number;
readonly cached_tokens?: number;
readonly cached_text_tokens?: number;
readonly cached_audio_tokens?: number;
readonly cached_tokens_details?: {
readonly text_tokens?: number;
readonly audio_tokens?: number;
};
};
readonly output_token_details?: { readonly text_tokens?: number };
};
};
};
function token(value: unknown): number {
return typeof value === "number" && Number.isFinite(value)
? Math.max(0, Math.floor(value))
: 0;
}
export function usageFromResponse(event: RealtimeServerEvent): AiUsage {
const usage = event.response?.usage;
const input = usage?.input_token_details;
const output = usage?.output_token_details;
const totalText = token(input?.text_tokens ?? usage?.input_tokens);
const totalAudio = token(input?.audio_tokens);
const cachedTotal = token(input?.cached_tokens);
let cachedText = token(
input?.cached_tokens_details?.text_tokens ?? input?.cached_text_tokens,
);
let cachedAudio = token(
input?.cached_tokens_details?.audio_tokens ?? input?.cached_audio_tokens,
);
let unclassifiedCached = Math.max(
0,
cachedTotal - cachedText - cachedAudio,
);
const extraText = Math.min(
unclassifiedCached,
Math.max(0, totalText - cachedText),
);
cachedText += extraText;
unclassifiedCached -= extraText;
cachedAudio += Math.min(
unclassifiedCached,
Math.max(0, totalAudio - cachedAudio),
);
return {
...EMPTY_AI_USAGE,
textInputTokens: Math.max(0, totalText - cachedText),
cachedTextInputTokens: cachedText,
audioInputTokens: Math.max(0, totalAudio - cachedAudio),
cachedAudioInputTokens: cachedAudio,
textOutputTokens: token(output?.text_tokens ?? usage?.output_tokens),
};
}
export function usageFromTranscription(event: RealtimeServerEvent): AiUsage {
const usage = event.usage;
return {
...EMPTY_AI_USAGE,
transcriptionAudioInputTokens: token(
usage?.input_token_details?.audio_tokens ?? usage?.input_tokens,
),
transcriptionTextOutputTokens: token(
usage?.output_token_details?.text_tokens ?? usage?.output_tokens,
),
};
}