skip to content
==> 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}