==> src/infer_request.c <==1/* infer.rs's request parsing, word by word as the Rust does it. */
2#include "mesh/infer_request.h"
3#include "mesh/rsfmt.h"
4
5#include <stdlib.h>
6#include <string.h>
7
8/* char::is_whitespace for ASCII and the common Unicode spaces (the prompt's split points). */
9static size_t ws_len(const char *s) {
10 unsigned char c = (unsigned char)s[0];
11 if (c == ' ' || (c >= 9 && c <= 13)) return 1;
12 if (c == 0xc2 && ((unsigned char)s[1] == 0x85 || (unsigned char)s[1] == 0xa0)) return 2;
13 if (c == 0xe1 && (unsigned char)s[1] == 0x9a && (unsigned char)s[2] == 0x80) return 3;
14 if (c == 0xe2 && (unsigned char)s[1] == 0x80 && (((unsigned char)s[2] >= 0x80 && (unsigned char)s[2] <= 0x8a) ||
15 (unsigned char)s[2] == 0xa8 || (unsigned char)s[2] == 0xa9 || (unsigned char)s[2] == 0xaf))
16 return 3;
17 if (c == 0xe2 && (unsigned char)s[1] == 0x81 && (unsigned char)s[2] == 0x9f) return 3;
18 if (c == 0xe3 && (unsigned char)s[1] == 0x80 && (unsigned char)s[2] == 0x80) return 3;
19 return 0;
20}
21
22static const char *trim_start(const char *s) {
23 size_t k;
24 while (*s && (k = ws_len(s))) s += k;
25 return s;
26}
27
28/* split_once(char::is_whitespace): the word's end and what follows the separator. */
29static void split_ws(const char *s, size_t *wlen, const char **after) {
30 const char *p = s;
31 size_t k = 0;
32 while (*p && !(k = ws_len(p))) p++;
33 *wlen = (size_t)(p - s);
34 *after = *p ? p + k : p;
35}
36
37/* u32::from_str: optional '+', digits, no overflow. */
38static int u32_of(const char *s, size_t n, uint32_t *out) {
39 size_t i = n > 0 && s[0] == '+' && n > 1;
40 if (i == n) return -1;
41 uint64_t x = 0;
42 for (; i < n; i++) {
43 if (s[i] < '0' || s[i] > '9') return -1;
44 x = x * 10 + (uint64_t)(s[i] - '0');
45 if (x > UINT32_MAX) return -1;
46 }
47 *out = (uint32_t)x;
48 return 0;
49}
50
51static int num(const char *w, size_t wn, const char *v, size_t vn, uint32_t *out, char **err) {
52 if (u32_of(v, vn, out) == 0) return 0;
53 *err = mesh_buf_fmt("%.*s: not a number (%s)", (int)wn, w, MESH_INFER_USAGE);
54 return -1;
55}
56
57static int prefixed(const char *w, size_t n, const char *p) { size_t k = strlen(p); return n >= k && strncmp(w, p, k) == 0; }
58
59int mesh_infer_parse(const char *arg, mesh_infer_request *r, char **err) {
60 memset(r, 0, sizeof *r);
61 strcpy(r->mode, "fast");
62 r->max = 32;
63 const char *rest = trim_start(arg);
64 for (;;) {
65 size_t wn;
66 const char *after;
67 split_ws(rest, &wn, &after);
68 const char *w = rest;
69 if ((wn == 4 && !strncmp(w, "fast", 4)) || (wn == 6 && !strncmp(w, "fast16", 6)) || (wn == 5 && !strncmp(w, "exact", 5)) ||
70 (wn == 9 && !strncmp(w, "exact-cpu", 9))) {
71 memcpy(r->mode, w, wn);
72 r->mode[wn] = 0;
73 } else if (wn == 3 && !strncmp(w, "max", 3)) {
74 const char *a = trim_start(after), *a2;
75 size_t vn;
76 split_ws(a, &vn, &a2);
77 if (num("max", 3, a, vn, &r->max, err)) return -1;
78 rest = trim_start(a2);
79 continue;
80 } else if (prefixed(w, wn, "max=")) {
81 if (num(w, wn, w + 4, wn - 4, &r->max, err)) return -1;
82 } else if (prefixed(w, wn, "prefill=")) {
83 if (num(w, wn, w + 8, wn - 8, &r->prefill, err)) return -1;
84 r->has_prefill = true;
85 } else if (prefixed(w, wn, "decode=")) {
86 if (num(w, wn, w + 7, wn - 7, &r->decode, err)) return -1;
87 r->has_decode = true;
88 } else if (prefixed(w, wn, "mono=")) {
89 if (num(w, wn, w + 5, wn - 5, &r->mono, err)) return -1;
90 r->has_mono = true;
91 } else if (prefixed(w, wn, "len=")) {
92 if (num(w, wn, w + 4, wn - 4, &r->len, err)) return -1;
93 r->has_len = true;
94 } else if (prefixed(w, wn, "repeat=")) {
95 if (num(w, wn, w + 7, wn - 7, &r->repeat, err)) return -1;
96 if (r->repeat < 1) r->repeat = 1;
97 if (r->repeat > 1000) r->repeat = 1000;
98 r->has_repeat = true;
99 } else break;
100 rest = trim_start(after);
101 }
102 /* trim_end */
103 size_t n = strlen(rest);
104 while (n) {
105 size_t back = 0;
106 for (size_t k = 1; k <= 3 && k <= n; k++) if (ws_len(rest + n - k) == k) { back = k; break; }
107 if (!back) break;
108 n -= back;
109 }
110 r->prompt = mesh_xstrndup(rest, n);
111 if (!n) { *err = mesh_buf_fmt("no prompt (%s)", MESH_INFER_USAGE); return -1; }
112 if (r->max == 0 || r->max > 1000) { *err = mesh_buf_fmt("max %u: 1..=1000", r->max); return -1; }
113 if (r->has_mono && (r->has_prefill || r->has_decode)) { *err = mesh_buf_fmt("mono= or prefill=/decode=, not both (%s)", MESH_INFER_USAGE); return -1; }
114 if (r->has_prefill != r->has_decode) { *err = mesh_buf_fmt("prefill= and decode= go together (%s)", MESH_INFER_USAGE); return -1; }
115 return 0;
116}
117
118void mesh_infer_request_free(mesh_infer_request *r) {
119 free(r->prompt);
120 r->prompt = NULL;
121}
122
123void mesh_infer_text(mesh_buf *out, const mesh_infer_msg *res) {
124 for (size_t i = 0; i < res->n; i++) mesh_buf_printf(out, "%s %s\n", res->k[i], res->v[i]);
125 mesh_buf_printf(out, "text %s\n", res->body);
126}
127
128static int hexval(char c) {
129 if (c >= '0' && c <= '9') return c - '0';
130 if (c >= 'a' && c <= 'f') return c - 'a' + 10;
131 if (c >= 'A' && c <= 'F') return c - 'A' + 10;
132 return -1;
133}
134
135char *mesh_infer_url_decode(const char *s) {
136 mesh_buf raw = MESH_BUF_INIT;
137 size_t n = strlen(s);
138 for (size_t i = 0; i < n;) {
139 if (s[i] == '+') { mesh_buf_putc(&raw, ' '); i++; continue; }
140 /* u8::from_str_radix of the two bytes: a sign or anything not hex is no escape. */
141 if (s[i] == '%' && i + 3 <= n) {
142 int a = hexval(s[i + 1]), b = hexval(s[i + 2]);
143 if (a >= 0 && b >= 0) { mesh_buf_putc(&raw, (char)(a * 16 + b)); i += 3; continue; }
144 if (s[i + 1] == '+' && b >= 0) { mesh_buf_putc(&raw, (char)b); i += 3; continue; }
145 }
146 mesh_buf_putc(&raw, s[i]);
147 i++;
148 }
149 mesh_buf out = MESH_BUF_INIT;
150 mesh_rs_lossy(&out, raw.data ? raw.data : "", raw.len);
151 mesh_buf_free(&raw);
152 return mesh_buf_take(&out);
153}
154
155int mesh_infer_from_query(const char *q, mesh_infer_request *r, bool *full, char **err) {
156 mesh_buf words = MESH_BUF_INIT;
157 char *prompt = NULL;
158 *full = false;
159 int nw = 0, rc = 0;
160 const char *p = q;
161 while (*p && rc == 0) {
162 const char *amp = strchr(p, '&');
163 size_t n = amp ? (size_t)(amp - p) : strlen(p);
164 if (n) {
165 char *kv = mesh_xstrndup(p, n);
166 char *eq = strchr(kv, '=');
167 const char *k = kv, *vraw = "";
168 if (eq) { *eq = 0; vraw = eq + 1; }
169 char *v = mesh_infer_url_decode(vraw);
170 if (!strcmp(k, "prompt")) { free(prompt); prompt = mesh_xstrdup(v); }
171 else if (!strcmp(k, "mode")) { mesh_buf_printf(&words, "%s%s", nw++ ? " " : "", v); }
172 else if (!strcmp(k, "max") || !strcmp(k, "prefill") || !strcmp(k, "decode") || !strcmp(k, "mono") || !strcmp(k, "len") ||
173 !strcmp(k, "repeat")) {
174 mesh_buf_printf(&words, "%s%s=%s", nw++ ? " " : "", k, v);
175 } else if (!strcmp(k, "format")) *full = !strcmp(v, "full");
176 else {
177 mesh_buf e = MESH_BUF_INIT;
178 mesh_buf_puts(&e, "unknown parameter ");
179 mesh_rs_debug_cstr(&e, k);
180 mesh_buf_puts(&e, " (prompt, max, mode, prefill, decode, mono, format)");
181 *err = mesh_buf_take(&e);
182 rc = -1;
183 }
184 free(v);
185 free(kv);
186 }
187 if (!amp) break;
188 p = amp + 1;
189 }
190 if (rc == 0 && !prompt) { *err = mesh_xstrdup("no prompt= parameter"); rc = -1; }
191 if (rc == 0) {
192 char *line = mesh_buf_fmt("%s %s", mesh_buf_str(&words), prompt);
193 rc = mesh_infer_parse(line, r, err);
194 free(line);
195 } else memset(r, 0, sizeof *r);
196 free(prompt);
197 mesh_buf_free(&words);
198 return rc;
199}