Source file generation.ml
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
open! Core
open! Async
open Jsonaf.Export
let endpoint_url = Uri.of_string "https://openrouter.ai/api/v1/generation"
module Provider_response = struct
type t =
{ endpoint_id : string option [@default None]
; id : string option [@default None]
; is_byok : bool option [@default None]
; latency : int option [@default None]
; model_permaslug : string option [@default None]
; provider_name : string option [@default None]
; status : int option [@default None]
}
[@@deriving of_jsonaf, sexp] [@@jsonaf.allow_extra_fields]
end
module Stats = struct
type t =
{ id : string
; model : string
; provider_name : string option [@default None]
; created_at : string
; api_type : string option [@default None]
; origin : string option [@default None]
; user_agent : string option [@default None]
; http_referer : string option [@default None]
; session_id : string option [@default None]
; request_id : string option [@default None]
; upstream_id : string option [@default None]
; app_id : Jsonaf.t option [@default None]
; external_user : Jsonaf.t option [@default None]
; router : Jsonaf.t option [@default None]
; streamed : bool option [@default None]
; cancelled : bool option [@default None]
; is_byok : bool option [@default None]
; finish_reason : string option [@default None]
; native_finish_reason : string option [@default None]
; service_tier : string option [@default None]
; latency : int option [@default None]
; moderation_latency : int option [@default None]
; generation_time : int option [@default None]
; tokens_prompt : int option [@default None]
; tokens_completion : int option [@default None]
; native_tokens_prompt : int option [@default None]
; native_tokens_completion : int option [@default None]
; native_tokens_completion_images : int option [@default None]
; native_tokens_reasoning : int option [@default None]
; native_tokens_cached : int option [@default None]
; num_media_prompt : int option [@default None]
; num_input_audio_prompt : int option [@default None]
; num_media_completion : int option [@default None]
; num_search_results : int option [@default None]
; num_fetches : int option [@default None]
; web_search_engine : string option [@default None]
; usage : float option [@default None]
; total_cost : float option [@default None]
; upstream_inference_cost : float option [@default None]
; cache_discount : float option [@default None]
; response_cache_source_id : string option [@default None]
; provider_responses : Provider_response.t list [@default []]
}
[@@deriving fields ~iterators:to_list, of_jsonaf, sexp] [@@jsonaf.allow_extra_fields]
end
module Response = struct
type t = { data : Stats.t } [@@deriving of_jsonaf, sexp]
end
let get ~api_key ?app_info ?on_response_body ~id () =
let = Http.make_headers ~api_key ?app_info () in
let uri = Uri.add_query_param' endpoint_url ("id", id) in
let%bind response, body = Cohttp_async.Client.get ~headers uri in
let%bind body_string = Cohttp_async.Body.to_string body in
let%map () =
match on_response_body with
| None -> return ()
| Some f -> f body_string
in
let status = Cohttp.Response.status response in
match Http.is_success_status response, status with
| false, `Not_found -> Ok None
| false, _ ->
let error_message =
match Jsonaf.parse body_string with
| Ok json -> Api_error.of_json_or_body ~body_string json
| Error _ -> body_string
in
Or_error.error_s
[%message
"OpenRouter API error" (status : Cohttp.Code.status_code) (error_message : string)]
| true, _ ->
let%bind.Or_error json =
Jsonaf.parse body_string
|> Or_error.tag_s_lazy
~tag:
(lazy
[%message
"Failed to parse generation response into JSON"
(response : Cohttp.Response.t)
(body_string : string)])
in
Or_error.try_with (fun () -> Some ([%of_jsonaf: Response.t] json).data)
|> Or_error.tag_s_lazy
~tag:
(lazy
[%message
"Failed to parse JSON into Generation.Stats.t"
(response : Cohttp.Response.t)
(json : Jsonaf.t)])
;;
module For_testing = struct
let stats_of_jsonaf json = ([%of_jsonaf: Response.t] json).data
end