OCaml Claude SDK using Eio and Jsont
1(*---------------------------------------------------------------------------
2 Copyright (c) 2025 Anil Madhavapeddy <anil@recoil.org>. All rights reserved.
3 SPDX-License-Identifier: ISC
4 ---------------------------------------------------------------------------*)
5
6(** Object-oriented response handler with sensible defaults.
7
8 This module provides an object-oriented interface for handling response
9 events from Claude. It offers both a concrete default implementation (where
10 all methods do nothing) and an abstract base class (where all methods must
11 be implemented).
12
13 {1 Usage}
14
15 The simplest approach is to inherit from {!default} and override only the
16 methods you care about:
17
18 {[
19 let my_handler =
20 object
21 inherit Claude.Handler.default
22 method! on_text t = print_endline (Response.Text.content t)
23
24 method! on_complete c =
25 Printf.printf "Done! Cost: $%.4f\n"
26 (Option.value ~default:0.0 (Response.Complete.total_cost_usd c))
27 end
28 ]}
29
30 For compile-time guarantees that all events are handled, inherit from
31 {!abstract}:
32
33 {[
34 let complete_handler = object
35 inherit Claude.Handler.abstract
36 method on_text t = (* must implement *)
37 method on_tool_use t = (* must implement *)
38 method on_tool_result t = (* must implement *)
39 method on_thinking t = (* must implement *)
40 method on_init t = (* must implement *)
41 method on_error t = (* must implement *)
42 method on_complete t = (* must implement *)
43 end
44 ]} *)
45
46(** {1 Handler Interface} *)
47
48(** The handler interface for processing response events.
49
50 Each method corresponds to a variant of {!Response.t}. Handlers can be
51 passed to {!Client.run} to process responses in an event-driven style. *)
52class type handler = object
53 method on_text : Response.Text.t -> unit
54 (** [on_text t] is called when text content is received from the assistant. *)
55
56 method on_tool_use : Response.Tool_use.t -> unit
57 (** [on_tool_use t] is called when the assistant requests a tool invocation.
58 The caller is responsible for responding with {!Client.respond_to_tool}.
59 *)
60
61 method on_tool_result : Content_block.Tool_result.t -> unit
62 (** [on_tool_result t] is called when a tool result is observed in the message
63 stream. This is typically an echo of what was sent to Claude. *)
64
65 method on_thinking : Response.Thinking.t -> unit
66 (** [on_thinking t] is called when internal reasoning content is received. *)
67
68 method on_init : Response.Init.t -> unit
69 (** [on_init t] is called when the session is initialized. This provides
70 session metadata like session_id and model. *)
71
72 method on_error : Response.Error.t -> unit
73 (** [on_error t] is called when an error occurs. Errors can come from the
74 system (e.g., CLI errors) or from the assistant (e.g., rate limits). *)
75
76 method on_complete : Response.Complete.t -> unit
77 (** [on_complete t] is called when the conversation completes. This provides
78 final metrics like duration, cost, and token usage. *)
79end
80
81(** {1 Concrete Implementations} *)
82
83class default : handler
84(** Default handler that does nothing for all events.
85
86 This is the recommended base class for most use cases. Override only the
87 methods you need:
88
89 {[
90 let handler =
91 object
92 inherit Claude.Handler.default
93
94 method! on_text t =
95 Printf.printf "Text: %s\n" (Response.Text.content t)
96 end
97 ]}
98
99 Methods you don't override will simply be ignored, making this ideal for
100 prototyping and for cases where you only care about specific events. *)
101
102(** Abstract handler requiring all methods to be implemented.
103
104 Use this when you want compile-time guarantees that all events are handled:
105
106 {[
107 let handler = object
108 inherit Claude.Handler.abstract
109 method on_text t = (* required *)
110 method on_tool_use t = (* required *)
111 method on_tool_result t = (* required *)
112 method on_thinking t = (* required *)
113 method on_init t = (* required *)
114 method on_error t = (* required *)
115 method on_complete t = (* required *)
116 end
117 ]}
118
119 The compiler will enforce that you implement all methods, ensuring no events
120 are silently ignored. *)
121class virtual abstract : object
122 method virtual on_text : Response.Text.t -> unit
123 (** [on_text t] must be implemented by subclasses. *)
124
125 method virtual on_tool_use : Response.Tool_use.t -> unit
126 (** [on_tool_use t] must be implemented by subclasses. *)
127
128 method virtual on_tool_result : Content_block.Tool_result.t -> unit
129 (** [on_tool_result t] must be implemented by subclasses. *)
130
131 method virtual on_thinking : Response.Thinking.t -> unit
132 (** [on_thinking t] must be implemented by subclasses. *)
133
134 method virtual on_init : Response.Init.t -> unit
135 (** [on_init t] must be implemented by subclasses. *)
136
137 method virtual on_error : Response.Error.t -> unit
138 (** [on_error t] must be implemented by subclasses. *)
139
140 method virtual on_complete : Response.Complete.t -> unit
141 (** [on_complete t] must be implemented by subclasses. *)
142end
143
144(** {1 Dispatch Functions} *)
145
146val dispatch : #handler -> Response.t -> unit
147(** [dispatch handler response] dispatches a response event to the appropriate
148 handler method based on the response type.
149
150 Example:
151 {[
152 let handler =
153 object
154 inherit Claude.Handler.default
155 method! on_text t = print_endline (Response.Text.content t)
156 end
157 in
158 dispatch handler (Response.Text text_event)
159 ]} *)
160
161val dispatch_all : #handler -> Response.t list -> unit
162(** [dispatch_all handler responses] dispatches all response events to the
163 handler.
164
165 This is equivalent to calling [List.iter (dispatch handler) responses] but
166 may be more convenient:
167
168 {[
169 let responses = Client.receive_all client in
170 dispatch_all handler responses
171 ]} *)