Skip to content

YAMS Plugin ABI Specification

Core C-ABI Plugin Interface

The YAMS plugin system is built around a stable C ABI that enables plugins to expose services to the host and optionally call back into host services.

Plugin Entry Points

Every plugin MUST implement the following symbols:

// Return the plugin ABI version (always 1)
int yams_plugin_get_abi_version(void);

// Return the plugin name (static string, lifetime of plugin)
const char* yams_plugin_get_name(void);

// Return the plugin version (static string, lifetime of plugin)  
const char* yams_plugin_get_version(void);

// Return the plugin manifest as JSON (static string, lifetime of plugin)
const char* yams_plugin_get_manifest_json(void);

// Get an interface vtable by ID and version
// Returns 0 on success, non-zero on error/interface not found
int yams_plugin_get_interface(const char* iface_id, uint32_t version, void** out_iface);

// Initialize the plugin with config JSON and optional host context
// Returns 0 on success, non-zero error code on failure
int yams_plugin_init(const char* config_json, const void* host_context);

// Shutdown the plugin (cleanup resources)
void yams_plugin_shutdown(void);

// Optional: Get health status as JSON (allocated with malloc, host frees)
// Returns 0 on success, non-zero if not implemented
int yams_plugin_get_health_json(char** out_json);

Memory Management

  • Plugin-allocated memory: For any function that returns allocated memory (like get_health_json), the plugin MUST use malloc()/calloc(). The host is responsible for calling free().
  • Host-allocated memory: When the host provides data to plugins via callbacks, the host allocates and frees the memory.
  • Interface vtables: Interface pointers returned by get_interface have static lifetime for the duration of the plugin load.

Error Codes

Plugins SHOULD use standard error codes:

#define YAMS_PLUGIN_OK 0
#define YAMS_PLUGIN_ERR_INVALID 1
#define YAMS_PLUGIN_ERR_INCOMPATIBLE 2
#define YAMS_PLUGIN_ERR_NOT_FOUND 3
#define YAMS_PLUGIN_ERR_IO 4
#define YAMS_PLUGIN_ERR_INTERNAL 5
#define YAMS_PLUGIN_ERR_UNSUPPORTED 6

Interface VTables

Standard Interface Pattern

All interfaces follow this pattern:

typedef struct iface_name_v1 {
    void* handle;  // Plugin-specific context
    // Interface methods...
} iface_name_v1_t;

Required Interfaces

model_provider_v1

Header: include/yams/plugins/model_provider_v1.h

typedef struct model_provider_v1 {
    void* handle;

    // Set progress callback for long operations (optional)
    int (*set_progress_callback)(void* handle, 
                                 void (*callback)(void* user_data, const char* phase, int progress, int total),
                                 void* user_data);

    // Load a model (non-blocking)
    int (*load_model)(void* handle, const char* model_id, const char* model_path, const char* options_json);

    // Unload a model
    int (*unload_model)(void* handle, const char* model_id);

    // Check if model is loaded
    int (*is_model_loaded)(void* handle, const char* model_id, bool* out_loaded);

    // Get list of loaded models (plugin allocates arrays, host frees)
    int (*get_loaded_models)(void* handle, char*** out_ids, uint32_t* out_count);
    void (*free_model_list)(void* handle, char** ids, uint32_t count);

    // Generate single embedding
    int (*generate_embedding)(void* handle, const char* model_id, 
                             const uint8_t* input, size_t input_len,
                             float** out_vec, uint32_t* out_dim);
    void (*free_embedding)(void* handle, float* vec, uint32_t dim);

    // Generate batch embeddings
    int (*generate_embedding_batch)(void* handle, const char* model_id,
                                   const uint8_t** inputs, const size_t* lens, uint32_t batch,
                                   float*** out_vecs, uint32_t* out_batch, uint32_t* out_dim);
    void (*free_embedding_batch)(void* handle, float** vecs, uint32_t batch, uint32_t dim);

    // v1.2 extensions (optional, may return UNSUPPORTED)
    int (*get_embedding_dim)(void* handle, const char* model_id, uint32_t* out_dim);
    int (*get_runtime_info_json)(void* handle, const char* model_id, char** out_json);
    void (*free_string)(void* handle, char* str);
    int (*set_threading)(void* handle, const char* model_id, uint32_t intra_threads, uint32_t inter_threads);
} model_provider_v1_t;

content_extractor_v1

Header: include/yams/plugins/content_extractor_v1.h

typedef struct content_extractor_v1 {
    void* handle;

    // Extract content from a document
    int (*extract_content)(void* handle, const char* file_path,
                           char** out_text, char** out_metadata_json);
    void (*free_extracted_content)(void* handle, char* text, char* metadata);

    // Get supported file extensions (plugin allocates, host frees)
    int (*get_supported_extensions)(void* handle, char*** out_extensions, uint32_t* out_count);
    void (*free_extension_list)(void* handle, char** extensions, uint32_t count);
} content_extractor_v1_t;

Host Services API (Optional)

Plugins that need to call back into YAMS can use the Host Services API.

Host Context

During yams_plugin_init, the host MAY pass a yams_plugin_host_context_v1 struct:

typedef struct yams_plugin_host_context_v1 {
    uint32_t version;  // YAMS_PLUGIN_HOST_SERVICES_API_VERSION
    const document_service_v1* document_service;
    const search_service_v1* search_service;
    // Additional services may be added in future versions
} yams_plugin_host_context_v1_t;

Document Service v1

typedef struct document_service_v1 {
    void* handle;

    int (*list_documents)(void* handle, const list_documents_request_t* req,
                         list_documents_response_t** out_res);
    void (*free_list_documents_response)(void* handle, list_documents_response_t* res);

    int (*get_document)(void* handle, const get_document_request_t* req,
                       get_document_response_t** out_res);
    void (*free_get_document_response)(void* handle, get_document_response_t* res);

    // Additional methods...
} document_service_v1_t;

Plugin Manifest

Each plugin MUST provide a JSON manifest via yams_plugin_get_manifest_json():

{
  "name": "plugin_name",
  "version": "1.0.0",
  "description": "Brief description",
  "interfaces": [
    {
      "id": "model_provider_v1",
      "version": 2
    }
  ],
  "abi_version": 1,
  "capabilities": [],
  "dependencies": []
}

Lifecycle

  1. Discovery: Host scans for plugin files and reads symbols without side effects
  2. Validation: Host verifies ABI version, manifest format, and required interfaces
  3. Initialization: Host calls yams_plugin_init() with config and optional host context
  4. Interface Registration: Host retrieves interface vtables via yams_plugin_get_interface()
  5. Operation: Host calls into plugin interfaces as needed
  6. Shutdown: Host calls yams_plugin_shutdown() before unloading

Security Considerations

  • Plugins run in the same process space as the host
  • Host should validate plugin signatures and maintain a trust list
  • Plugins should validate all input parameters and handle errors gracefully
  • Memory safety is critical - use proper bounds checking and null checks

Threading

  • Plugin initialization and shutdown are single-threaded
  • Interface methods MAY be called from multiple threads concurrently
  • Plugins MUST implement their own synchronization if needed
  • Callbacks MAY be invoked from background threads

Versioning

  • Plugin ABI version is incremented for breaking changes
  • Interface versions are incremented independently
  • Host should support multiple interface versions in parallel
  • Plugins should advertise the highest interface version they support

Best Practices

  • Use static allocation for fixed-size data structures
  • Validate all pointers before dereferencing
  • Provide meaningful error messages
  • Implement graceful degradation for optional features
  • Use consistent naming conventions across interfaces
  • Document thread safety guarantees for each method