OCaml Claude SDK using Eio and Jsont
at main 206 lines 6.5 kB view raw
1(*--------------------------------------------------------------------------- 2 Copyright (c) 2025 Anil Madhavapeddy <anil@recoil.org>. All rights reserved. 3 SPDX-License-Identifier: ISC 4 ---------------------------------------------------------------------------*) 5 6let src = Logs.Src.create "claude.message" ~doc:"Claude messages" 7 8module Log = (val Logs.src_log src : Logs.LOG) 9 10module User = struct 11 type t = Proto.Message.User.t 12 13 let of_string s = Proto.Message.User.create_string s 14 15 let of_blocks blocks = 16 Proto.Message.User.create_blocks (List.map Content_block.to_proto blocks) 17 18 let with_tool_result ~tool_use_id ~content ?is_error () = 19 Proto.Message.User.create_with_tool_result ~tool_use_id ~content ?is_error 20 () 21 22 let as_text t = 23 match Proto.Message.User.content t with 24 | Proto.Message.User.String s -> Some s 25 | Proto.Message.User.Blocks _ -> None 26 27 let blocks t = 28 match Proto.Message.User.content t with 29 | Proto.Message.User.String s -> [ Content_block.text s ] 30 | Proto.Message.User.Blocks bs -> List.map Content_block.of_proto bs 31 32 let of_proto proto = proto 33 let to_proto t = t 34 35 (* Internal wire format functions *) 36 let incoming_jsont = Proto.Message.User.incoming_jsont 37 38 let to_json t = 39 match Jsont.Json.encode Proto.Message.User.jsont t with 40 | Ok json -> json 41 | Error e -> invalid_arg ("User.to_json: " ^ e) 42end 43 44module Assistant = struct 45 type error = Proto.Message.Assistant.error 46 type t = Proto.Message.Assistant.t 47 48 let content t = 49 List.map Content_block.of_proto (Proto.Message.Assistant.content t) 50 51 let model t = Proto.Message.Assistant.model t 52 let error t = Proto.Message.Assistant.error t 53 54 let text_blocks t = 55 List.filter_map 56 (function 57 | Content_block.Text text -> Some (Content_block.Text.text text) 58 | _ -> None) 59 (content t) 60 61 let tool_uses t = 62 List.filter_map 63 (function Content_block.Tool_use tool -> Some tool | _ -> None) 64 (content t) 65 66 let thinking_blocks t = 67 List.filter_map 68 (function Content_block.Thinking thinking -> Some thinking | _ -> None) 69 (content t) 70 71 let has_tool_use t = 72 List.exists 73 (function Content_block.Tool_use _ -> true | _ -> false) 74 (content t) 75 76 let combined_text t = String.concat "\n" (text_blocks t) 77 let of_proto proto = proto 78 let to_proto t = t 79 80 (* Internal wire format functions *) 81 let incoming_jsont = Proto.Message.Assistant.incoming_jsont 82 83 let to_json t = 84 match Jsont.Json.encode Proto.Message.Assistant.jsont t with 85 | Ok json -> json 86 | Error e -> invalid_arg ("Assistant.to_json: " ^ e) 87end 88 89module System = struct 90 type t = Proto.Message.System.t 91 92 let is_init = function Proto.Message.System.Init _ -> true | _ -> false 93 let is_error = function Proto.Message.System.Error _ -> true | _ -> false 94 let session_id = Proto.Message.System.session_id 95 let model = Proto.Message.System.model 96 let cwd = Proto.Message.System.cwd 97 let error_message = Proto.Message.System.error_msg 98 let of_proto proto = proto 99 let to_proto t = t 100 101 (* Internal wire format functions *) 102 let jsont = Proto.Message.System.jsont 103 104 let to_json t = 105 match Jsont.Json.encode Proto.Message.System.jsont t with 106 | Ok json -> json 107 | Error e -> invalid_arg ("System.to_json: " ^ e) 108end 109 110module Result = struct 111 module Usage = struct 112 type t = Proto.Message.Result.Usage.t 113 114 let input_tokens = Proto.Message.Result.Usage.input_tokens 115 let output_tokens = Proto.Message.Result.Usage.output_tokens 116 let total_tokens = Proto.Message.Result.Usage.total_tokens 117 118 let cache_creation_input_tokens = 119 Proto.Message.Result.Usage.cache_creation_input_tokens 120 121 let cache_read_input_tokens = 122 Proto.Message.Result.Usage.cache_read_input_tokens 123 124 let of_proto proto = proto 125 end 126 127 type t = Proto.Message.Result.t 128 129 let duration_ms = Proto.Message.Result.duration_ms 130 let duration_api_ms = Proto.Message.Result.duration_api_ms 131 let is_error = Proto.Message.Result.is_error 132 let num_turns = Proto.Message.Result.num_turns 133 let session_id = Proto.Message.Result.session_id 134 let total_cost_usd = Proto.Message.Result.total_cost_usd 135 let usage t = Option.map Usage.of_proto (Proto.Message.Result.usage t) 136 let result_text = Proto.Message.Result.result 137 let structured_output = Proto.Message.Result.structured_output 138 let of_proto proto = proto 139 let to_proto t = t 140 141 (* Internal wire format functions *) 142 let jsont = Proto.Message.Result.jsont 143 144 let to_json t = 145 match Jsont.Json.encode Proto.Message.Result.jsont t with 146 | Ok json -> json 147 | Error e -> invalid_arg ("Result.to_json: " ^ e) 148end 149 150type t = 151 | User of User.t 152 | Assistant of Assistant.t 153 | System of System.t 154 | Result of Result.t 155 156let of_proto = function 157 | Proto.Message.User u -> User (User.of_proto u) 158 | Proto.Message.Assistant a -> Assistant (Assistant.of_proto a) 159 | Proto.Message.System s -> System (System.of_proto s) 160 | Proto.Message.Result r -> Result (Result.of_proto r) 161 162let to_proto = function 163 | User u -> Proto.Message.User (User.to_proto u) 164 | Assistant a -> Proto.Message.Assistant (Assistant.to_proto a) 165 | System s -> Proto.Message.System (System.to_proto s) 166 | Result r -> Proto.Message.Result (Result.to_proto r) 167 168let is_user = function User _ -> true | _ -> false 169let is_assistant = function Assistant _ -> true | _ -> false 170let is_system = function System _ -> true | _ -> false 171let is_result = function Result _ -> true | _ -> false 172 173let is_error = function 174 | Result r -> Result.is_error r 175 | System s -> System.is_error s 176 | _ -> false 177 178let extract_text = function 179 | User u -> User.as_text u 180 | Assistant a -> 181 let text = Assistant.combined_text a in 182 if text = "" then None else Some text 183 | _ -> None 184 185let extract_tool_uses = function 186 | Assistant a -> Assistant.tool_uses a 187 | _ -> [] 188 189let get_session_id = function 190 | System s -> System.session_id s 191 | Result r -> Some (Result.session_id r) 192 | _ -> None 193 194(* Wire format conversion *) 195let to_json = function 196 | User u -> User.to_json u 197 | Assistant a -> Assistant.to_json a 198 | System s -> System.to_json s 199 | Result r -> Result.to_json r 200 201(* Convenience constructors *) 202let user_string s = User (User.of_string s) 203let user_blocks blocks = User (User.of_blocks blocks) 204let pp fmt t = Jsont.pp_value Proto.Message.jsont () fmt (to_proto t) 205let log_received t = Log.info (fun m -> m "← %a" pp t) 206let log_sending t = Log.info (fun m -> m "→ %a" pp t)