|
Csilk 0.2.1
A lightweight, high-performance C HTTP web framework
|
AI unified interface engine implementation. More...
#include "csilk/drivers/ai.h"#include <ctype.h>#include <stdatomic.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <time.h>#include <uv.h>#include "cJSON.h"#include "csilk/csilk.h"
Data Structures | |
| struct | csilk_ai_t |
| Opaper AI engine handle wrapping a single driver backend. Each instance pairs a driver vtable with its private initialization state (API credentials, connection pool, etc.). Created via csilk_ai_new() and freed via csilk_ai_free(). Not thread-safe for concurrent use from multiple threads. More... | |
| struct | async_chat_req_t |
| Per-async-chat-request context passed between the work callback (on a thread-pool thread) and the after-work callback (on the main loop thread). Keeps the response on the heap so it survives across threads without data races on the caller's stack. More... | |
| struct | async_emb_req_t |
| Per-async-embedding-request context passed between the work callback (thread-pool thread) and after-work callback (main loop). More... | |
Macros | |
| #define | MAX_DRIVERS 8 |
| Maximum number of concurrently registered AI drivers. | |
Functions | |
| static void | ai_ensure_monitor_init (void) |
| static void | _ai_broadcast (const char *event, const char *model, int status, int prompt_tokens, int completion_tokens, uint64_t duration_us, const char *error) |
| void | csilk_ai_get_stats (csilk_ai_stats_t *stats) |
| Get current AI engine statistics. | |
| char * | csilk_ai_stats_to_json (const csilk_ai_stats_t *stats) |
| Convert AI statistics to a JSON string. | |
| void | csilk_ai_register_monitor (void *c) |
| Register a WebSocket monitor for real-time AI events. | |
| void | csilk_ai_register_driver (const csilk_ai_driver_t *driver) |
| Register an AI driver implementation in the global registry. Called during driver module initialization (e.g., csilk_ai_openai_init_driver()). Silently ignores registration if the registry is full. | |
| static const csilk_ai_driver_t * | find_driver (const char *name) |
| Linear search of the global driver registry by name. | |
| void | csilk_ai_openai_init_driver (void) |
| Register the OpenAI driver with the AI subsystem. Called during startup to make "openai" available to csilk_ai_new(). | |
| void | csilk_ai_ollama_init_driver (void) |
| Register the Ollama driver with the AI subsystem. Called during startup to make "ollama" available to csilk_ai_new(). | |
| csilk_ai_t * | csilk_ai_new (const char *driver_name, const char *api_key, const char *base_url) |
| Create a new AI engine instance bound to a specific driver backend. | |
| int | csilk_ai_chat (csilk_ai_t *ai, const csilk_ai_chat_request_t *req, csilk_ai_chat_response_t *res) |
| Send a chat completion request with automatic retry on transient failures. | |
| static void | chat_work_cb (uv_work_t *req) |
| libuv thread-pool work callback — runs csilk_ai_chat() off the main loop thread. The response is stored in the heap-allocated async context. | |
| static void | chat_after_work_cb (uv_work_t *req, int status) |
| libuv after-work callback — delivers the result on the main loop thread via the user's callback, then frees the async context. | |
| void | csilk_ai_chat_async (csilk_ai_t *ai, const csilk_ai_chat_request_t *req, csilk_ai_chat_async_cb cb, void *user_data) |
| Send a chat completion request asynchronously on the libuv thread pool. | |
| int | csilk_ai_embeddings (csilk_ai_t *ai, const char *model, const char **input, size_t count, csilk_ai_embeddings_response_t *res) |
| Generate embeddings for a batch of input strings. | |
| static void | emb_work_cb (uv_work_t *req) |
| libuv thread-pool work callback for async embeddings. Runs csilk_ai_embeddings() off the main loop thread, storing the result in the heap-allocated async context. | |
| static void | emb_after_work_cb (uv_work_t *req, int status) |
| libuv after-work callback for async embeddings — delivers the result on the main loop thread via the user's callback, then frees the async context. | |
| void | csilk_ai_embeddings_async (csilk_ai_t *ai, const char *model, const char **input, size_t count, csilk_ai_embeddings_async_cb cb, void *user_data) |
| Generate embeddings asynchronously on the libuv thread pool. | |
| void | csilk_ai_free (csilk_ai_t *ai) |
| Free an AI engine handle and its driver state. Calls the driver's free() callback first, then frees the handle. | |
| void | csilk_ai_chat_response_free (csilk_ai_chat_response_t *res) |
| Free all dynamically allocated fields in a chat response. Frees content, tool calls (with their id/name/arguments), raw_response, and error_message. Does NOT free the struct itself. | |
| void | csilk_ai_embeddings_response_free (csilk_ai_embeddings_response_t *res) |
| Free dynamically allocated fields in an embeddings response. Frees values array and error_message. Does NOT free the struct itself. | |
| csilk_ai_context_t * | csilk_ai_context_new (size_t max_history) |
| Create a new conversation context with sliding-window history. | |
| void | csilk_ai_context_add (csilk_ai_context_t *ctx, const char *role, const char *content) |
| Add a message to the conversation context with sliding-window eviction. | |
| void | csilk_ai_context_clear (csilk_ai_context_t *ctx) |
| Clear all messages from the conversation context. Frees each message's role and content strings and resets the message count to zero. The internal array capacity is preserved to avoid repeated reallocation. | |
| void | csilk_ai_context_free (csilk_ai_context_t *ctx) |
| Free a conversation context and all associated resources. Clears messages, frees the message array, and frees the context struct. | |
Variables | |
| static atomic_uint_fast64_t | ai_requests_total = 0 |
| static atomic_uint_fast64_t | ai_tokens_total = 0 |
| static atomic_uint_fast64_t | ai_prompt_tokens = 0 |
| static atomic_uint_fast64_t | ai_completion_tokens = 0 |
| static atomic_uint_fast64_t | ai_errors_total = 0 |
| static atomic_uint_fast64_t | ai_duration_us_total = 0 |
| static csilk_ctx_t * | g_ai_monitors [16] |
| static size_t | g_ai_monitor_count = 0 |
| static uv_mutex_t | g_ai_monitor_mutex |
| static int | g_ai_monitor_init = 0 |
| static const csilk_ai_driver_t * | g_drivers [MAX_DRIVERS] |
| Global registry of AI driver implementations. Populated once during the first csilk_ai_new() call via lazy init of the built-in drivers (OpenAI, Ollama). Not thread-safe for concurrent registration — all registration happens during the single-threaded startup phase. | |
| static size_t | g_driver_count = 0 |
AI unified interface engine implementation.
Architecture: Facade pattern over pluggable AI driver backends (OpenAI, Ollama, etc.). The global driver registry is populated once at first use via lazy initialization. Each csilk_ai_t instance wraps a single driver with its private state.
The module provides both synchronous (blocking) and asynchronous (libuv thread-pool) variants for chat completions and embeddings. Chat requests include automatic retry with exponential backoff for transient failures (network errors, rate limits, server errors).
A context helper manages sliding-window conversation history for multi-turn interactions. Memory for async operations is heap-allocated and freed in the after-work callback on the main loop thread.
| struct csilk_ai_s |
Opaper AI engine handle wrapping a single driver backend. Each instance pairs a driver vtable with its private initialization state (API credentials, connection pool, etc.). Created via csilk_ai_new() and freed via csilk_ai_free(). Not thread-safe for concurrent use from multiple threads.
Opaque handle for an AI provider instance.

| Data Fields | ||
|---|---|---|
| const csilk_ai_driver_t * | driver |
Driver vtable (init, chat, embeddings, free). |
| void * | driver_state |
Driver-private state (API key, base URL, etc.). |
| struct async_chat_req_t |
Per-async-chat-request context passed between the work callback (on a thread-pool thread) and the after-work callback (on the main loop thread). Keeps the response on the heap so it survives across threads without data races on the caller's stack.

| Data Fields | ||
|---|---|---|
| csilk_ai_t * | ai |
AI engine handle. |
| csilk_ai_chat_async_cb | cb |
Completion callback. |
| const csilk_ai_chat_request_t * | req |
Request parameters (caller-owned). |
| csilk_ai_chat_response_t | res |
Response buffer (filled by worker). |
| int | status |
Result code from csilk_ai_chat(). |
| void * | user_data |
Opaque user context for callback. |
| struct async_emb_req_t |
Per-async-embedding-request context passed between the work callback (thread-pool thread) and after-work callback (main loop).

| Data Fields | ||
|---|---|---|
| csilk_ai_t * | ai | |
| csilk_ai_embeddings_async_cb | cb | |
| size_t | count | |
| const char ** | input | |
| const char * | model | |
| csilk_ai_embeddings_response_t | res | |
| int | status | |
| void * | user_data | |
| #define MAX_DRIVERS 8 |
Maximum number of concurrently registered AI drivers.
|
static |
|
static |
|
static |
libuv after-work callback — delivers the result on the main loop thread via the user's callback, then frees the async context.
|
static |
libuv thread-pool work callback — runs csilk_ai_chat() off the main loop thread. The response is stored in the heap-allocated async context.
| int csilk_ai_chat | ( | csilk_ai_t * | ai, |
| const csilk_ai_chat_request_t * | req, | ||
| csilk_ai_chat_response_t * | res | ||
| ) |
Send a chat completion request with automatic retry on transient failures.
Perform a chat completion.
Algorithm:
| ai | AI engine handle (must not be NULL). |
| req | Chat request parameters (model, messages, temperature, tools). |
| res | [out] Receives the chat response, including content, tool calls, and token usage. Zeroed on each retry attempt. |
| void csilk_ai_chat_async | ( | csilk_ai_t * | ai, |
| const csilk_ai_chat_request_t * | req, | ||
| csilk_ai_chat_async_cb | cb, | ||
| void * | user_data | ||
| ) |
Send a chat completion request asynchronously on the libuv thread pool.
Perform an asynchronous chat completion.
Allocates a work request and an async context on the heap, queues the work via uv_queue_work(), and returns immediately. The callback fires on the main loop thread after the driver's chat() completes. The response is valid only during the callback invocation.
Ownership: The caller retains ownership of req. The response res is owned by the async context and is freed after the callback returns. If the caller needs the response beyond the callback, it must deep-copy it.
| ai | AI engine handle. |
| req | Chat request (must remain valid until the callback fires). |
| cb | Completion callback (required). |
| user_data | Opaque pointer passed through to the callback. |
| void csilk_ai_chat_response_free | ( | csilk_ai_chat_response_t * | res | ) |
Free all dynamically allocated fields in a chat response. Frees content, tool calls (with their id/name/arguments), raw_response, and error_message. Does NOT free the struct itself.
Free a chat response structure.
| res | Response struct to clean (may be NULL). Safe to call on a zero-initialized struct. |
| void csilk_ai_context_add | ( | csilk_ai_context_t * | ctx, |
| const char * | role, | ||
| const char * | content | ||
| ) |
Add a message to the conversation context with sliding-window eviction.
Add a message to the context.
Algorithm:
| ctx | Conversation context. |
| role | Message role (e.g., "user", "assistant", "system"). |
| content | Message content text. |
| void csilk_ai_context_clear | ( | csilk_ai_context_t * | ctx | ) |
Clear all messages from the conversation context. Frees each message's role and content strings and resets the message count to zero. The internal array capacity is preserved to avoid repeated reallocation.
Clear all messages from the context.
| ctx | Conversation context (may be NULL). |
| void csilk_ai_context_free | ( | csilk_ai_context_t * | ctx | ) |
Free a conversation context and all associated resources. Clears messages, frees the message array, and frees the context struct.
Free a conversation context.
| ctx | Context to free (may be NULL). |
| csilk_ai_context_t * csilk_ai_context_new | ( | size_t | max_history | ) |
Create a new conversation context with sliding-window history.
Initialize a new conversation context.
Allocates a context struct that manages a rolling window of message history. When max_history messages are reached, the oldest message is evicted on each new add().
| max_history | Maximum number of messages to retain (0 = unlimited). |
| int csilk_ai_embeddings | ( | csilk_ai_t * | ai, |
| const char * | model, | ||
| const char ** | input, | ||
| size_t | count, | ||
| csilk_ai_embeddings_response_t * | res | ||
| ) |
Generate embeddings for a batch of input strings.
Generate embeddings for the given input strings.
Checks that the driver supports embeddings (optional operation), then delegates to the driver's embeddings() implementation.
| ai | AI engine handle. |
| model | Model name (e.g., "text-embedding-3-small"). |
| input | Array of input strings to embed. |
| count | Number of input strings. |
| res | [out] Receives the embeddings values and error message. |
| void csilk_ai_embeddings_async | ( | csilk_ai_t * | ai, |
| const char * | model, | ||
| const char ** | input, | ||
| size_t | count, | ||
| csilk_ai_embeddings_async_cb | cb, | ||
| void * | user_data | ||
| ) |
Generate embeddings asynchronously on the libuv thread pool.
Generate embeddings asynchronously.
Allocates a work request and async context on the heap, queues via uv_queue_work(), and returns immediately. The callback fires on the main loop thread after completion.
| ai | AI engine handle. |
| model | Model name. |
| input | Array of input strings (must remain valid until callback). |
| count | Number of input strings. |
| cb | Completion callback (required). |
| user_data | Opaque pointer passed through to callback. |
| void csilk_ai_embeddings_response_free | ( | csilk_ai_embeddings_response_t * | res | ) |
Free dynamically allocated fields in an embeddings response. Frees values array and error_message. Does NOT free the struct itself.
Free an embeddings response structure.
| res | Response struct to clean (may be NULL). |
| void csilk_ai_free | ( | csilk_ai_t * | ai | ) |
Free an AI engine handle and its driver state. Calls the driver's free() callback first, then frees the handle.
Free an AI handle.
| ai | The handle to free (may be NULL). |
| void csilk_ai_get_stats | ( | csilk_ai_stats_t * | stats | ) |
Get current AI engine statistics.
| stats | [out] Pointer to stats struct to populate. |
| csilk_ai_t * csilk_ai_new | ( | const char * | driver_name, |
| const char * | api_key, | ||
| const char * | base_url | ||
| ) |
Create a new AI engine instance bound to a specific driver backend.
Create a new AI instance with a specific driver.
On the very first call, lazy-initializes the built-in driver registry (OpenAI and Ollama). Subsequent calls reuse the already-registered drivers. If the named driver is not found or its init() fails, returns NULL.
| driver_name | Backend name (e.g., "openai", "ollama"). |
| api_key | API key for authentication (e.g., OpenAI API key). |
| base_url | Optional custom base URL (NULL for driver default). |
|
extern |
Register the Ollama driver with the AI subsystem. Called during startup to make "ollama" available to csilk_ai_new().
|
extern |
Register the OpenAI driver with the AI subsystem. Called during startup to make "openai" available to csilk_ai_new().
| void csilk_ai_register_driver | ( | const csilk_ai_driver_t * | driver | ) |
Register an AI driver implementation in the global registry. Called during driver module initialization (e.g., csilk_ai_openai_init_driver()). Silently ignores registration if the registry is full.
Register a new AI driver.
| driver | Driver vtable with name, init, chat, embeddings, free. |
| void csilk_ai_register_monitor | ( | void * | c | ) |
Register a WebSocket monitor for real-time AI events.
| c | Framework context (WebSocket connection). |
| char * csilk_ai_stats_to_json | ( | const csilk_ai_stats_t * | stats | ) |
Convert AI statistics to a JSON string.
| stats | Pointer to stats struct. |
|
static |
libuv after-work callback for async embeddings — delivers the result on the main loop thread via the user's callback, then frees the async context.
|
static |
libuv thread-pool work callback for async embeddings. Runs csilk_ai_embeddings() off the main loop thread, storing the result in the heap-allocated async context.
|
static |
Linear search of the global driver registry by name.
| name | Driver name (e.g., "openai", "ollama"). |
|
static |
|
static |
|
static |
|
static |
|
static |
|
static |
|
static |
|
static |
|
static |
|
static |
|
static |
|
static |
Global registry of AI driver implementations. Populated once during the first csilk_ai_new() call via lazy init of the built-in drivers (OpenAI, Ollama). Not thread-safe for concurrent registration — all registration happens during the single-threaded startup phase.