OCaml Claude SDK using Eio and Jsont
at main 352 lines 12 kB view raw
1module User = struct 2 type content = String of string | Blocks of Content_block.t list 3 type t = { content : content; unknown : Unknown.t } 4 5 let create_string s = { content = String s; unknown = Unknown.empty } 6 7 let create_blocks blocks = 8 { content = Blocks blocks; unknown = Unknown.empty } 9 10 let create_with_tool_result ~tool_use_id ~content ?is_error () = 11 let tool_result = 12 Content_block.tool_result ~tool_use_id ~content ?is_error () 13 in 14 { content = Blocks [ tool_result ]; unknown = Unknown.empty } 15 16 let make content unknown = { content; unknown } 17 let content t = t.content 18 let unknown t = t.unknown 19 20 (* Decode content from json value *) 21 let decode_content json = 22 match json with 23 | Jsont.String (s, _) -> String s 24 | Jsont.Array (items, _) -> 25 let blocks = 26 List.map 27 (fun j -> 28 match Jsont.Json.decode Content_block.jsont j with 29 | Ok v -> v 30 | Error e -> invalid_arg ("Invalid content block: " ^ e)) 31 items 32 in 33 Blocks blocks 34 | _ -> failwith "Content must be string or array" 35 36 (* Encode content to json value *) 37 let encode_content = function 38 | String s -> Jsont.String (s, Jsont.Meta.none) 39 | Blocks blocks -> 40 let jsons = 41 List.map 42 (fun b -> 43 match Jsont.Json.encode Content_block.jsont b with 44 | Ok json -> json 45 | Error e -> invalid_arg ("encode_content: " ^ e)) 46 blocks 47 in 48 Jsont.Array (jsons, Jsont.Meta.none) 49 50 let jsont : t Jsont.t = 51 Jsont.Object.map ~kind:"User" (fun json_content unknown -> 52 let content = decode_content json_content in 53 make content unknown) 54 |> Jsont.Object.mem "content" Jsont.json ~enc:(fun t -> 55 encode_content (content t)) 56 |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:unknown 57 |> Jsont.Object.finish 58 59 (* Jsont codec for parsing incoming user messages from CLI *) 60 let incoming_jsont : t Jsont.t = 61 let message_jsont = 62 Jsont.Object.map ~kind:"UserMessage" (fun json_content -> 63 let content = decode_content json_content in 64 { content; unknown = Unknown.empty }) 65 |> Jsont.Object.mem "content" Jsont.json ~enc:(fun t -> 66 encode_content (content t)) 67 |> Jsont.Object.finish 68 in 69 Jsont.Object.map ~kind:"UserEnvelope" Fun.id 70 |> Jsont.Object.mem "message" message_jsont ~enc:Fun.id 71 |> Jsont.Object.finish 72end 73 74module Assistant = struct 75 type error = 76 [ `Authentication_failed 77 | `Billing_error 78 | `Rate_limit 79 | `Invalid_request 80 | `Server_error 81 | `Unknown ] 82 83 let error_jsont : error Jsont.t = 84 Jsont.enum 85 [ 86 ("authentication_failed", `Authentication_failed); 87 ("billing_error", `Billing_error); 88 ("rate_limit", `Rate_limit); 89 ("invalid_request", `Invalid_request); 90 ("server_error", `Server_error); 91 ("unknown", `Unknown); 92 ] 93 94 type t = { 95 content : Content_block.t list; 96 model : string; 97 error : error option; 98 unknown : Unknown.t; 99 } 100 101 let create ~content ~model ?error () = 102 { content; model; error; unknown = Unknown.empty } 103 104 let make content model error unknown = { content; model; error; unknown } 105 let content t = t.content 106 let model t = t.model 107 let error t = t.error 108 let unknown t = t.unknown 109 110 let jsont : t Jsont.t = 111 Jsont.Object.map ~kind:"Assistant" make 112 |> Jsont.Object.mem "content" (Jsont.list Content_block.jsont) ~enc:content 113 |> Jsont.Object.mem "model" Jsont.string ~enc:model 114 |> Jsont.Object.opt_mem "error" error_jsont ~enc:error 115 |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:unknown 116 |> Jsont.Object.finish 117 118 (* Jsont codec for parsing incoming assistant messages from CLI *) 119 let incoming_jsont : t Jsont.t = 120 Jsont.Object.map ~kind:"AssistantEnvelope" Fun.id 121 |> Jsont.Object.mem "message" jsont ~enc:Fun.id 122 |> Jsont.Object.finish 123end 124 125module System = struct 126 (** System messages as a discriminated union on "subtype" field *) 127 128 type init = { 129 session_id : string option; 130 model : string option; 131 cwd : string option; 132 unknown : Unknown.t; 133 } 134 135 type error = { error : string; unknown : Unknown.t } 136 type t = Init of init | Error of error 137 138 (* Accessors *) 139 let session_id = function Init i -> i.session_id | _ -> None 140 let model = function Init i -> i.model | _ -> None 141 let cwd = function Init i -> i.cwd | _ -> None 142 let error_msg = function Error e -> Some e.error | _ -> None 143 let unknown = function Init i -> i.unknown | Error e -> e.unknown 144 145 (* Constructors *) 146 let init ?session_id ?model ?cwd () = 147 Init { session_id; model; cwd; unknown = Unknown.empty } 148 149 let error ~error = Error { error; unknown = Unknown.empty } 150 151 (* Individual record codecs *) 152 let init_jsont : init Jsont.t = 153 let make session_id model cwd unknown : init = 154 { session_id; model; cwd; unknown } 155 in 156 Jsont.Object.map ~kind:"SystemInit" make 157 |> Jsont.Object.opt_mem "session_id" Jsont.string ~enc:(fun (r : init) -> 158 r.session_id) 159 |> Jsont.Object.opt_mem "model" Jsont.string ~enc:(fun (r : init) -> 160 r.model) 161 |> Jsont.Object.opt_mem "cwd" Jsont.string ~enc:(fun (r : init) -> r.cwd) 162 |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun (r : init) -> 163 r.unknown) 164 |> Jsont.Object.finish 165 166 let error_jsont : error Jsont.t = 167 let make err unknown : error = { error = err; unknown } in 168 Jsont.Object.map ~kind:"SystemError" make 169 |> Jsont.Object.mem "error" Jsont.string ~enc:(fun (r : error) -> r.error) 170 |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun (r : error) -> 171 r.unknown) 172 |> Jsont.Object.finish 173 174 (* Main codec using case_mem for "subtype" discriminator *) 175 let jsont : t Jsont.t = 176 let case_init = 177 Jsont.Object.Case.map "init" init_jsont ~dec:(fun v -> Init v) 178 in 179 let case_error = 180 Jsont.Object.Case.map "error" error_jsont ~dec:(fun v -> Error v) 181 in 182 let enc_case = function 183 | Init v -> Jsont.Object.Case.value case_init v 184 | Error v -> Jsont.Object.Case.value case_error v 185 in 186 let cases = Jsont.Object.Case.[ make case_init; make case_error ] in 187 Jsont.Object.map ~kind:"System" Fun.id 188 |> Jsont.Object.case_mem "subtype" Jsont.string ~enc:Fun.id ~enc_case cases 189 ~tag_to_string:Fun.id ~tag_compare:String.compare 190 |> Jsont.Object.finish 191end 192 193module Result = struct 194 module Usage = struct 195 type t = { 196 input_tokens : int option; 197 output_tokens : int option; 198 total_tokens : int option; 199 cache_creation_input_tokens : int option; 200 cache_read_input_tokens : int option; 201 unknown : Unknown.t; 202 } 203 204 let make input_tokens output_tokens total_tokens cache_creation_input_tokens 205 cache_read_input_tokens unknown = 206 { 207 input_tokens; 208 output_tokens; 209 total_tokens; 210 cache_creation_input_tokens; 211 cache_read_input_tokens; 212 unknown; 213 } 214 215 let create ?input_tokens ?output_tokens ?total_tokens 216 ?cache_creation_input_tokens ?cache_read_input_tokens () = 217 { 218 input_tokens; 219 output_tokens; 220 total_tokens; 221 cache_creation_input_tokens; 222 cache_read_input_tokens; 223 unknown = Unknown.empty; 224 } 225 226 let input_tokens t = t.input_tokens 227 let output_tokens t = t.output_tokens 228 let total_tokens t = t.total_tokens 229 let cache_creation_input_tokens t = t.cache_creation_input_tokens 230 let cache_read_input_tokens t = t.cache_read_input_tokens 231 let unknown t = t.unknown 232 233 let jsont : t Jsont.t = 234 Jsont.Object.map ~kind:"Usage" make 235 |> Jsont.Object.opt_mem "input_tokens" Jsont.int ~enc:input_tokens 236 |> Jsont.Object.opt_mem "output_tokens" Jsont.int ~enc:output_tokens 237 |> Jsont.Object.opt_mem "total_tokens" Jsont.int ~enc:total_tokens 238 |> Jsont.Object.opt_mem "cache_creation_input_tokens" Jsont.int 239 ~enc:cache_creation_input_tokens 240 |> Jsont.Object.opt_mem "cache_read_input_tokens" Jsont.int 241 ~enc:cache_read_input_tokens 242 |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:unknown 243 |> Jsont.Object.finish 244 end 245 246 type t = { 247 subtype : string; 248 duration_ms : int; 249 duration_api_ms : int; 250 is_error : bool; 251 num_turns : int; 252 session_id : string; 253 total_cost_usd : float option; 254 usage : Usage.t option; 255 result : string option; 256 structured_output : Jsont.json option; 257 unknown : Unknown.t; 258 } 259 260 let create ~subtype ~duration_ms ~duration_api_ms ~is_error ~num_turns 261 ~session_id ?total_cost_usd ?usage ?result ?structured_output () = 262 { 263 subtype; 264 duration_ms; 265 duration_api_ms; 266 is_error; 267 num_turns; 268 session_id; 269 total_cost_usd; 270 usage; 271 result; 272 structured_output; 273 unknown = Unknown.empty; 274 } 275 276 let make subtype duration_ms duration_api_ms is_error num_turns session_id 277 total_cost_usd usage result structured_output unknown = 278 { 279 subtype; 280 duration_ms; 281 duration_api_ms; 282 is_error; 283 num_turns; 284 session_id; 285 total_cost_usd; 286 usage; 287 result; 288 structured_output; 289 unknown; 290 } 291 292 let subtype t = t.subtype 293 let duration_ms t = t.duration_ms 294 let duration_api_ms t = t.duration_api_ms 295 let is_error t = t.is_error 296 let num_turns t = t.num_turns 297 let session_id t = t.session_id 298 let total_cost_usd t = t.total_cost_usd 299 let usage t = t.usage 300 let result t = t.result 301 let structured_output t = t.structured_output 302 let unknown t = t.unknown 303 304 let jsont : t Jsont.t = 305 Jsont.Object.map ~kind:"Result" make 306 |> Jsont.Object.mem "subtype" Jsont.string ~enc:subtype 307 |> Jsont.Object.mem "duration_ms" Jsont.int ~enc:duration_ms 308 |> Jsont.Object.mem "duration_api_ms" Jsont.int ~enc:duration_api_ms 309 |> Jsont.Object.mem "is_error" Jsont.bool ~enc:is_error 310 |> Jsont.Object.mem "num_turns" Jsont.int ~enc:num_turns 311 |> Jsont.Object.mem "session_id" Jsont.string ~enc:session_id 312 |> Jsont.Object.opt_mem "total_cost_usd" Jsont.number ~enc:total_cost_usd 313 |> Jsont.Object.opt_mem "usage" Usage.jsont ~enc:usage 314 |> Jsont.Object.opt_mem "result" Jsont.string ~enc:result 315 |> Jsont.Object.opt_mem "structured_output" Jsont.json 316 ~enc:structured_output 317 |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:unknown 318 |> Jsont.Object.finish 319end 320 321type t = 322 | User of User.t 323 | Assistant of Assistant.t 324 | System of System.t 325 | Result of Result.t 326 327(* Jsont codec for the main Message variant type. 328 Uses case_mem for discriminated union based on "type" field. *) 329let jsont : t Jsont.t = 330 let case_map kind obj dec = Jsont.Object.Case.map kind obj ~dec in 331 let case_user = case_map "user" User.incoming_jsont (fun v -> User v) in 332 let case_assistant = 333 case_map "assistant" Assistant.incoming_jsont (fun v -> Assistant v) 334 in 335 let case_system = case_map "system" System.jsont (fun v -> System v) in 336 let case_result = case_map "result" Result.jsont (fun v -> Result v) in 337 let enc_case = function 338 | User v -> Jsont.Object.Case.value case_user v 339 | Assistant v -> Jsont.Object.Case.value case_assistant v 340 | System v -> Jsont.Object.Case.value case_system v 341 | Result v -> Jsont.Object.Case.value case_result v 342 in 343 let cases = 344 Jsont.Object.Case. 345 [ 346 make case_user; make case_assistant; make case_system; make case_result; 347 ] 348 in 349 Jsont.Object.map ~kind:"Message" Fun.id 350 |> Jsont.Object.case_mem "type" Jsont.string ~enc:Fun.id ~enc_case cases 351 ~tag_to_string:Fun.id ~tag_compare:String.compare 352 |> Jsont.Object.finish