cactus-react-native 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/README.md +212 -27
  2. package/android/src/main/jniLibs/arm64-v8a/libcactus.a +0 -0
  3. package/cpp/HybridCactus.cpp +119 -0
  4. package/cpp/HybridCactus.hpp +13 -0
  5. package/cpp/cactus_ffi.h +24 -0
  6. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/cactus_ffi.h +24 -0
  7. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/cactus_utils.h +41 -1
  8. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/engine.h +66 -48
  9. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/gemma_tools.h +549 -0
  10. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/graph.h +102 -21
  11. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/kernel.h +45 -195
  12. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/kernel_utils.h +399 -140
  13. package/ios/cactus.xcframework/ios-arm64/cactus.framework/cactus +0 -0
  14. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/cactus_ffi.h +24 -0
  15. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/cactus_utils.h +41 -1
  16. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/engine.h +66 -48
  17. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/gemma_tools.h +549 -0
  18. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/graph.h +102 -21
  19. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/kernel.h +45 -195
  20. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/kernel_utils.h +399 -140
  21. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/cactus +0 -0
  22. package/lib/module/api/Database.js +0 -92
  23. package/lib/module/api/Database.js.map +1 -1
  24. package/lib/module/classes/CactusLM.js +33 -15
  25. package/lib/module/classes/CactusLM.js.map +1 -1
  26. package/lib/module/classes/CactusSTT.js +90 -15
  27. package/lib/module/classes/CactusSTT.js.map +1 -1
  28. package/lib/module/hooks/useCactusLM.js +14 -5
  29. package/lib/module/hooks/useCactusLM.js.map +1 -1
  30. package/lib/module/hooks/useCactusSTT.js +100 -4
  31. package/lib/module/hooks/useCactusSTT.js.map +1 -1
  32. package/lib/module/index.js.map +1 -1
  33. package/lib/module/models.js +336 -0
  34. package/lib/module/models.js.map +1 -0
  35. package/lib/module/native/Cactus.js +37 -0
  36. package/lib/module/native/Cactus.js.map +1 -1
  37. package/lib/module/types/CactusLM.js +2 -0
  38. package/lib/module/types/CactusSTT.js +2 -0
  39. package/lib/module/types/common.js +2 -0
  40. package/lib/module/types/{CactusModel.js.map → common.js.map} +1 -1
  41. package/lib/typescript/src/api/Database.d.ts +0 -6
  42. package/lib/typescript/src/api/Database.d.ts.map +1 -1
  43. package/lib/typescript/src/classes/CactusLM.d.ts +7 -3
  44. package/lib/typescript/src/classes/CactusLM.d.ts.map +1 -1
  45. package/lib/typescript/src/classes/CactusSTT.d.ts +13 -4
  46. package/lib/typescript/src/classes/CactusSTT.d.ts.map +1 -1
  47. package/lib/typescript/src/hooks/useCactusLM.d.ts +2 -2
  48. package/lib/typescript/src/hooks/useCactusLM.d.ts.map +1 -1
  49. package/lib/typescript/src/hooks/useCactusSTT.d.ts +12 -4
  50. package/lib/typescript/src/hooks/useCactusSTT.d.ts.map +1 -1
  51. package/lib/typescript/src/index.d.ts +2 -3
  52. package/lib/typescript/src/index.d.ts.map +1 -1
  53. package/lib/typescript/src/models.d.ts +6 -0
  54. package/lib/typescript/src/models.d.ts.map +1 -0
  55. package/lib/typescript/src/native/Cactus.d.ts +6 -1
  56. package/lib/typescript/src/native/Cactus.d.ts.map +1 -1
  57. package/lib/typescript/src/specs/Cactus.nitro.d.ts +5 -0
  58. package/lib/typescript/src/specs/Cactus.nitro.d.ts.map +1 -1
  59. package/lib/typescript/src/types/CactusLM.d.ts +2 -0
  60. package/lib/typescript/src/types/CactusLM.d.ts.map +1 -1
  61. package/lib/typescript/src/types/CactusSTT.d.ts +20 -0
  62. package/lib/typescript/src/types/CactusSTT.d.ts.map +1 -1
  63. package/lib/typescript/src/types/common.d.ts +28 -0
  64. package/lib/typescript/src/types/common.d.ts.map +1 -0
  65. package/nitrogen/generated/shared/c++/HybridCactusSpec.cpp +5 -0
  66. package/nitrogen/generated/shared/c++/HybridCactusSpec.hpp +5 -0
  67. package/package.json +1 -1
  68. package/src/api/Database.ts +0 -133
  69. package/src/classes/CactusLM.ts +49 -17
  70. package/src/classes/CactusSTT.ts +118 -17
  71. package/src/hooks/useCactusLM.ts +25 -5
  72. package/src/hooks/useCactusSTT.ts +117 -5
  73. package/src/index.tsx +6 -2
  74. package/src/models.ts +344 -0
  75. package/src/native/Cactus.ts +55 -0
  76. package/src/specs/Cactus.nitro.ts +5 -0
  77. package/src/types/CactusLM.ts +3 -0
  78. package/src/types/CactusSTT.ts +26 -0
  79. package/src/types/common.ts +28 -0
  80. package/lib/module/types/CactusModel.js +0 -2
  81. package/lib/module/types/CactusSTTModel.js +0 -2
  82. package/lib/module/types/CactusSTTModel.js.map +0 -1
  83. package/lib/typescript/src/types/CactusModel.d.ts +0 -13
  84. package/lib/typescript/src/types/CactusModel.d.ts.map +0 -1
  85. package/lib/typescript/src/types/CactusSTTModel.d.ts +0 -8
  86. package/lib/typescript/src/types/CactusSTTModel.d.ts.map +0 -1
  87. package/src/types/CactusModel.ts +0 -15
  88. package/src/types/CactusSTTModel.ts +0 -10
@@ -0,0 +1,549 @@
1
+ #pragma once
2
+
3
+ #include <string>
4
+ #include <vector>
5
+ #include <algorithm>
6
+ #include <cctype>
7
+ #include <map>
8
+ #include <set>
9
+
10
+ namespace gemma {
11
+
12
+ inline std::string to_upper(const std::string& s) {
13
+ std::string result = s;
14
+ for (auto& c : result) c = std::toupper(c);
15
+ return result;
16
+ }
17
+
18
+ inline std::string escape(const std::string& s) {
19
+ return "<escape>" + s + "<escape>";
20
+ }
21
+
22
+ inline void skip_whitespace(const std::string& json, size_t& pos) {
23
+ while (pos < json.length() && std::isspace(json[pos])) pos++;
24
+ }
25
+
26
+ inline std::string extract_json_string(const std::string& json, size_t& pos) {
27
+ std::string value;
28
+ while (pos < json.length() && json[pos] != '"') {
29
+ if (json[pos] == '\\' && pos + 1 < json.length()) {
30
+ pos++;
31
+ if (json[pos] == 'n') value += '\n';
32
+ else if (json[pos] == 't') value += '\t';
33
+ else if (json[pos] == 'r') value += '\r';
34
+ else if (json[pos] == '"') value += '"';
35
+ else if (json[pos] == '\\') value += '\\';
36
+ else value += json[pos];
37
+ } else {
38
+ value += json[pos];
39
+ }
40
+ pos++;
41
+ }
42
+ if (pos < json.length()) pos++;
43
+ return value;
44
+ }
45
+
46
+ std::string format_argument(const std::string& json, size_t& pos, bool escape_keys);
47
+ std::string format_parameters(const std::string& properties_json, const std::string& /*required_json*/);
48
+
49
+ inline std::string format_argument(const std::string& json, size_t& pos, bool escape_keys = true) {
50
+ skip_whitespace(json, pos);
51
+ if (pos >= json.length()) return "";
52
+
53
+ char c = json[pos];
54
+
55
+ if (c == '"') {
56
+ std::string value = extract_json_string(json, pos);
57
+ return escape(value);
58
+ } else if (c == '{') {
59
+ std::string result = "{";
60
+ pos++;
61
+ bool first = true;
62
+
63
+ while (pos < json.length()) {
64
+ skip_whitespace(json, pos);
65
+ if (pos >= json.length() || json[pos] == '}') { pos++; break; }
66
+ if (json[pos] == ',') { pos++; continue; }
67
+
68
+ if (json[pos] != '"') break;
69
+ pos++;
70
+ std::string key = extract_json_string(json, pos);
71
+
72
+ skip_whitespace(json, pos);
73
+ if (pos < json.length() && json[pos] == ':') pos++;
74
+
75
+ std::string value = format_argument(json, pos, escape_keys);
76
+
77
+ if (!first) result += ",";
78
+ first = false;
79
+ if (escape_keys) {
80
+ result += escape(key) + ":" + value;
81
+ } else {
82
+ result += key + ":" + value;
83
+ }
84
+ }
85
+ result += "}";
86
+ return result;
87
+ } else if (c == '[') {
88
+ std::string result = "[";
89
+ pos++;
90
+ bool first = true;
91
+
92
+ while (pos < json.length()) {
93
+ skip_whitespace(json, pos);
94
+ if (pos >= json.length() || json[pos] == ']') { pos++; break; }
95
+ if (json[pos] == ',') { pos++; continue; }
96
+
97
+ std::string value = format_argument(json, pos, escape_keys);
98
+
99
+ if (!first) result += ",";
100
+ first = false;
101
+ result += value;
102
+ }
103
+ result += "]";
104
+ return result;
105
+ } else if (json.compare(pos, 4, "true") == 0) {
106
+ pos += 4;
107
+ return "true";
108
+ } else if (json.compare(pos, 5, "false") == 0) {
109
+ pos += 5;
110
+ return "false";
111
+ } else if (json.compare(pos, 4, "null") == 0) {
112
+ pos += 4;
113
+ return "null";
114
+ } else {
115
+ size_t start = pos;
116
+ while (pos < json.length() && (std::isdigit(json[pos]) || json[pos] == '.' ||
117
+ json[pos] == '-' || json[pos] == '+' || json[pos] == 'e' || json[pos] == 'E')) {
118
+ pos++;
119
+ }
120
+ return json.substr(start, pos - start);
121
+ }
122
+ }
123
+
124
+ inline std::map<std::string, std::string> parse_json_object_raw(const std::string& json, size_t& pos) {
125
+ std::map<std::string, std::string> result;
126
+ skip_whitespace(json, pos);
127
+ if (pos >= json.length() || json[pos] != '{') return result;
128
+ pos++;
129
+
130
+ while (pos < json.length()) {
131
+ skip_whitespace(json, pos);
132
+ if (pos >= json.length() || json[pos] == '}') { pos++; break; }
133
+ if (json[pos] == ',') { pos++; continue; }
134
+
135
+ if (json[pos] != '"') break;
136
+ pos++;
137
+ std::string key = extract_json_string(json, pos);
138
+
139
+ skip_whitespace(json, pos);
140
+ if (pos < json.length() && json[pos] == ':') pos++;
141
+ skip_whitespace(json, pos);
142
+
143
+ size_t value_start = pos;
144
+ if (json[pos] == '"') {
145
+ pos++;
146
+ while (pos < json.length() && json[pos] != '"') {
147
+ if (json[pos] == '\\') pos++;
148
+ pos++;
149
+ }
150
+ pos++;
151
+ } else if (json[pos] == '{') {
152
+ int depth = 1;
153
+ pos++;
154
+ while (pos < json.length() && depth > 0) {
155
+ if (json[pos] == '{') depth++;
156
+ else if (json[pos] == '}') depth--;
157
+ else if (json[pos] == '"') {
158
+ pos++;
159
+ while (pos < json.length() && json[pos] != '"') {
160
+ if (json[pos] == '\\') pos++;
161
+ pos++;
162
+ }
163
+ }
164
+ pos++;
165
+ }
166
+ } else if (json[pos] == '[') {
167
+ int depth = 1;
168
+ pos++;
169
+ while (pos < json.length() && depth > 0) {
170
+ if (json[pos] == '[') depth++;
171
+ else if (json[pos] == ']') depth--;
172
+ else if (json[pos] == '"') {
173
+ pos++;
174
+ while (pos < json.length() && json[pos] != '"') {
175
+ if (json[pos] == '\\') pos++;
176
+ pos++;
177
+ }
178
+ }
179
+ pos++;
180
+ }
181
+ } else {
182
+ while (pos < json.length() && json[pos] != ',' && json[pos] != '}') pos++;
183
+ }
184
+ result[key] = json.substr(value_start, pos - value_start);
185
+ }
186
+ return result;
187
+ }
188
+
189
+ inline std::string get_json_string_value(const std::string& json, size_t pos) {
190
+ skip_whitespace(json, pos);
191
+ if (pos < json.length() && json[pos] == '"') {
192
+ pos++;
193
+ return extract_json_string(json, pos);
194
+ }
195
+ return "";
196
+ }
197
+
198
+ inline std::string format_parameters(const std::string& properties_json, const std::string& /*required_json*/) {
199
+ static const std::set<std::string> standard_keys = {"description", "type", "properties", "required", "nullable"};
200
+
201
+ size_t pos = 0;
202
+ auto properties = parse_json_object_raw(properties_json, pos);
203
+
204
+ std::string result;
205
+ bool first = true;
206
+
207
+ for (const auto& [key, value_json] : properties) {
208
+ if (standard_keys.count(key)) continue;
209
+
210
+ if (!first) result += ",";
211
+ first = false;
212
+
213
+ size_t prop_pos = 0;
214
+ auto prop_obj = parse_json_object_raw(value_json, prop_pos);
215
+
216
+ result += key + ":{";
217
+
218
+ if (prop_obj.count("description")) {
219
+ std::string desc = get_json_string_value(prop_obj["description"], 0);
220
+ result += "description:" + escape(desc);
221
+ }
222
+
223
+ std::string type_val;
224
+ if (prop_obj.count("type")) {
225
+ type_val = get_json_string_value(prop_obj["type"], 0);
226
+ }
227
+
228
+ if (to_upper(type_val) == "STRING") {
229
+ if (prop_obj.count("enum")) {
230
+ size_t enum_pos = 0;
231
+ std::string enum_formatted = format_argument(prop_obj["enum"], enum_pos, true);
232
+ result += ",enum:" + enum_formatted;
233
+ }
234
+ } else if (to_upper(type_val) == "OBJECT") {
235
+ if (prop_obj.count("properties")) {
236
+ std::string nested_required;
237
+ if (prop_obj.count("required")) {
238
+ nested_required = prop_obj["required"];
239
+ }
240
+ result += ",properties:{" + format_parameters(prop_obj["properties"], nested_required) + "}";
241
+ }
242
+ if (prop_obj.count("required")) {
243
+ result += ",required:[";
244
+ size_t req_pos = 0;
245
+ skip_whitespace(prop_obj["required"], req_pos);
246
+ if (req_pos < prop_obj["required"].length() && prop_obj["required"][req_pos] == '[') {
247
+ req_pos++;
248
+ bool req_first = true;
249
+ while (req_pos < prop_obj["required"].length()) {
250
+ skip_whitespace(prop_obj["required"], req_pos);
251
+ if (prop_obj["required"][req_pos] == ']') break;
252
+ if (prop_obj["required"][req_pos] == ',') { req_pos++; continue; }
253
+ if (prop_obj["required"][req_pos] == '"') {
254
+ req_pos++;
255
+ std::string req_item = extract_json_string(prop_obj["required"], req_pos);
256
+ if (!req_first) result += ",";
257
+ req_first = false;
258
+ result += escape(req_item);
259
+ }
260
+ }
261
+ }
262
+ result += "]";
263
+ }
264
+ } else if (to_upper(type_val) == "ARRAY") {
265
+ if (prop_obj.count("items")) {
266
+ result += ",items:{";
267
+ size_t items_pos = 0;
268
+ auto items_obj = parse_json_object_raw(prop_obj["items"], items_pos);
269
+ bool items_first = true;
270
+
271
+ for (const auto& [item_key, item_value] : items_obj) {
272
+ if (!items_first) result += ",";
273
+ items_first = false;
274
+
275
+ if (item_key == "properties") {
276
+ std::string items_required;
277
+ if (items_obj.count("required")) {
278
+ items_required = items_obj["required"];
279
+ }
280
+ result += "properties:{" + format_parameters(item_value, items_required) + "}";
281
+ } else if (item_key == "required") {
282
+ result += "required:[";
283
+ size_t req_pos = 0;
284
+ skip_whitespace(item_value, req_pos);
285
+ if (req_pos < item_value.length() && item_value[req_pos] == '[') {
286
+ req_pos++;
287
+ bool req_first = true;
288
+ while (req_pos < item_value.length()) {
289
+ skip_whitespace(item_value, req_pos);
290
+ if (item_value[req_pos] == ']') break;
291
+ if (item_value[req_pos] == ',') { req_pos++; continue; }
292
+ if (item_value[req_pos] == '"') {
293
+ req_pos++;
294
+ std::string req_item = extract_json_string(item_value, req_pos);
295
+ if (!req_first) result += ",";
296
+ req_first = false;
297
+ result += escape(req_item);
298
+ }
299
+ }
300
+ }
301
+ result += "]";
302
+ } else if (item_key == "type") {
303
+ std::string item_type = get_json_string_value(item_value, 0);
304
+ result += "type:" + escape(to_upper(item_type));
305
+ } else {
306
+ size_t val_pos = 0;
307
+ result += item_key + ":" + format_argument(item_value, val_pos, true);
308
+ }
309
+ }
310
+ result += "}";
311
+ }
312
+ }
313
+
314
+ if (!type_val.empty()) {
315
+ result += ",type:" + escape(to_upper(type_val));
316
+ }
317
+
318
+ result += "}";
319
+ }
320
+
321
+ return result;
322
+ }
323
+
324
+ inline std::string format_function_declaration(const std::string& name,
325
+ const std::string& description,
326
+ const std::string& params_json) {
327
+ std::string result = "declaration:" + name + "{";
328
+ result += "description:" + escape(description);
329
+
330
+ if (!params_json.empty()) {
331
+ result += ",parameters:{";
332
+
333
+ size_t pos = 0;
334
+ auto params = parse_json_object_raw(params_json, pos);
335
+
336
+ if (params.count("properties")) {
337
+ std::string required_json;
338
+ if (params.count("required")) {
339
+ required_json = params["required"];
340
+ }
341
+ result += "properties:{" + format_parameters(params["properties"], required_json) + "}";
342
+ }
343
+
344
+ if (params.count("required")) {
345
+ result += ",required:[";
346
+ size_t req_pos = 0;
347
+ skip_whitespace(params["required"], req_pos);
348
+ if (req_pos < params["required"].length() && params["required"][req_pos] == '[') {
349
+ req_pos++;
350
+ bool first = true;
351
+ while (req_pos < params["required"].length()) {
352
+ skip_whitespace(params["required"], req_pos);
353
+ if (params["required"][req_pos] == ']') break;
354
+ if (params["required"][req_pos] == ',') { req_pos++; continue; }
355
+ if (params["required"][req_pos] == '"') {
356
+ req_pos++;
357
+ std::string item = extract_json_string(params["required"], req_pos);
358
+ if (!first) result += ",";
359
+ first = false;
360
+ result += escape(item);
361
+ }
362
+ }
363
+ }
364
+ result += "]";
365
+ }
366
+
367
+ if (params.count("type")) {
368
+ std::string type_val = get_json_string_value(params["type"], 0);
369
+ result += ",type:" + escape(to_upper(type_val));
370
+ }
371
+
372
+ result += "}";
373
+ }
374
+
375
+ result += "}";
376
+ return result;
377
+ }
378
+
379
+ template<typename ToolFunction>
380
+ inline std::string format_tools(const std::vector<ToolFunction>& tools) {
381
+ if (tools.empty()) return "";
382
+
383
+ std::string result;
384
+ for (const auto& tool : tools) {
385
+ result += "<start_function_declaration>";
386
+ std::string params_json;
387
+ auto it = tool.parameters.find("schema");
388
+ if (it != tool.parameters.end()) {
389
+ params_json = it->second;
390
+ }
391
+
392
+ result += format_function_declaration(tool.name, tool.description, params_json);
393
+ result += "<end_function_declaration>";
394
+ }
395
+ return result;
396
+ }
397
+
398
+
399
+ inline std::string unescape(const std::string& s) {
400
+ const std::string ESCAPE_TAG = "<escape>";
401
+ std::string result = s;
402
+ size_t pos = 0;
403
+ while ((pos = result.find(ESCAPE_TAG, pos)) != std::string::npos) {
404
+ result.erase(pos, ESCAPE_TAG.length());
405
+ }
406
+ return result;
407
+ }
408
+
409
+ inline std::string args_to_json(const std::string& args_content) {
410
+ std::string result = "{";
411
+ size_t pos = 0;
412
+ bool first = true;
413
+
414
+ if (!args_content.empty() && args_content[0] == '{') pos = 1;
415
+
416
+ while (pos < args_content.length()) {
417
+ while (pos < args_content.length() && std::isspace(args_content[pos])) pos++;
418
+ if (pos >= args_content.length() || args_content[pos] == '}') break;
419
+ if (args_content[pos] == ',') { pos++; continue; }
420
+
421
+ size_t key_start = pos;
422
+ while (pos < args_content.length() && args_content[pos] != ':') pos++;
423
+ std::string key = args_content.substr(key_start, pos - key_start);
424
+ if (pos < args_content.length()) pos++;
425
+
426
+ std::string value;
427
+ while (pos < args_content.length() && std::isspace(args_content[pos])) pos++;
428
+
429
+ if (pos < args_content.length()) {
430
+ if (args_content.compare(pos, 8, "<escape>") == 0) {
431
+ pos += 8;
432
+ size_t val_end = args_content.find("<escape>", pos);
433
+ if (val_end != std::string::npos) {
434
+ value = "\"" + args_content.substr(pos, val_end - pos) + "\"";
435
+ pos = val_end + 8;
436
+ }
437
+ } else if (args_content[pos] == '{') {
438
+ int depth = 1;
439
+ size_t start = pos;
440
+ pos++;
441
+ while (pos < args_content.length() && depth > 0) {
442
+ if (args_content[pos] == '{') depth++;
443
+ else if (args_content[pos] == '}') depth--;
444
+ pos++;
445
+ }
446
+ value = args_to_json(args_content.substr(start, pos - start));
447
+ } else if (args_content[pos] == '[') {
448
+ int depth = 1;
449
+ size_t start = pos;
450
+ pos++;
451
+ while (pos < args_content.length() && depth > 0) {
452
+ if (args_content[pos] == '[') depth++;
453
+ else if (args_content[pos] == ']') depth--;
454
+ pos++;
455
+ }
456
+ std::string arr_content = args_content.substr(start + 1, pos - start - 2);
457
+ value = "[";
458
+ size_t arr_pos = 0;
459
+ bool first_item = true;
460
+ while (arr_pos < arr_content.length()) {
461
+ while (arr_pos < arr_content.length() && (std::isspace(arr_content[arr_pos]) || arr_content[arr_pos] == ',')) arr_pos++;
462
+ if (arr_pos >= arr_content.length()) break;
463
+
464
+ if (!first_item) value += ",";
465
+ first_item = false;
466
+
467
+ if (arr_content.compare(arr_pos, 8, "<escape>") == 0) {
468
+ arr_pos += 8;
469
+ size_t end = arr_content.find("<escape>", arr_pos);
470
+ if (end != std::string::npos) {
471
+ value += "\"" + arr_content.substr(arr_pos, end - arr_pos) + "\"";
472
+ arr_pos = end + 8;
473
+ }
474
+ } else {
475
+ size_t end = arr_content.find_first_of(",]", arr_pos);
476
+ if (end == std::string::npos) end = arr_content.length();
477
+ value += arr_content.substr(arr_pos, end - arr_pos);
478
+ arr_pos = end;
479
+ }
480
+ }
481
+ value += "]";
482
+ } else {
483
+ size_t val_start = pos;
484
+ while (pos < args_content.length() && args_content[pos] != ',' && args_content[pos] != '}') {
485
+ pos++;
486
+ }
487
+ value = args_content.substr(val_start, pos - val_start);
488
+ while (!value.empty() && std::isspace(value.back())) value.pop_back();
489
+ }
490
+ }
491
+
492
+ if (!first) result += ",";
493
+ first = false;
494
+ result += "\"" + key + "\":" + value;
495
+ }
496
+
497
+ result += "}";
498
+ return result;
499
+ }
500
+
501
+ inline void parse_function_calls(std::string& response, std::vector<std::string>& function_calls) {
502
+ const std::string CALL_START = "<start_function_call>";
503
+ const std::string CALL_END = "<end_function_call>";
504
+ size_t pos = 0;
505
+
506
+ while ((pos = response.find(CALL_START, pos)) != std::string::npos) {
507
+ size_t content_start = pos + CALL_START.length();
508
+ size_t call_end_pos = response.find(CALL_END, content_start);
509
+
510
+ size_t content_end = (call_end_pos != std::string::npos) ? call_end_pos : response.length();
511
+ std::string call_content = response.substr(content_start, content_end - content_start);
512
+
513
+ if (call_content.compare(0, 5, "call:") == 0) {
514
+ size_t brace_pos = call_content.find('{');
515
+
516
+ if (brace_pos == std::string::npos) {
517
+ size_t sep_pos = call_content.find_first_of(", ", 5);
518
+ if (sep_pos != std::string::npos) {
519
+ std::string func_name = call_content.substr(5, sep_pos - 5);
520
+ size_t args_start = sep_pos + 1;
521
+ while (args_start < call_content.length() &&
522
+ (call_content[args_start] == ' ' || call_content[args_start] == ',')) {
523
+ args_start++;
524
+ }
525
+ std::string args_content = "{" + call_content.substr(args_start);
526
+ if (args_content.back() != '}') args_content += "}";
527
+
528
+ std::string args_json = args_to_json(args_content);
529
+ std::string json_call = "{\"name\":\"" + func_name + "\",\"arguments\":" + args_json + "}";
530
+ function_calls.push_back(json_call);
531
+ }
532
+ } else {
533
+ std::string func_name = call_content.substr(5, brace_pos - 5);
534
+ std::string args_content = call_content.substr(brace_pos);
535
+ if (args_content.back() != '}') args_content += "}";
536
+
537
+ std::string args_json = args_to_json(args_content);
538
+ std::string json_call = "{\"name\":\"" + func_name + "\",\"arguments\":" + args_json + "}";
539
+ function_calls.push_back(json_call);
540
+ }
541
+ }
542
+
543
+ size_t erase_end = (call_end_pos != std::string::npos) ?
544
+ call_end_pos + CALL_END.length() : response.length();
545
+ response.erase(pos, erase_end - pos);
546
+ }
547
+ }
548
+
549
+ } // namespace gemma