···
(* Helper for extracting string value from JSON *)
···
(match List.assoc_opt name fields with
| Some (`Int value) -> value
18
+
| Some (`String value) -> int_of_string value
| _ -> raise (Failure (Printf.sprintf "Missing or invalid parameter: %s" name)))
| _ -> raise (Failure "Expected JSON object")
···
Bytes.sub_string result 0 (loop 0 0)
50
-
(* Generate a random image as PPM format (simple RGB format) *)
50
+
(* Generate a random image as SVG format *)
let generate_random_image width height =
52
-
let header = Printf.sprintf "P6\n%d %d\n255\n" width height in
53
-
let data = Bytes.create (width * height * 3) in
54
-
let step = Random.int 20 + 10 in
52
+
(* Helper to get random color with param control *)
53
+
let random_color ?(min=0) ?(max=255) () =
54
+
let r = min + Random.int (max - min + 1) in
55
+
let g = min + Random.int (max - min + 1) in
56
+
let b = min + Random.int (max - min + 1) in
57
+
Printf.sprintf "#%02x%02x%02x" r g b
60
+
(* Create SVG header *)
61
+
let svg_buffer = Buffer.create 10240 in
62
+
Buffer.add_string svg_buffer (Printf.sprintf
63
+
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n\
64
+
<svg width=\"%d\" height=\"%d\" xmlns=\"http://www.w3.org/2000/svg\">\n\
65
+
<rect width=\"%d\" height=\"%d\" fill=\"%s\"/>\n"
66
+
width height width height (random_color ~min:200 ~max:240 ())
69
+
(* Generate different SVG shapes based on image size and randomness *)
70
+
let shape_count = (width * height) / 5000 + 5 in
72
+
(* Add random circles *)
73
+
for _ = 1 to shape_count / 2 do
74
+
let cx = Random.int width in
75
+
let cy = Random.int height in
76
+
let r = 5 + Random.int (min width height / 8) in
77
+
let color = random_color ~min:50 ~max:200 () in
78
+
let opacity = 0.3 +. (Random.float 0.7) in
79
+
Buffer.add_string svg_buffer (Printf.sprintf
80
+
"<circle cx=\"%d\" cy=\"%d\" r=\"%d\" fill=\"%s\" fill-opacity=\"%.2f\" />\n"
81
+
cx cy r color opacity
85
+
(* Add random rectangles *)
86
+
for _ = 1 to shape_count / 3 do
87
+
let x = Random.int width in
88
+
let y = Random.int height in
89
+
let w = 10 + Random.int (width / 5) in
90
+
let h = 10 + Random.int (height / 5) in
91
+
let color = random_color ~min:50 ~max:200 () in
92
+
let opacity = 0.2 +. (Random.float 0.6) in
93
+
let rx = 2 + Random.int 20 in (* Rounded corners *)
94
+
Buffer.add_string svg_buffer (Printf.sprintf
95
+
"<rect x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" rx=\"%d\" fill=\"%s\" fill-opacity=\"%.2f\" />\n"
96
+
x y w h rx color opacity
100
+
(* Add random lines *)
101
+
for _ = 1 to shape_count do
102
+
let x1 = Random.int width in
103
+
let y1 = Random.int height in
104
+
let x2 = Random.int width in
105
+
let y2 = Random.int height in
106
+
let stroke = random_color () in
107
+
let sw = 1 + Random.int 5 in
108
+
Buffer.add_string svg_buffer (Printf.sprintf
109
+
"<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" stroke=\"%s\" stroke-width=\"%d\" />\n"
110
+
x1 y1 x2 y2 stroke sw
56
-
for y = 0 to height - 1 do
57
-
for x = 0 to width - 1 do
58
-
let pattern_val = ((x / step) + (y / step)) mod 2 in
59
-
let offset = (y * width + x) * 3 in
60
-
if pattern_val = 0 then begin
61
-
(* Random bright color for checkerboard *)
62
-
let r = 150 + Random.int 100 in
63
-
let g = 150 + Random.int 100 in
64
-
let b = 150 + Random.int 100 in
65
-
Bytes.set data offset (Char.chr r);
66
-
Bytes.set data (offset + 1) (Char.chr g);
67
-
Bytes.set data (offset + 2) (Char.chr b);
70
-
let r = Random.int 100 in
71
-
let g = Random.int 100 in
72
-
let b = Random.int 100 in
73
-
Bytes.set data offset (Char.chr r);
74
-
Bytes.set data (offset + 1) (Char.chr g);
75
-
Bytes.set data (offset + 2) (Char.chr b);
114
+
(* Add some random polygons *)
115
+
for _ = 1 to shape_count / 4 do
116
+
let points = 3 + Random.int 5 in (* 3 to 7 points *)
117
+
let cx = Random.int width in
118
+
let cy = Random.int height in
119
+
let radius = 10 + Random.int (min width height / 6) in
120
+
let points_str = Buffer.create 100 in
122
+
for i = 0 to points - 1 do
123
+
let angle = 2.0 *. Float.pi *. (float_of_int i) /. (float_of_int points) in
124
+
let px = cx + int_of_float (float_of_int radius *. cos angle) in
125
+
let py = cy + int_of_float (float_of_int radius *. sin angle) in
126
+
Buffer.add_string points_str (Printf.sprintf "%d,%d " px py);
129
+
let fill = random_color ~min:100 ~max:220 () in
130
+
let opacity = 0.2 +. Random.float 0.5 in
132
+
Buffer.add_string svg_buffer (Printf.sprintf
133
+
"<polygon points=\"%s\" fill=\"%s\" fill-opacity=\"%.2f\" />\n"
134
+
(Buffer.contents points_str) fill opacity
80
-
(* Encode PPM data as Base64 *)
81
-
Base64.encode (header ^ Bytes.to_string data)
138
+
(* Close SVG tag *)
139
+
Buffer.add_string svg_buffer "</svg>";
141
+
(* Return the SVG directly, no need for Base64 since it's already text *)
142
+
Buffer.contents svg_buffer
144
+
(* Helper to write 32-bit little endian integer *)
145
+
let write_int32_le buf n =
146
+
Buffer.add_char buf (Char.chr (n land 0xff));
147
+
Buffer.add_char buf (Char.chr ((n lsr 8) land 0xff));
148
+
Buffer.add_char buf (Char.chr ((n lsr 16) land 0xff));
149
+
Buffer.add_char buf (Char.chr ((n lsr 24) land 0xff))
151
+
(* Helper to write 16-bit little endian integer *)
152
+
let write_int16_le buf n =
153
+
Buffer.add_char buf (Char.chr (n land 0xff));
154
+
Buffer.add_char buf (Char.chr ((n lsr 8) land 0xff))
(* Generate a simple WAV file with sine wave *)
let generate_sine_wave_audio frequency duration =
let sample_rate = 8000 in
let num_samples = sample_rate * duration in
88
-
let header = "RIFF" ^
89
-
String.make 4 '\000' ^ (* Size placeholder *)
91
-
String.make 4 '\016' ^ (* Subchunk1 size *)
92
-
String.make 2 '\001' ^ (* Audio format = 1 (PCM) *)
93
-
String.make 2 '\001' ^ (* Num channels = 1 (mono) *)
94
-
String.make 4 (Char.chr sample_rate) ^ (* Sample rate as 4 bytes *)
95
-
String.make 4 (Char.chr (sample_rate * 2)) ^ (* Byte rate *)
96
-
String.make 2 '\002' ^ (* Block align *)
97
-
String.make 2 '\016' ^ (* Bits per sample = 16 *)
99
-
String.make 4 (Char.chr (num_samples * 2)) in (* Data size *)
161
+
let header_buf = Buffer.create 44 in
163
+
(* Fill WAV header properly *)
164
+
Buffer.add_string header_buf "RIFF";
165
+
write_int32_le header_buf (36 + num_samples * 2); (* File size minus 8 *)
166
+
Buffer.add_string header_buf "WAVE";
169
+
Buffer.add_string header_buf "fmt ";
170
+
write_int32_le header_buf 16; (* Format chunk size *)
171
+
write_int16_le header_buf 1; (* PCM format *)
172
+
write_int16_le header_buf 1; (* Mono *)
173
+
write_int32_le header_buf sample_rate; (* Sample rate *)
174
+
write_int32_le header_buf (sample_rate * 2); (* Byte rate *)
175
+
write_int16_le header_buf 2; (* Block align *)
176
+
write_int16_le header_buf 16; (* Bits per sample *)
179
+
Buffer.add_string header_buf "data";
180
+
write_int32_le header_buf (num_samples * 2); (* Data size *)
(* Generate sine wave samples *)
102
-
let samples = Bytes.create (num_samples * 2) in
183
+
let samples_buf = Buffer.create (num_samples * 2) in
let amplitude = 16384.0 in (* 16-bit with headroom *)
for i = 0 to num_samples - 1 do
···
(* Convert to 16-bit little-endian *)
let sample = if sample < 0 then sample + 65536 else sample in
112
-
Bytes.set samples (i * 2) (Char.chr (sample land 0xff));
113
-
Bytes.set samples (i * 2 + 1) (Char.chr ((sample lsr 8) land 0xff));
193
+
write_int16_le samples_buf sample;
116
-
(* Encode WAV data as Base64 *)
117
-
Base64.encode (header ^ Bytes.to_string samples)
196
+
(* Combine header and samples, then encode as Base64 *)
197
+
let wav_data = Buffer.contents header_buf ^ Buffer.contents samples_buf in
198
+
Base64.encode wav_data
let server = create_server
···
(* Create a multimodal tool result *)
156
-
~image:(Some (image_data, "image/ppm"))
237
+
~image:(Some (image_data, "image/svg+xml"))
~audio:(Some (audio_data, "audio/wav"))
···
let image_data = generate_random_image width height in
187
-
[ImageContent { data = image_data; mime_type = "image/ppm" }]
268
+
[ImageContent { data = image_data; mime_type = "image/svg+xml" }]
···
245
-
"mimeType": "image/ppm"
326
+
"mimeType": "image/svg+xml"
···
Random.self_init(); (* Initialize random generator *)
Eio_main.run @@ fun env ->
260
-
Mcp_server.run_server env server
341
+
Mcp_server.run_server env server