19#include <nlohmann/json.hpp>
55 char* buf =
static_cast<char*
>(std::malloc(s.size() + 1));
57 std::memcpy(buf, s.c_str(), s.size() + 1);
78static void set_if(
const nlohmann::json& j,
const char* key, T& out) {
79 if (j.contains(key)) { out = j[key].get<T>(); }
89 auto j = nlohmann::json::parse(json_str);
91 set_if(j,
"path", config.
path);
92 set_if(j,
"adapter", config.
adapter);
97 set_if(j,
"n_batch", config.
n_batch);
114static void assign_if_present(
const nlohmann::json& j,
115 const char* key, T& dst) {
116 if (j.contains(key)) { dst = j[key].get<T>(); }
129static void parse_logit_bias_into(
130 const nlohmann::json& j,
131 std::unordered_map<int32_t, float>& dst)
133 if (!j.contains(
"logit_bias") || !j[
"logit_bias"].is_object()) {
136 for (
auto it = j[
"logit_bias"].begin(); it != j[
"logit_bias"].end(); ++it) {
138 dst[std::stoi(it.key())] = it.value().get<
float>();
139 }
catch (
const std::exception&) {
154 auto j = nlohmann::json::parse(json_str);
156 assign_if_present(j,
"temperature", params.
temperature);
157 assign_if_present(j,
"top_p", params.
top_p);
158 assign_if_present(j,
"top_k", params.
top_k);
159 assign_if_present(j,
"min_p", params.
min_p);
163 assign_if_present(j,
"max_tokens", params.
max_tokens);
164 assign_if_present(j,
"grammar", params.
grammar);
183 j[
"error_code"] =
static_cast<int>(result.
error_code);
209 const char* config_json)
217 logger->info(
"C API: inference_load");
219 auto config = parse_config_json(config_json);
220 auto rc = to_backend(backend)->load(config)
222 logger->info(
"C API: inference_load -> {}",
223 static_cast<int>(rc));
225 }
catch (
const std::exception& e) {
226 logger->error(
"inference_load exception: {}", e.what());
244 }
catch (
const std::exception& e) {
245 logger->error(
"inference_activate exception: {}", e.what());
262 to_backend(backend)->deactivate();
264 }
catch (
const std::exception& e) {
265 logger->error(
"inference_deactivate exception: {}", e.what());
282 to_backend(backend)->unload();
284 }
catch (
const std::exception& e) {
285 logger->error(
"inference_unload exception: {}", e.what());
304 return static_cast<int>(to_backend(backend)->state());
319 const char* messages_json,
320 const char* params_json,
323 if (!backend || !result_json) {
326 logger->info(
"C API: inference_generate");
329 auto params = parse_params_json(params_json);
330 auto result = to_backend(backend)->generate(msgs, params);
331 *result_json =
alloc_string(serialize_result_json(result));
332 logger->info(
"C API: inference_generate -> {}",
333 result.ok() ?
"ok" :
"error");
334 return result.ok() ?
ENTROPIC_OK : result.error_code;
335 }
catch (
const std::exception& e) {
336 logger->error(
"inference_generate exception: {}", e.what());
376class CancelFlagBridge {
383 explicit CancelFlagBridge(
int* cancel_flag) {
384 if (cancel_flag ==
nullptr) {
return; }
385 poller_ = std::thread([
this, cancel_flag]() {
386 while (!done_.load(std::memory_order_acquire)) {
387 if (*cancel_flag != 0) {
388 cancel_.store(true, std::memory_order_release);
391 std::this_thread::sleep_for(std::chrono::milliseconds(10));
401 ~CancelFlagBridge() {
402 done_.store(
true, std::memory_order_release);
403 if (poller_.joinable()) { poller_.join(); }
406 CancelFlagBridge(
const CancelFlagBridge&) =
delete;
407 CancelFlagBridge& operator=(
const CancelFlagBridge&) =
delete;
414 std::atomic<bool>& flag() {
return cancel_; }
417 std::atomic<bool> cancel_{
false};
418 std::atomic<bool> done_{
false};
439 const char* messages_json,
440 const char* params_json,
444 if (!backend || !result_json) {
447 logger->info(
"C API: inference_generate_with_cancel");
450 auto params = parse_params_json(params_json);
451 CancelFlagBridge bridge(cancel_flag);
452 auto result = to_backend(backend)->generate(
453 msgs, params, bridge.flag());
454 *result_json =
alloc_string(serialize_result_json(result));
455 logger->info(
"C API: inference_generate_with_cancel -> {}",
456 result.ok() ?
"ok" :
"error");
457 return result.ok() ?
ENTROPIC_OK : result.error_code;
458 }
catch (
const std::exception& e) {
459 logger->error(
"inference_generate_with_cancel exception: {}", e.what());
478 const char* messages_json,
479 const char* params_json,
480 void (*on_token)(
const char* token,
size_t len,
void* user_data),
484 if (!backend || !on_token) {
489 auto params = parse_params_json(params_json);
490 std::atomic<bool> cancel{
false};
492 auto callback = [on_token, user_data, cancel_flag, &cancel]
493 (std::string_view token) {
494 on_token(token.data(), token.size(), user_data);
495 if (cancel_flag && *cancel_flag) {
496 cancel.store(
true, std::memory_order_release);
500 auto result = to_backend(backend)->generate_streaming(
501 msgs, params, callback, cancel);
502 return result.ok() ?
ENTROPIC_OK : result.error_code;
503 }
catch (
const std::exception& e) {
504 logger->error(
"inference_generate_streaming exception: {}", e.what());
522 const char* params_json,
525 if (!backend || !prompt || !result_json) {
529 auto params = parse_params_json(params_json);
530 auto result = to_backend(backend)->complete(prompt, params);
531 *result_json =
alloc_string(serialize_result_json(result));
532 return result.ok() ?
ENTROPIC_OK : result.error_code;
533 }
catch (
const std::exception& e) {
534 logger->error(
"inference_complete exception: {}", e.what());
556 if (!backend || !text) {
return 0; }
558 return to_backend(backend)->count_tokens(std::string(text, text_len));
560 return static_cast<int>(text_len) / 4;
573 delete to_backend(backend);
609static FILE* s_ggml_log_fp =
nullptr;
615static std::mutex s_ggml_log_mu;
616static std::optional<std::string> s_ggml_log_path;
624 const char* text,
void* ) {
625 if (s_ggml_log_fp && text) {
626 fputs(text, s_ggml_log_fp);
627 fflush(s_ggml_log_fp);
637 const char* ,
void* ) {
653 fclose(s_ggml_log_fp);
654 s_ggml_log_fp =
nullptr;
656 s_ggml_log_path.reset();
667 auto canonical = std::filesystem::weakly_canonical(path, ec).string();
668 return ec ? std::string(path) : canonical;
683 std::lock_guard lk(s_ggml_log_mu);
685 if (!path || path[0] ==
'\0') {
693 if (s_ggml_log_path && *s_ggml_log_path != canonical) {
695 "ggml log redirect already wired to {}; ignoring request for {}",
696 *s_ggml_log_path, canonical);
700 FILE* fp = fopen(path,
"w");
702 logger->warn(
"ggml log fopen failed for {}: {}",
703 path, std::strerror(errno));
706 if (s_ggml_log_fp) { fclose(s_ggml_log_fp); }
708 s_ggml_log_path = canonical;
718 std::lock_guard lk(s_ggml_log_mu);
Concrete base class for inference backends (80% logic).
LlamaCppBackend — common llama.cpp patterns (15% layer).
Symbol visibility macro for all exported symbols.
@ ENTROPIC_MODEL_STATE_COLD
On disk only, no RAM consumed.
entropic_error_t
Error codes returned by all C API functions.
@ ENTROPIC_ERROR_INTERNAL
Unexpected internal error (bug)
@ ENTROPIC_ERROR_INVALID_ARGUMENT
NULL pointer, empty string, out-of-range value.
@ ENTROPIC_ERROR_GENERATE_FAILED
Generation failed (context overflow, model error)
@ ENTROPIC_ERROR_LOAD_FAILED
Model load failed (corrupt file, OOM, unsupported format)
Pure C interface contract for inference backends.
struct entropic_inference_backend * entropic_inference_backend_t
Opaque handle to an inference backend instance.
ENTROPIC_EXPORT entropic_error_t entropic_inference_complete(entropic_inference_backend_t backend, const char *prompt, const char *params_json, char **result_json)
Plugin C API: raw text completion without chat template.
static void ggml_log_to_file(enum ggml_log_level, const char *text, void *)
Callback that writes to the ggml log file.
ENTROPIC_EXPORT entropic_error_t entropic_inference_generate_streaming(entropic_inference_backend_t backend, const char *messages_json, const char *params_json, void(*on_token)(const char *token, size_t len, void *user_data), void *user_data, int *cancel_flag)
Plugin C API: streaming generation with token callback and cancel flag.
ENTROPIC_EXPORT entropic_error_t entropic_inference_generate(entropic_inference_backend_t backend, const char *messages_json, const char *params_json, char **result_json)
Plugin C API: blocking generation returning full result.
ENTROPIC_EXPORT int entropic_plugin_api_version()
Plugin API version.
static std::string canonicalize_or_passthrough(const char *path)
Resolve path via weakly_canonical, fall back to raw on error.
ENTROPIC_EXPORT entropic_error_t entropic_inference_unload(entropic_inference_backend_t backend)
Plugin C API: release the loaded model (transition to COLD).
ENTROPIC_EXPORT entropic_error_t entropic_inference_generate_with_cancel(entropic_inference_backend_t backend, const char *messages_json, const char *params_json, char **result_json, int *cancel_flag)
Plugin C API: batch generation with mid-decode cancel.
void entropic_inference_log_silence(void)
Silence all llama/ggml output.
ENTROPIC_EXPORT int entropic_inference_count_tokens(entropic_inference_backend_t backend, const char *text, size_t text_len)
Plugin C API: count tokens for a text span (exact when loaded, estimate when COLD).
ENTROPIC_EXPORT entropic_error_t entropic_inference_deactivate(entropic_inference_backend_t backend)
Plugin C API: demote backend from ACTIVE to WARM (release GPU).
static void ggml_log_silence_locked()
Redirect llama/ggml logs to a file or silence them.
void entropic_inference_log_to_file(const char *path)
Redirect llama/ggml logs to a file or silence them.
ENTROPIC_EXPORT entropic_error_t entropic_inference_load(entropic_inference_backend_t backend, const char *config_json)
Plugin C API: load a model into the inference backend.
ENTROPIC_EXPORT entropic_inference_backend_t entropic_create_inference_backend()
Factory: create inference backend instance.
static void ggml_log_noop(enum ggml_log_level, const char *, void *)
No-op callback.
ENTROPIC_EXPORT void entropic_inference_free(void *ptr)
Plugin C API: free memory allocated by the inference backend.
ENTROPIC_EXPORT void entropic_inference_destroy(entropic_inference_backend_t backend)
Plugin C API: destroy the backend and free its resources.
ENTROPIC_EXPORT entropic_error_t entropic_inference_activate(entropic_inference_backend_t backend)
Plugin C API: promote backend from WARM to ACTIVE (GPU load).
ENTROPIC_EXPORT int entropic_inference_state(entropic_inference_backend_t backend)
Plugin C API: query current lifecycle state (lock-free).
LlamaCppBackend — llama.cpp C API integration.
spdlog initialization and logger access.
ENTROPIC_EXPORT std::shared_ptr< spdlog::logger > get(const std::string &name)
Get or create a named logger.
static char * alloc_string(const std::string &s)
Allocate a C string copy for caller-owned return.
Shared parser: messages-JSON wire format → vector<Message>.
std::vector< Message > parse_messages_json(const char *json_str)
Parse a JSON array of messages into a vector of Message.
Generation parameters for a single inference call.
std::string grammar
GBNF grammar string (empty = unconstrained)
std::unordered_map< int32_t, float > logit_bias
Per-token logit bias map (gh#23 MVP item 4).
float repeat_penalty
Repetition penalty.
float temperature
Sampling temperature.
float frequency_penalty
Frequency-penalty term in llama.cpp's penalties sampler (gh#23 MVP item 3).
float presence_penalty
Presence-penalty term in llama.cpp's penalties sampler (gh#23 MVP item 2).
float min_p
Min-p nucleus sampling threshold (gh#23 MVP item 1).
int max_tokens
Maximum tokens to generate.
float top_p
Nucleus sampling threshold.
Result of a single generation call.
entropic_error_t error_code
Error code (ENTROPIC_OK if no error)
double generation_time_ms
Wall-clock generation time.
std::string finish_reason
Finish reason: "stop", "length", "error".
std::string content
Generated text (cleaned by adapter)
std::string error_message
Error description (empty if no error)
int token_count
Generated token count.
Model configuration for a single tier.
int gpu_layers
GPU offload layers (-1 = all)
int context_length
Context window size (512–131072)
std::filesystem::path path
Resolved model file path.
int n_threads
CPU threads (0 = auto-detect)
bool keep_warm
Pre-warm model at startup.
int n_batch
Batch size for prompt processing.
bool flash_attn
Enable flash attention.
bool use_mlock
Lock model in system RAM.
std::string adapter
Chat adapter name.