/* Dynagate LLM Gateway: dynamically change models on the fly (C++20 port) Created by Luxferre in 2026, released into the public domain */ #ifndef DYNAGATE_HPP #define DYNAGATE_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Minimal RFC 8259 JSON value, parser and serializer (json.cpp) enum class JType { Null, Bool, Int, Float, Str, Arr, Obj }; struct Json { JType type = JType::Null; bool bval = false; int64_t ival = 0; double dval = 0.0; std::string sval; std::vector aval; std::vector> oval; Json(); ~Json(); Json(const Json &); Json(Json &&) noexcept; Json &operator=(const Json &); Json &operator=(Json &&) noexcept; Json(std::nullptr_t); Json(bool b); Json(int n); Json(int64_t n); Json(double d); Json(const char *s); Json(std::string s); Json(std::string_view s); static Json object(); static Json object( std::initializer_list> init); static Json array(); static Json array(std::initializer_list init); bool is_null() const { return type == JType::Null; } bool is_bool() const { return type == JType::Bool; } bool is_boolean() const { return type == JType::Bool; } bool is_number_integer() const { return type == JType::Int; } bool is_number() const { return type == JType::Int || type == JType::Float; } bool is_string() const { return type == JType::Str; } bool is_array() const { return type == JType::Arr; } bool is_object() const { return type == JType::Obj; } bool empty() const; size_t size() const; bool contains(std::string_view k) const; void erase(std::string_view k); Json &operator[](std::string_view k); const Json &operator[](std::string_view k) const; Json &at(size_t i); const Json &at(size_t i) const; void push_back(Json v); std::string str(std::string_view def = "") const; bool as_bool(bool def = false) const; int64_t as_int64(int64_t def = 0) const; int as_int(int def = 0) const; std::string dump(int indent = -1) const; static bool parse(std::string_view src, Json &out); static Json parse(std::string_view src); }; struct ModelConfig { std::string model; std::string key; std::string endpoint; std::string extra; bool operator==(const ModelConfig &other) const = default; }; class ConfigManager { public: explicit ConfigManager(std::string file_path); ConfigManager(std::vector configs, std::vector unique_models); bool load(std::string *err_out = nullptr); std::vector get_configs() const; std::vector get_unique_models() const; void watch(std::atomic &stop_flag); private: void update(std::vector configs, std::filesystem::file_time_type mod_time, std::uintmax_t size, bool has_stat); std::string file_path_; mutable std::shared_mutex mu_; std::vector configs_; std::vector unique_models_; std::filesystem::file_time_type last_mod_time_{}; std::uintmax_t last_size_ = 0; bool has_stat_ = false; }; struct HeaderList { std::vector> items; void add(std::string_view key, std::string_view value); void set(std::string_view key, std::string_view value); void del(std::string_view key); std::string get(std::string_view key) const; std::vector values(std::string_view key) const; }; struct HttpRequest { std::string method; std::string path; HeaderList headers; std::string body; int client_fd = -1; std::function is_cancelled; }; class ResponseWriter { public: virtual ~ResponseWriter() = default; virtual HeaderList &header() = 0; virtual void write_header(int status_code) = 0; virtual bool write(std::string_view data) = 0; virtual void flush() = 0; }; class RecorderResponseWriter : public ResponseWriter { public: HeaderList &header() override { return headers_; } void write_header(int status_code) override { if (!header_written_) { status_code_ = status_code; header_written_ = true; } } bool write(std::string_view data) override { if (!header_written_) { write_header(200); } body_.append(data.data(), data.size()); return true; } void flush() override {} int status_code() const { return status_code_; } const HeaderList &headers() const { return headers_; } const std::string &body() const { return body_; } private: int status_code_ = 200; bool header_written_ = false; HeaderList headers_; std::string body_; }; // Logging and string utilities (config.cpp) void log_msg(const char *fmt, ...); std::string str_trim(std::string_view sv); std::string str_to_lower(std::string_view sv); bool str_iequals(std::string_view a, std::string_view b); bool parse_csv(std::string_view content, std::vector> &rows, std::string *err_out = nullptr); std::vector parse_auth_tokens(std::string_view content); void start_csv_updater(std::atomic &stop_flag, const std::string &command, std::chrono::milliseconds interval); // Core gateway logic and handlers (handler.cpp) void ensure_curl_initialized(); bool check_auth(const std::vector &expected_tokens, const HttpRequest &req); void send_unauthorized(ResponseWriter &w); std::vector get_trial_configs( const std::vector &configs, const std::vector &unique_models, std::string_view requested_model); std::string build_url(std::string_view endpoint, std::string_view req_path); int64_t fibonacci(int n); bool is_media_content(const Json &m); Json normalize_content(const Json &content_any); void autodetect_and_normalize_messages(Json &body_map); Json convert_anthropic_to_openai(const Json &body_map); Json convert_anthropic_content_to_openai(const Json &cnt_any); Json convert_openai_to_anthropic_response( const Json &openai_resp, const std::string &requested_model); Json convert_responses_to_openai(const Json &body_map); Json convert_openai_to_responses_response( const Json &openai_resp, const std::string &requested_model); void handle_models(ConfigManager &cm, const std::vector &expected_tokens, const HttpRequest &req, ResponseWriter &w); void handle_chat_completions( ConfigManager &cm, const std::vector &expected_tokens, std::chrono::milliseconds retry_base_delay, const HttpRequest &req, ResponseWriter &w); void handle_image_generations( ConfigManager &cm, const std::vector &expected_tokens, std::chrono::milliseconds retry_base_delay, const HttpRequest &req, ResponseWriter &w); void handle_messages( ConfigManager &cm, const std::vector &expected_tokens, std::chrono::milliseconds retry_base_delay, const HttpRequest &req, ResponseWriter &w); void handle_responses( ConfigManager &cm, const std::vector &expected_tokens, std::chrono::milliseconds retry_base_delay, const HttpRequest &req, ResponseWriter &w); // HTTP server (server.cpp) class GatewayServer { public: GatewayServer(int port, ConfigManager &cm, std::vector auth_tokens, std::chrono::milliseconds retry_delay); ~GatewayServer(); GatewayServer(const GatewayServer &) = delete; GatewayServer &operator=(const GatewayServer &) = delete; bool start(std::string *err_out = nullptr); void serve(); void stop(); int bound_port() const { return bound_port_; } private: void handle_client(int client_fd); int requested_port_; int bound_port_ = 0; int listen_fd_ = -1; ConfigManager &cm_; std::vector auth_tokens_; std::chrono::milliseconds retry_delay_; std::atomic stopping_{false}; mutable std::mutex active_mu_; std::condition_variable active_cv_; int active_connections_ = 0; static constexpr int kMaxConcurrentConnections = 256; }; #endif // DYNAGATE_HPP