My agentic slop goes here. Not intended for anyone else!

claudeio

+1
claudeio/.gitignore
···
+
_build
+1
claudeio/CLAUDE.md
···
+
I want a high quality OCaml Eio library that wraps `claude` Claude Code CLI invocations in an Eio style library. It should use the JSON input and output streaming modes of the CLI, and expose these as JSON messages using ezjsonm and good Eio abstractions (like using Buf_read and Seq)
+27
claudeio/claude.opam
···
+
# This file is generated by dune, edit dune-project instead
+
opam-version: "2.0"
+
synopsis: "OCaml client library for Claude CLI"
+
description:
+
"An Eio-based OCaml library for interacting with the Claude CLI using JSON streaming"
+
depends: [
+
"ocaml"
+
"dune" {>= "3.0"}
+
"eio"
+
"ezjsonm"
+
"alcotest" {with-test}
+
"odoc" {with-doc}
+
]
+
build: [
+
["dune" "subst"] {dev}
+
[
+
"dune"
+
"build"
+
"-p"
+
name
+
"-j"
+
jobs
+
"@install"
+
"@runtest" {with-test}
+
"@doc" {with-doc}
+
]
+
]
+14
claudeio/dune-project
···
+
(lang dune 3.0)
+
(name claude)
+
(generate_opam_files true)
+
+
(package
+
(name claude)
+
(synopsis "OCaml client library for Claude CLI")
+
(description "An Eio-based OCaml library for interacting with the Claude CLI using JSON streaming")
+
(depends
+
ocaml
+
dune
+
eio
+
ezjsonm
+
(alcotest :with-test)))
+6
claudeio/lib/claude.ml
···
+
module Types = Types
+
module Message = Message
+
module Permissions = Permissions
+
module Options = Options
+
module Transport = Transport
+
module Client = Client
+6
claudeio/lib/claude.mli
···
+
module Types = Types
+
module Message = Message
+
module Permissions = Permissions
+
module Options = Options
+
module Transport = Transport
+
module Client = Client
+153
claudeio/lib/client.ml
···
+
open Eio.Std
+
+
type t = {
+
transport : Transport.t;
+
receive_stream : Types.message Eio.Stream.t;
+
receive_fiber : unit Eio.Promise.or_exn;
+
control_stream : Types.control_message Eio.Stream.t;
+
permission_callback : Permissions.callback option;
+
permission_log : Permissions.rule list ref option;
+
mutable session_id : string option;
+
}
+
+
let handle_control_request t control_msg =
+
let open Ezjsonm in
+
match find control_msg.Types.data ["request"; "subtype"] |> get_string with
+
| "can_use_tool" ->
+
let tool_name = find control_msg.data ["request"; "tool_name"] |> get_string in
+
let input = find control_msg.data ["request"; "input"] in
+
let suggestions =
+
try
+
let sugg_json = find control_msg.data ["request"; "permission_suggestions"] in
+
match sugg_json with
+
| `A _ ->
+
(* TODO: Parse permission suggestions *)
+
[]
+
| _ -> []
+
with Not_found -> []
+
in
+
let context = Permissions.{ suggestions } in
+
+
let result = match t.permission_callback with
+
| Some callback -> callback ~tool_name ~input ~context
+
| None -> Permissions.default_allow_callback ~tool_name ~input ~context
+
in
+
+
let response = dict [
+
"type", string "control_response";
+
"response", dict [
+
"subtype", string "success";
+
"request_id", string control_msg.request_id;
+
"response", Permissions.serialize_result result
+
]
+
] in
+
Transport.send t.transport response
+
+
| subtype ->
+
(* Respond with error for unknown control requests *)
+
let response = dict [
+
"type", string "control_response";
+
"response", dict [
+
"subtype", string "error";
+
"request_id", string control_msg.request_id;
+
"error", string (Printf.sprintf "Unsupported control request: %s" subtype)
+
]
+
] in
+
Transport.send t.transport response
+
+
let receiver_loop t =
+
let rec loop () =
+
match Transport.receive_line t.transport with
+
| None -> () (* EOF *)
+
| Some line ->
+
try
+
let json = Ezjsonm.value_from_string line in
+
+
(* Check if it's a control request *)
+
match Ezjsonm.find json ["type"] |> Ezjsonm.get_string with
+
| "control_request" ->
+
let control_msg = Types.{
+
request_id = Ezjsonm.find json ["request_id"] |> Ezjsonm.get_string;
+
subtype = Ezjsonm.find json ["request"; "subtype"] |> Ezjsonm.get_string;
+
data = json;
+
} in
+
Eio.Stream.add t.control_stream control_msg;
+
handle_control_request t control_msg
+
+
| _ ->
+
(* Regular message *)
+
let msg = Message.parse json in
+
+
(* Extract session ID from system messages *)
+
(match msg with
+
| Types.System { subtype = "init"; data } ->
+
(try
+
t.session_id <- Some (Ezjsonm.find data ["session_id"] |> Ezjsonm.get_string)
+
with Not_found -> ())
+
| _ -> ());
+
+
Eio.Stream.add t.receive_stream msg
+
with
+
| exn ->
+
traceln "Failed to parse message: %s\nLine: %s"
+
(Printexc.to_string exn) line;
+
loop ()
+
in
+
try
+
loop ()
+
with
+
| exn ->
+
traceln "Receiver loop error: %s" (Printexc.to_string exn);
+
raise exn
+
+
let create ?(options = Options.default) ~sw ~process_mgr ~fs () =
+
let transport = Transport.create ~sw ~process_mgr ~fs ~options () in
+
let receive_stream = Eio.Stream.create 100 in
+
let control_stream = Eio.Stream.create 10 in
+
+
let t = {
+
transport;
+
receive_stream;
+
receive_fiber = Eio.Promise.create_resolved (Ok ()); (* Placeholder *)
+
control_stream;
+
permission_callback = options.permission_callback;
+
permission_log = None;
+
session_id = None;
+
} in
+
+
(* Start receiver fiber *)
+
let receive_fiber = Fiber.fork_promise ~sw (fun () -> receiver_loop t) in
+
+
{ t with receive_fiber }
+
+
let query t prompt =
+
let msg = Message.serialize_user_message (`String prompt) in
+
Transport.send t.transport msg
+
+
let send_message t json =
+
Transport.send t.transport json
+
+
let receive t =
+
t.receive_stream
+
+
let receive_all t =
+
let rec collect acc =
+
match Eio.Stream.take t.receive_stream with
+
| Types.Result _ as msg -> List.rev (msg :: acc)
+
| msg -> collect (msg :: acc)
+
in
+
collect []
+
+
let interrupt t =
+
Transport.interrupt t.transport
+
+
let discover_permissions t =
+
let log = ref [] in
+
let callback = Permissions.discovery_callback log in
+
{ t with
+
permission_callback = Some callback;
+
permission_log = Some log
+
}
+
+
let with_permission_callback t callback =
+
{ t with permission_callback = Some callback }
+19
claudeio/lib/client.mli
···
+
type t
+
+
val create :
+
?options:Options.t ->
+
sw:Eio.Switch.t ->
+
process_mgr:_ Eio.Process.mgr ->
+
fs:Eio.Fs.dir_ty Eio.Path.t ->
+
unit -> t
+
+
val query : t -> string -> unit
+
val send_message : t -> Ezjsonm.value -> unit
+
+
val receive : t -> Types.message Eio.Stream.t
+
val receive_all : t -> Types.message list
+
+
val interrupt : t -> unit
+
+
val discover_permissions : t -> t
+
val with_permission_callback : t -> Permissions.callback -> t
+4
claudeio/lib/dune
···
+
(library
+
(public_name claude)
+
(name claude)
+
(libraries eio eio.unix ezjsonm))
+109
claudeio/lib/message.ml
···
+
open Types
+
+
exception Message_parse_error of string * Ezjsonm.value
+
+
let parse_content_block json =
+
let open Ezjsonm in
+
match find json ["type"] |> get_string with
+
| "text" ->
+
Text { text = find json ["text"] |> get_string }
+
| "tool_use" ->
+
ToolUse {
+
id = find json ["id"] |> get_string;
+
name = find json ["name"] |> get_string;
+
input = find json ["input"];
+
}
+
| "tool_result" ->
+
ToolResult {
+
tool_use_id = find json ["tool_use_id"] |> get_string;
+
content = (try Some (find json ["content"] |> get_string) with Not_found -> None);
+
is_error = (try Some (find json ["is_error"] |> get_bool) with Not_found -> None);
+
}
+
| "thinking" ->
+
Thinking {
+
thinking = find json ["thinking"] |> get_string;
+
signature = find json ["signature"] |> get_string;
+
}
+
| t -> raise (Message_parse_error (Printf.sprintf "Unknown content block type: %s" t, json))
+
+
let parse json =
+
let open Ezjsonm in
+
try
+
match find json ["type"] |> get_string with
+
| "user" ->
+
let message = find json ["message"] in
+
let content_json = find message ["content"] in
+
let content =
+
match content_json with
+
| `String s -> `String s
+
| `A blocks ->
+
let parsed_blocks = List.map parse_content_block blocks in
+
`Blocks parsed_blocks
+
| _ -> `String (value_to_string content_json)
+
in
+
User { content }
+
+
| "assistant" ->
+
let message = find json ["message"] in
+
let content_blocks = find message ["content"] |> get_list parse_content_block in
+
let model = find message ["model"] |> get_string in
+
Assistant { content = content_blocks; model }
+
+
| "system" ->
+
let subtype = find json ["subtype"] |> get_string in
+
System { subtype; data = json }
+
+
| "result" ->
+
Result {
+
subtype = find json ["subtype"] |> get_string;
+
duration_ms = find json ["duration_ms"] |> get_int;
+
duration_api_ms = find json ["duration_api_ms"] |> get_int;
+
is_error = find json ["is_error"] |> get_bool;
+
num_turns = find json ["num_turns"] |> get_int;
+
session_id = find json ["session_id"] |> get_string;
+
total_cost_usd = (try Some (find json ["total_cost_usd"] |> get_float) with Not_found -> None);
+
usage = (try Some (find json ["usage"]) with Not_found -> None);
+
result = (try Some (find json ["result"] |> get_string) with Not_found -> None);
+
}
+
+
| msg_type ->
+
raise (Message_parse_error (Printf.sprintf "Unknown message type: %s" msg_type, json))
+
with
+
| Message_parse_error _ as e -> raise e
+
| e ->
+
raise (Message_parse_error (Printf.sprintf "Failed to parse message: %s" (Printexc.to_string e), json))
+
+
let serialize_user_message content =
+
let open Ezjsonm in
+
let content_json =
+
match content with
+
| `String s -> string s
+
| `Blocks blocks ->
+
let serialize_block = function
+
| Text { text } ->
+
dict [ "type", string "text"; "text", string text ]
+
| ToolUse { id; name; input } ->
+
dict [ "type", string "tool_use"; "id", string id; "name", string name; "input", input ]
+
| ToolResult { tool_use_id; content; is_error } ->
+
let fields = [ "type", string "tool_result"; "tool_use_id", string tool_use_id ] in
+
let fields = match content with
+
| Some c -> ("content", string c) :: fields
+
| None -> fields
+
in
+
let fields = match is_error with
+
| Some e -> ("is_error", bool e) :: fields
+
| None -> fields
+
in
+
dict fields
+
| Thinking { thinking; signature } ->
+
dict [ "type", string "thinking"; "thinking", string thinking; "signature", string signature ]
+
in
+
list serialize_block blocks
+
in
+
dict [
+
"type", string "user";
+
"message", dict [
+
"role", string "user";
+
"content", content_json
+
]
+
]
+7
claudeio/lib/message.mli
···
+
exception Message_parse_error of string * Ezjsonm.value
+
+
val parse : Ezjsonm.value -> Types.message
+
+
val serialize_user_message :
+
[ `String of string | `Blocks of Types.content_block list ] ->
+
Ezjsonm.value
+25
claudeio/lib/options.ml
···
+
type t = {
+
allowed_tools : string list;
+
disallowed_tools : string list;
+
max_thinking_tokens : int;
+
system_prompt : string option;
+
append_system_prompt : string option;
+
permission_mode : [ `Default | `AcceptEdits | `Plan | `BypassPermissions ] option;
+
permission_callback : Permissions.callback option;
+
model : string option;
+
cwd : Eio.Fs.dir_ty Eio.Path.t option;
+
env : (string * string) list;
+
}
+
+
let default = {
+
allowed_tools = [];
+
disallowed_tools = [];
+
max_thinking_tokens = 8000;
+
system_prompt = None;
+
append_system_prompt = None;
+
permission_mode = None;
+
permission_callback = Some Permissions.default_allow_callback;
+
model = None;
+
cwd = None;
+
env = [];
+
}
+14
claudeio/lib/options.mli
···
+
type t = {
+
allowed_tools : string list;
+
disallowed_tools : string list;
+
max_thinking_tokens : int;
+
system_prompt : string option;
+
append_system_prompt : string option;
+
permission_mode : [ `Default | `AcceptEdits | `Plan | `BypassPermissions ] option;
+
permission_callback : Permissions.callback option;
+
model : string option;
+
cwd : Eio.Fs.dir_ty Eio.Path.t option;
+
env : (string * string) list;
+
}
+
+
val default : t
+59
claudeio/lib/permissions.ml
···
+
type behavior = Allow | Deny | Ask
+
+
type rule = {
+
tool_name : string;
+
rule_content : string option;
+
}
+
+
type update = {
+
behavior : behavior;
+
rules : rule list;
+
}
+
+
type context = {
+
suggestions : update list;
+
}
+
+
type result =
+
| Allow of {
+
updated_input : Ezjsonm.value option;
+
updated_permissions : update list option;
+
}
+
| Deny of {
+
message : string;
+
interrupt : bool;
+
}
+
+
type callback =
+
tool_name:string ->
+
input:Ezjsonm.value ->
+
context:context ->
+
result
+
+
let serialize_result = function
+
| Allow { updated_input; updated_permissions = _ } ->
+
let open Ezjsonm in
+
let fields = [ "behavior", string "allow" ] in
+
let fields = match updated_input with
+
| Some input -> ("updated_input", input) :: fields
+
| None -> fields
+
in
+
dict fields
+
| Deny { message; interrupt } ->
+
let open Ezjsonm in
+
dict [
+
"behavior", string "deny";
+
"message", string message;
+
"interrupt", bool interrupt
+
]
+
+
let default_allow_callback ~tool_name:_ ~input:_ ~context:_ =
+
Allow { updated_input = None; updated_permissions = None }
+
+
let discovery_callback log ~tool_name:_ ~input:_ ~context =
+
List.iter (fun update ->
+
List.iter (fun rule ->
+
log := rule :: !log
+
) update.rules
+
) context.suggestions;
+
Allow { updated_input = None; updated_permissions = None }
+37
claudeio/lib/permissions.mli
···
+
type behavior = Allow | Deny | Ask
+
+
type rule = {
+
tool_name : string;
+
rule_content : string option;
+
}
+
+
type update = {
+
behavior : behavior;
+
rules : rule list;
+
}
+
+
type context = {
+
suggestions : update list;
+
}
+
+
type result =
+
| Allow of {
+
updated_input : Ezjsonm.value option;
+
updated_permissions : update list option;
+
}
+
| Deny of {
+
message : string;
+
interrupt : bool;
+
}
+
+
type callback =
+
tool_name:string ->
+
input:Ezjsonm.value ->
+
context:context ->
+
result
+
+
val serialize_result : result -> Ezjsonm.value
+
+
val default_allow_callback : callback
+
+
val discovery_callback : rule list ref -> callback
+154
claudeio/lib/transport.ml
···
+
open Eio.Std
+
+
exception CLI_not_found of string
+
exception Process_error of string
+
exception Connection_error of string
+
+
type process = P : _ Eio.Process.t -> process
+
+
type t = {
+
process : process;
+
stdin : Eio.Flow.sink_ty r;
+
stdin_close : [`Close | `Flow] r;
+
stdout : Eio.Buf_read.t;
+
sw : Switch.t;
+
}
+
+
let find_claude_cli ~fs =
+
let ( / ) = Eio.Path.( / ) in
+
let paths = [
+
fs / "usr" / "local" / "bin" / "claude";
+
fs / "opt" / "homebrew" / "bin" / "claude";
+
] in
+
+
let rec check_paths = function
+
| [] ->
+
(* Try using 'which' *)
+
None
+
| path :: rest ->
+
if Eio.Path.is_file path then
+
Some (Eio.Path.native_exn path)
+
else
+
check_paths rest
+
in
+
+
match check_paths paths with
+
| Some path -> path
+
| None ->
+
(* Try to find using shell *)
+
"claude"
+
+
let build_command ~claude_path ~options =
+
let open Options in
+
let cmd = [claude_path; "--output-format"; "stream-json"; "--verbose"] in
+
+
let cmd = match options.system_prompt with
+
| Some prompt -> cmd @ ["--system-prompt"; prompt]
+
| None -> cmd
+
in
+
+
let cmd = match options.append_system_prompt with
+
| Some prompt -> cmd @ ["--append-system-prompt"; prompt]
+
| None -> cmd
+
in
+
+
let cmd = match options.allowed_tools with
+
| [] -> cmd
+
| tools -> cmd @ ["--allowedTools"; String.concat "," tools]
+
in
+
+
let cmd = match options.disallowed_tools with
+
| [] -> cmd
+
| tools -> cmd @ ["--disallowedTools"; String.concat "," tools]
+
in
+
+
let cmd = match options.model with
+
| Some model -> cmd @ ["--model"; model]
+
| None -> cmd
+
in
+
+
let cmd = match options.permission_mode with
+
| Some `Default -> cmd @ ["--permission-mode"; "default"]
+
| Some `AcceptEdits -> cmd @ ["--permission-mode"; "acceptEdits"]
+
| Some `Plan -> cmd @ ["--permission-mode"; "plan"]
+
| Some `BypassPermissions -> cmd @ ["--permission-mode"; "bypassPermissions"]
+
| None -> cmd
+
in
+
+
(* Use streaming input mode *)
+
cmd @ ["--input-format"; "stream-json"]
+
+
let create ~sw ~process_mgr ~fs ~options () =
+
let claude_path = find_claude_cli ~fs in
+
let cmd = build_command ~claude_path ~options in
+
+
let env =
+
Array.of_list (
+
List.map (fun (k, v) -> Printf.sprintf "%s=%s" k v) (
+
("CLAUDE_CODE_ENTRYPOINT", "sdk-ocaml") ::
+
options.Options.env
+
)
+
)
+
in
+
+
let stdin_r, stdin_w = Eio.Process.pipe ~sw process_mgr in
+
let stdout_r, stdout_w = Eio.Process.pipe ~sw process_mgr in
+
let stderr_r, stderr_w = Eio.Process.pipe ~sw process_mgr in
+
(* Close stderr pipes - we don't need them *)
+
Eio.Flow.close stderr_r;
+
Eio.Flow.close stderr_w;
+
+
let process =
+
try
+
Eio.Process.spawn ~sw process_mgr
+
~env
+
~stdin:(stdin_r :> Eio.Flow.source_ty r)
+
~stdout:(stdout_w :> Eio.Flow.sink_ty r)
+
?cwd:options.Options.cwd
+
cmd
+
with
+
| exn ->
+
raise (CLI_not_found (Printf.sprintf "Failed to spawn claude CLI: %s" (Printexc.to_string exn)))
+
in
+
+
let stdin = (stdin_w :> Eio.Flow.sink_ty r) in
+
let stdin_close = (stdin_w :> [`Close | `Flow] r) in
+
let stdout = Eio.Buf_read.of_flow ~max_size:1_000_000 (stdout_r :> Eio.Flow.source_ty r) in
+
+
{ process = P process; stdin; stdin_close; stdout; sw }
+
+
let send t json =
+
let data = Ezjsonm.value_to_string json in
+
try
+
Eio.Flow.write t.stdin [Cstruct.of_string (data ^ "\n")]
+
with
+
| exn ->
+
raise (Connection_error (Printf.sprintf "Failed to send message: %s" (Printexc.to_string exn)))
+
+
let receive_line t =
+
try
+
match Eio.Buf_read.line t.stdout with
+
| line -> Some line
+
| exception End_of_file -> None
+
with
+
| exn ->
+
raise (Connection_error (Printf.sprintf "Failed to receive message: %s" (Printexc.to_string exn)))
+
+
let interrupt t =
+
let interrupt_msg =
+
Ezjsonm.dict [
+
"type", Ezjsonm.string "control_response";
+
"response", Ezjsonm.dict [
+
"subtype", Ezjsonm.string "interrupt";
+
"request_id", Ezjsonm.string "";
+
]
+
]
+
in
+
send t interrupt_msg
+
+
let close t =
+
try
+
Eio.Flow.close t.stdin_close;
+
let (P process) = t.process in
+
Eio.Process.await_exn process
+
with _ -> ()
+17
claudeio/lib/transport.mli
···
+
exception CLI_not_found of string
+
exception Process_error of string
+
exception Connection_error of string
+
+
type t
+
+
val create :
+
sw:Eio.Switch.t ->
+
process_mgr:_ Eio.Process.mgr ->
+
fs:Eio.Fs.dir_ty Eio.Path.t ->
+
options:Options.t ->
+
unit -> t
+
+
val send : t -> Ezjsonm.value -> unit
+
val receive_line : t -> string option
+
val interrupt : t -> unit
+
val close : t -> unit
+62
claudeio/lib/types.ml
···
+
type text_block = { text : string }
+
+
type tool_use_block = {
+
id : string;
+
name : string;
+
input : Ezjsonm.value
+
}
+
+
type tool_result_block = {
+
tool_use_id : string;
+
content : string option;
+
is_error : bool option
+
}
+
+
type thinking_block = {
+
thinking : string;
+
signature : string;
+
}
+
+
type content_block =
+
| Text of text_block
+
| ToolUse of tool_use_block
+
| ToolResult of tool_result_block
+
| Thinking of thinking_block
+
+
type user_message = {
+
content : [ `String of string | `Blocks of content_block list ]
+
}
+
+
type assistant_message = {
+
content : content_block list;
+
model : string
+
}
+
+
type system_message = {
+
subtype : string;
+
data : Ezjsonm.value
+
}
+
+
type result_message = {
+
subtype : string;
+
duration_ms : int;
+
duration_api_ms : int;
+
is_error : bool;
+
num_turns : int;
+
session_id : string;
+
total_cost_usd : float option;
+
usage : Ezjsonm.value option;
+
result : string option;
+
}
+
+
type message =
+
| User of user_message
+
| Assistant of assistant_message
+
| System of system_message
+
| Result of result_message
+
+
type control_message = {
+
request_id : string;
+
subtype : string;
+
data : Ezjsonm.value;
+
}
+62
claudeio/lib/types.mli
···
+
type text_block = { text : string }
+
+
type tool_use_block = {
+
id : string;
+
name : string;
+
input : Ezjsonm.value
+
}
+
+
type tool_result_block = {
+
tool_use_id : string;
+
content : string option;
+
is_error : bool option
+
}
+
+
type thinking_block = {
+
thinking : string;
+
signature : string;
+
}
+
+
type content_block =
+
| Text of text_block
+
| ToolUse of tool_use_block
+
| ToolResult of tool_result_block
+
| Thinking of thinking_block
+
+
type user_message = {
+
content : [ `String of string | `Blocks of content_block list ]
+
}
+
+
type assistant_message = {
+
content : content_block list;
+
model : string
+
}
+
+
type system_message = {
+
subtype : string;
+
data : Ezjsonm.value
+
}
+
+
type result_message = {
+
subtype : string;
+
duration_ms : int;
+
duration_api_ms : int;
+
is_error : bool;
+
num_turns : int;
+
session_id : string;
+
total_cost_usd : float option;
+
usage : Ezjsonm.value option;
+
result : string option;
+
}
+
+
type message =
+
| User of user_message
+
| Assistant of assistant_message
+
| System of system_message
+
| Result of result_message
+
+
type control_message = {
+
request_id : string;
+
subtype : string;
+
data : Ezjsonm.value;
+
}
+65
claudeio/test/camel_jokes.ml
···
+
open Eio.Std
+
+
let process_claude_response client name =
+
traceln "\n=== %s's Response ===" name;
+
let messages = Claude.Client.receive_all client in
+
List.iter (fun msg ->
+
match msg with
+
| Claude.Types.Assistant msg ->
+
List.iter (function
+
| Claude.Types.Text { text } ->
+
traceln "%s: %s" name text
+
| _ -> ()
+
) msg.content
+
| Claude.Types.Result msg ->
+
if msg.is_error then
+
traceln "Error from %s!" name
+
else
+
(match msg.total_cost_usd with
+
| Some cost -> traceln "%s's joke cost: $%.6f" name cost
+
| None -> ())
+
| _ -> ()
+
) messages
+
+
let run_claude ~sw ~env name prompt =
+
traceln "Starting %s..." name;
+
let options = Claude.Options.{
+
default with
+
model = Some "claude-3-5-sonnet-20241022";
+
(* Allow Claude to be creative *)
+
allowed_tools = [];
+
} in
+
+
let client = Claude.Client.create ~options ~sw
+
~process_mgr:env#process_mgr
+
~fs:env#fs
+
() in
+
+
Claude.Client.query client prompt;
+
process_claude_response client name
+
+
let main ~env =
+
Switch.run @@ fun sw ->
+
+
traceln "๐Ÿช Starting the Great Camel Joke Competition! ๐Ÿช";
+
traceln "================================================\n";
+
+
let prompts = [
+
"Claude 1", "Tell me a short, funny joke about camels! Make it original and clever.";
+
"Claude 2", "Give me your best camel joke - something witty and unexpected!";
+
"Claude 3", "Share a hilarious camel joke that will make everyone laugh!";
+
] in
+
+
(* Run all three Claudes concurrently *)
+
Fiber.all (
+
List.map (fun (name, prompt) ->
+
fun () -> run_claude ~sw ~env name prompt
+
) prompts
+
);
+
+
traceln "\n================================================";
+
traceln "๐ŸŽ‰ The Camel Joke Competition is complete! ๐ŸŽ‰"
+
+
let () =
+
Eio_main.run @@ fun env ->
+
main ~env
+4
claudeio/test/dune
···
+
(executable
+
(public_name camel_jokes)
+
(name camel_jokes)
+
(libraries claude eio_main))