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
type timing = { latency_ms : int; time_to_first_token_ms : int option }
type token_speed = {
output_tokens_per_second : float option;
total_tokens_per_second : float option;
measured_output_tokens : int;
measured_total_tokens : int;
}
type cost = { estimated_usd : float; actual_usd : float option }
type generation = {
content : string;
reasoning_content : string;
usage : Chatoyant_tokens.Cost.usage;
usage_source : Chatoyant_tokens.Cost.source;
timing : timing;
token_speed : token_speed;
cost : cost;
provider : Chatoyant_provider.Provider.id;
model : string;
tool_calls : Chatoyant_provider.Provider.tool_call list;
finish_reason : string option;
cached : bool;
iterations : int;
}
let empty_timing = { latency_ms = 0; time_to_first_token_ms = None }
let empty_token_speed =
{
output_tokens_per_second = None;
total_tokens_per_second = None;
measured_output_tokens = 0;
measured_total_tokens = 0;
}
let empty_cost = { estimated_usd = 0.0; actual_usd = None }
let rate ~latency_ms tokens =
if latency_ms <= 0 || tokens <= 0 then None
else Some (Float.of_int tokens /. (Float.of_int latency_ms /. 1000.0))
let token_speed ~latency_ms usage =
let usage = Chatoyant_tokens.Cost.normalize_total usage in
{
output_tokens_per_second = rate ~latency_ms usage.output_tokens;
total_tokens_per_second = rate ~latency_ms usage.total_tokens;
measured_output_tokens = usage.output_tokens;
measured_total_tokens = usage.total_tokens;
}
let string value = Chatoyant_runtime.Json.String value
let int value = Chatoyant_runtime.Json.Float (Float.of_int value)
let float value = Chatoyant_runtime.Json.Float value
let add_opt name value fields =
match value with None -> fields | Some value -> (name, value) :: fields
let timing_to_json timing =
[ ("latency_ms", int timing.latency_ms) ]
|> add_opt "time_to_first_token_ms"
(Option.map int timing.time_to_first_token_ms)
|> List.rev
|> fun fields -> Chatoyant_runtime.Json.Object fields
let token_speed_to_json speed =
[
("measured_output_tokens", int speed.measured_output_tokens);
("measured_total_tokens", int speed.measured_total_tokens);
]
|> add_opt "output_tokens_per_second"
(Option.map float speed.output_tokens_per_second)
|> add_opt "total_tokens_per_second"
(Option.map float speed.total_tokens_per_second)
|> List.rev
|> fun fields -> Chatoyant_runtime.Json.Object fields
let cost_to_json cost =
[ ("estimated_usd", float cost.estimated_usd) ]
|> add_opt "actual_usd" (Option.map float cost.actual_usd)
|> List.rev
|> fun fields -> Chatoyant_runtime.Json.Object fields
let generation_to_json generation =
[
("content", string generation.content);
("reasoning_content", string generation.reasoning_content);
("usage", Chatoyant_tokens.Cost.usage_to_json generation.usage);
( "usage_source",
string (Chatoyant_tokens.Cost.source_to_string generation.usage_source) );
("timing", timing_to_json generation.timing);
("token_speed", token_speed_to_json generation.token_speed);
("cost", cost_to_json generation.cost);
( "provider",
string (Chatoyant_provider.Provider.string_of_id generation.provider) );
("model", string generation.model);
( "tool_calls",
Chatoyant_runtime.Json.Array
(List.map Chatoyant_provider.Provider.tool_call_to_json
generation.tool_calls) );
("cached", Chatoyant_runtime.Json.Bool generation.cached);
("iterations", int generation.iterations);
]
|> add_opt "finish_reason" (Option.map string generation.finish_reason)
|> List.rev
|> fun fields -> Chatoyant_runtime.Json.Object fields