Source file openai_stream.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
type tool_call_delta = {
  tool_index : int;
  tool_id : string option;
  tool_name : string option;
  tool_arguments_delta : string option;
}

type delta = {
  delta_content : string option;
  delta_reasoning_content : string option;
  delta_tool_calls : tool_call_delta list;
  delta_finish_reason : string option;
  delta_usage : Chatoyant_runtime.Json.t option;
}

type accumulated = {
  accumulated_content : string;
  accumulated_reasoning_content : string;
  accumulated_finish_reason : string option;
  accumulated_usage : Chatoyant_runtime.Json.t option;
}

let empty =
  {
    accumulated_content = "";
    accumulated_reasoning_content = "";
    accumulated_finish_reason = None;
    accumulated_usage = None;
  }

let field = Chatoyant_runtime.Json.field

let first_choice json =
  match field "choices" json with
  | Some (Chatoyant_runtime.Json.Array (choice :: _)) -> Some choice
  | _ -> None

let string_field name json =
  Option.bind (field name json) Chatoyant_runtime.Json.as_string

let int_field name json =
  Option.bind (field name json) Chatoyant_runtime.Json.as_int

let function_name_and_arguments json =
  match field "function" json with
  | Some fn -> (string_field "name" fn, string_field "arguments" fn)
  | None -> (None, None)

let tool_call_delta_of_json json =
  let name, arguments_delta = function_name_and_arguments json in
  {
    tool_index = Option.value (int_field "index" json) ~default:0;
    tool_id = string_field "id" json;
    tool_name = name;
    tool_arguments_delta = arguments_delta;
  }

let tool_calls_of_delta delta_json =
  match field "tool_calls" delta_json with
  | Some (Chatoyant_runtime.Json.Array calls) ->
      List.map tool_call_delta_of_json calls
  | _ -> []

let delta_of_json json =
  let usage = field "usage" json in
  match first_choice json with
  | None ->
      {
        delta_content = None;
        delta_reasoning_content = None;
        delta_tool_calls = [];
        delta_finish_reason = None;
        delta_usage = usage;
      }
  | Some choice ->
      let finish_reason = string_field "finish_reason" choice in
      let delta_json =
        Option.value (field "delta" choice) ~default:Chatoyant_runtime.Json.Null
      in
      {
        delta_content = string_field "content" delta_json;
        delta_reasoning_content = string_field "reasoning_content" delta_json;
        delta_tool_calls = tool_calls_of_delta delta_json;
        delta_finish_reason = finish_reason;
        delta_usage = usage;
      }

let apply_delta acc delta =
  {
    accumulated_content =
      acc.accumulated_content ^ Option.value delta.delta_content ~default:"";
    accumulated_reasoning_content =
      acc.accumulated_reasoning_content
      ^ Option.value delta.delta_reasoning_content ~default:"";
    accumulated_finish_reason =
      (match delta.delta_finish_reason with
      | Some _ -> delta.delta_finish_reason
      | None -> acc.accumulated_finish_reason);
    accumulated_usage =
      (match delta.delta_usage with
      | Some _ -> delta.delta_usage
      | None -> acc.accumulated_usage);
  }

let apply_chunk_json acc json = apply_delta acc (delta_of_json json)