2026-10-12 07:43:34 +03:00
|
|
|
#ifndef HYGATE_H
|
|
|
|
|
#define HYGATE_H
|
|
|
|
|
|
|
|
|
|
#include <stddef.h>
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
#include <stdbool.h>
|
|
|
|
|
|
|
|
|
|
#define DEFAULT_UA "Mozilla/5.0 (X11; Linux x86_64; rv:153.0) Gecko/20100101 Firefox/153.0"
|
|
|
|
|
#define DEFAULT_PORT "8080"
|
|
|
|
|
#define DEFAULT_ENDPOINT "https://tencent-hy3.hf.space"
|
|
|
|
|
#define DEFAULT_MODEL "hy3"
|
|
|
|
|
#define MAX_RETRIES 5
|
|
|
|
|
#define UPSTREAM_TIMEOUT 300L
|
|
|
|
|
#define MAX_CONCURRENT_THREADS 16
|
|
|
|
|
#define MAX_BODY_BYTES (5u * 1024u * 1024u) /* 5 MiB request body cap */
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
|
/* Dynamic string buffer (always NUL terminated) */
|
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
|
typedef struct {
|
|
|
|
|
char *buf;
|
|
|
|
|
size_t len;
|
|
|
|
|
size_t cap;
|
|
|
|
|
} strbuf;
|
|
|
|
|
|
|
|
|
|
void sb_init(strbuf *s);
|
|
|
|
|
void sb_free(strbuf *s);
|
|
|
|
|
int sb_append(strbuf *s, const char *data, size_t n);
|
|
|
|
|
int sb_append_str(strbuf *s, const char *str);
|
|
|
|
|
void sb_reset(strbuf *s);
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
|
/* Small utilities */
|
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
|
void generate_uuid(char out[37]);
|
|
|
|
|
long fib_delay(int attempt);
|
|
|
|
|
int write_all(int fd, const char *buf, size_t n);
|
|
|
|
|
int iequals(const char *a, const char *b);
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
|
/* Tool call representation (parsed from Gradio stream) */
|
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
|
typedef struct {
|
|
|
|
|
char *id;
|
|
|
|
|
char *type;
|
|
|
|
|
char *name;
|
|
|
|
|
char *arguments;
|
|
|
|
|
} tool_call_t;
|
|
|
|
|
|
|
|
|
|
void free_toolcalls(tool_call_t *tc, int n);
|
|
|
|
|
|
|
|
|
|
/* Parse a single Gradio SSE data payload: [["content","reasoning",[...]]]
|
|
|
|
|
* Returns 1 on success (caller frees *out_content, *out_reasoning,
|
|
|
|
|
* *out_tc via free_toolcalls). */
|
|
|
|
|
int parse_gradio_stream(const char *data_json,
|
|
|
|
|
char **out_content, char **out_reasoning,
|
|
|
|
|
tool_call_t **out_tc, int *out_n_tc);
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
|
/* JSON request <-> Gradio payload translation */
|
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
|
/* Build the Gradio call payload {"data":[...]} from an OpenAI chat
|
|
|
|
|
* completion request object. Caller frees the result. Returns NULL on
|
|
|
|
|
* malformed input. */
|
|
|
|
|
char *build_gradio_payload(void *req_obj, const char *model_default);
|
|
|
|
|
|
|
|
|
|
/* Build a non-streaming OpenAI completion response JSON string. Caller
|
|
|
|
|
* frees the result. */
|
|
|
|
|
char *build_completion_response(const char *id, long created, const char *model,
|
|
|
|
|
const char *content, const char *reasoning,
|
2026-10-13 20:36:29 +03:00
|
|
|
tool_call_t *tc, int n_tc, int prompt_tokens);
|
2026-10-12 07:43:34 +03:00
|
|
|
|
2026-10-13 20:36:29 +03:00
|
|
|
|
|
|
|
|
/* Token counting (embedded cl100k_base BPE tokenizer). Counts are
|
|
|
|
|
* deterministic subword counts; the served model (Hunyuan) uses a
|
|
|
|
|
* different tokenizer, so these are an approximation, not exact. */
|
|
|
|
|
int tok_count(const char *text, int len);
|
|
|
|
|
int tok_count_str(const char *text);
|
|
|
|
|
|
|
|
|
|
/* Count tokens across an OpenAI request's messages (prompt side). */
|
|
|
|
|
int count_messages_tokens(void *req_obj);
|
|
|
|
|
/* Count tokens of a generated completion (content + reasoning + tool calls). */
|
|
|
|
|
int count_completion_tokens(const char *content, const char *reasoning,
|
|
|
|
|
tool_call_t *tc, int n_tc);
|
2026-10-12 07:43:34 +03:00
|
|
|
/* Build a streaming tool-call delta JSON array (first emission: full
|
|
|
|
|
* tool call with index). Caller frees the result. */
|
|
|
|
|
char *toolcall_delta_json(tool_call_t *tc, int idx);
|
|
|
|
|
|
|
|
|
|
/* Build a streaming tool-call argument-delta JSON array (index +
|
|
|
|
|
* function.arguments only). Caller frees the result. */
|
|
|
|
|
char *toolcall_delta_argdelta(int idx, const char *delta);
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
|
/* Server / request handling */
|
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
|
typedef struct {
|
|
|
|
|
const char *port;
|
|
|
|
|
const char *endpoint;
|
|
|
|
|
const char *model;
|
|
|
|
|
const char *user_agent; /* empty string => use DEFAULT_UA */
|
|
|
|
|
long max_body_bytes;
|
|
|
|
|
int max_connections;
|
|
|
|
|
} config_t;
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
char method[16];
|
|
|
|
|
char path[4096];
|
|
|
|
|
char content_type[256];
|
|
|
|
|
long content_length;
|
|
|
|
|
char ua_override[1024];
|
|
|
|
|
bool has_x_ua;
|
|
|
|
|
char *body;
|
|
|
|
|
size_t body_len;
|
|
|
|
|
} http_req_t;
|
|
|
|
|
|
|
|
|
|
void http_req_free(http_req_t *r);
|
|
|
|
|
|
|
|
|
|
/* Read the full HTTP request header block from fd into buf. Returns 0
|
|
|
|
|
* on success, -1 on error/timeout. */
|
|
|
|
|
int read_headers(int fd, strbuf *buf);
|
|
|
|
|
|
|
|
|
|
/* Parse a buffered HTTP request. Returns 0 on success. */
|
|
|
|
|
int parse_http_request(strbuf *buf, int fd, http_req_t *req, long max_body_bytes);
|
|
|
|
|
|
|
|
|
|
/* Handle one accepted client connection (reads request, routes,
|
|
|
|
|
* writes response). */
|
|
|
|
|
void handle_connection(int fd, const config_t *cfg);
|
|
|
|
|
|
|
|
|
|
#endif /* HYGATE_H */
|