spacedrive/crates/sdk/API_REFERENCE.md
2025-10-11 08:48:11 -07:00

14 KiB

Spacedrive SDK API Reference

Status: Stubs for type-checking (implementations are todo!()) Purpose: Full API surface from docs/sdk/sdk.md - allows extensions to compile


Overview

The SDK now includes all APIs documented in the specification as type-checked stubs.

Modules

spacedrive-sdk/
├── actions.rs       - Action preview/execute system
├── agent.rs         - Agent context and memory
├── ai.rs            - AI models and inference
├── ffi.rs           - Low-level WASM imports (existing)
├── job_context.rs   - Job execution context (expanded)
├── models.rs        - Model registration
├── query.rs         - Query context
├── tasks.rs         - Task execution context
├── types.rs         - Common types (expanded)
└── vdfs.rs          - VDFS queries and operations

Core Types (types.rs)

// Results
pub type Result<T> = std::result::Result<T, Error>;
pub type AgentResult<T> = std::result::Result<T, Error>;
pub type JobResult<T> = std::result::Result<T, Error>;
pub type QueryResult<T> = std::result::Result<T, Error>;
pub type TaskResult<T> = std::result::Result<T, Error>;

// Core entities
pub struct Entry { id, uuid, name, kind, ... }
pub struct Tag { id, name, color, icon }
pub enum EntryKind { File, Directory, Symlink, Virtual }
pub enum Priority { Low, Normal, High }
pub enum Capability { GPU, CPU }
pub enum Progress { Indeterminate, Simple, Complete }

// Type markers
pub struct Image;  // For .of_type::<Image>()
pub struct Pdf;

VDFS Operations (vdfs.rs)

impl VdfsContext {
    // Entry queries
    fn query_entries() -> EntryQuery
    async fn get_entry(uuid) -> Result<Entry>

    // Model operations (content-scoped)
    async fn create_model_for_content<T>(content_uuid, model) -> Result<()>
    async fn get_model_by_content<T>(content_uuid) -> Result<T>
    async fn update_model_by_content<T, F>(content_uuid, f) -> Result<()>

    // Model operations (standalone)
    async fn create_model<T>(model) -> Result<()>
    async fn get_model<T>(uuid) -> Result<T>
    fn query_models<T>() -> ModelQuery<T>

    // Tagging
    async fn add_tag_to_content(content_uuid, tag) -> Result<()>
    async fn add_tag_to_model(model_uuid, tag) -> Result<()>
    async fn add_tag(metadata_id, tag) -> Result<()>

    // Custom fields
    async fn update_custom_field<T>(entry_uuid, field, value) -> Result<()>

    // Permissions
    fn in_granted_scope(path) -> bool
}

// Entry query builder
impl EntryQuery {
    fn in_location(path) -> Self
    fn of_type<T>() -> Self
    fn where_content_id(content_uuid) -> Self
    fn on_this_device() -> Self
    fn with_tag(tag) -> Self
    fn with_sidecar(kind) -> Self
    async fn first() -> Result<Option<Entry>>
    async fn collect() -> Result<Vec<Entry>>
    fn map<F, T>(f) -> MappedQuery<T>
}

// Model query builder
impl ModelQuery<T> {
    fn where_field(field, predicate) -> Self
    fn where_json_field(path, predicate) -> Self
    fn search_semantic(field, query) -> Self
    async fn first() -> Result<Option<T>>
    async fn collect() -> Result<Vec<T>>
}

// Predicates
fn equals<T>(value) -> FieldPredicate
fn contains(value) -> FieldPredicate
fn is_not_null() -> FieldPredicate
fn similar_to(query) -> SemanticQuery

AI Operations (ai.rs)

impl AiContext {
    fn from_registered(model_id: &str) -> ModelHandle
    fn with_model(preference: &str) -> ModelHandle
}

impl ModelHandle {
    fn prompt_template(template_name: &str) -> PromptBuilder
    async fn detect_faces(image_data: &[u8]) -> Result<Vec<FaceDetection>>
    async fn classify(image_data: &[u8]) -> Result<Vec<SceneTag>>
    async fn ocr_document(entry: &Entry) -> Result<String>
    async fn embed_text(text: &str) -> Result<Vec<f32>>
}

impl PromptBuilder {
    fn render_with<T: Serialize>(context: &T) -> Result<RenderedPrompt>
}

impl RenderedPrompt {
    async fn generate_text() -> Result<String>
    async fn generate_json<T>() -> Result<T>
}

// AI types
pub struct FaceDetection { bbox, confidence, embedding, identified_as }
pub struct BoundingBox { x, y, width, height }
pub struct SceneTag { label, confidence }

Agent System (agent.rs)

impl AgentContext<M> {
    fn vdfs() -> VdfsContext
    fn ai() -> AiContext
    fn models() -> ModelContext
    fn jobs() -> JobDispatcher
    fn memory() -> MemoryHandle<M>
    fn trace(message)
    fn in_granted_scope(path) -> bool
    fn config<C>() -> &C
    fn notify() -> NotificationBuilder
}

impl JobDispatcher {
    fn dispatch<J, A>(job, args) -> JobDispatchBuilder
}

impl JobDispatchBuilder {
    fn priority(priority) -> Self
    fn when_idle() -> Self
    fn on_device_with_capability(cap) -> Self
    async fn await() -> Result<()>
}

impl NotificationBuilder {
    fn message(msg) -> Self
    fn on_active_device() -> Self
    fn with_title(title) -> Self
    async fn send() -> Result<()>
}

// Memory types
pub struct TemporalMemory<T> {
    async fn append(event: T) -> Result<()>
    fn query() -> TemporalQuery<T>
}

pub struct AssociativeMemory<T> {
    async fn add(knowledge: T) -> Result<()>
    fn query() -> AssociativeQuery<T>
    fn query_similar(query: &str) -> AssociativeQuery<T>
}

pub struct WorkingMemory<T> {
    async fn read() -> T
    async fn update<F>(f: F) -> Result<()>
}

// Query builders
impl TemporalQuery<T> {
    fn where_variant<V>(variant) -> Self
    fn since(duration) -> Self
    fn where_field(field, predicate) -> Self
    fn where_semantic(field, query) -> Self
    fn sort_by<F>(f) -> Self
    fn limit(n) -> Self
    async fn collect() -> Result<Vec<T>>
}

impl AssociativeQuery<T> {
    fn where_variant<V>(variant) -> Self
    fn where_field(field, predicate) -> Self
    fn min_similarity(threshold) -> Self
    fn top_k(k) -> Self
    fn within_context<U>(context: &[U]) -> Self
    fn and_related_concepts(depth) -> Self
    async fn collect() -> Result<Vec<T>>
}

pub trait AgentMemory: Send + Sync {}

Job Context (job_context.rs)

impl JobContext {
    // Existing (working in test-extension)
    fn report_progress(progress: f32, message: &str)
    fn checkpoint<S: Serialize>(state: &S) -> Result<()>
    fn check_interrupt() -> bool
    fn add_warning(message: &str)
    fn increment_bytes(bytes: u64)
    fn increment_items(count: u64)
    fn log(message: &str)
    fn log_error(message: &str)

    // NEW
    fn vdfs() -> VdfsContext
    fn ai() -> AiContext
    fn models() -> ModelContext
    async fn run<F, A, R>(task: F, args: A) -> Result<R>
    fn progress(progress: Progress)
    async fn check_interrupt() -> Result<()>  // Async version
    fn sidecar_exists(content_uuid, kind) -> Result<bool>
    async fn save_sidecar<T>(content_uuid, kind, extension_id, data) -> Result<()>
    fn memory() -> MemoryHandle<()>
    fn config<C>() -> &C
    fn notify() -> NotificationBuilder
}

Task Context (tasks.rs)

impl TaskContext {
    fn vdfs() -> VdfsContext
    fn ai() -> AiContext
    fn config<C>() -> &C
    async fn read_sidecar<T>(content_uuid, kind) -> Result<T>
}

Action Context (actions.rs)

impl ActionContext {
    fn vdfs() -> VdfsContext
}

pub struct ActionPreview {
    pub title: String,
    pub description: String,
    pub changes: Vec<Change>,
    pub reversible: bool,
}

pub enum Change {
    CreateModel { model_type, data },
    UpdateModel { model_id, field, operation, value },
    UpdateCustomField { entry_id, field, value },
    AddTag { target, tag },
    CreateDirectory { name, parent },
    MoveEntry { entry, destination },
}

pub struct ExecutionResult {
    pub success: bool,
    pub message: String,
}

Query Context (query.rs)

impl QueryContext<M> {
    fn vdfs() -> VdfsContext
    fn memory() -> MemoryHandle<M>
}

Model Registration (models.rs)

impl ModelContext {
    async fn register(category, name, source) -> Result<ModelId>
    fn is_registered(model_id: &str) -> bool
}

pub enum ModelSource {
    Bundled(Vec<u8>),
    Download { url, sha256 },
    Ollama(String),
}

pub struct ModelId {
    pub category: String,
    pub name: String,
}

pub trait ExtensionModel: Serialize + DeserializeOwned + Send + Sync {
    const MODEL_TYPE: &'static str;
    fn uuid(&self) -> Uuid;
}

Macros (spacedrive-sdk-macros)

All macros are currently pass-through stubs:

#[extension(id = "...", permissions = [...])]  // Generates plugin_init, metadata
#[model(version = "...", scope = "...")]       // Generates ExtensionModel impl
#[agent]                                        // Generates agent registration
#[agent_memory]                                 // Generates AgentMemory impl
#[job(parallelism = 4)]                        // Generates FFI exports (working)
#[task(retries = 3)]                           // Generates task wrapper
#[action]                                       // Generates action exports
#[query("pattern")]                             // Generates query exports

Usage Examples

Photos Extension (Now Type-Checks!)

#[model(scope = "content")]
struct PhotoAnalysis {
    detected_faces: Vec<FaceDetection>,
    // ... compiles!
}

#[job]
async fn analyze_photos(ctx: &JobContext, content_uuids: Vec<Uuid>) -> JobResult<()> {
    for content_uuid in content_uuids {
        let entry = ctx.vdfs()  // Type-checks
            .query_entries()
            .where_content_id(content_uuid)
            .first()
            .await?;

        let faces = ctx.ai()  // Type-checks
            .from_registered("face_detection")
            .detect_faces(&image_data)
            .await?;

        ctx.vdfs().create_model_for_content(content_uuid, analysis).await?;  // Type-checks
    }
    Ok(())
}

Test Extension (Still Works!)

#[job(name = "counter")]
fn test_counter(ctx: &JobContext, state: &mut CounterState) -> Result<()> {
    ctx.log("Working...");  // Still works
    ctx.checkpoint(state)?;  // Still works
    Ok(())
}

Implementation Status

Module Status Notes
ffi.rs Implemented Low-level WASM imports
job_context.rs Expanded New methods added, existing preserved
types.rs Expanded All common types added
vdfs.rs Stubs Type-checks, todo!() for host calls
ai.rs Stubs Type-checks, todo!() for inference
agent.rs Stubs Type-checks, memory system defined
models.rs Stubs Type-checks, registration stubs
actions.rs Stubs Type-checks, preview/execute defined
tasks.rs Stubs Type-checks, task context defined
query.rs Stubs Type-checks, query context defined

Macros: All defined as pass-through (no codegen yet)


What Works

Type-checking: Photos extension compiles and type-checks Test extension: Existing test-extension still works API surface: Complete API from specification available Documentation: IntelliSense/rust-analyzer autocomplete works

What Doesn't Work Yet

Runtime: All new methods are todo!() - will panic if called Host functions: WASM imports not implemented in Core Macro codegen: Macros don't generate code yet Memory persistence: No storage backend


Next Steps for Implementation

Phase 1: Core Host Functions

Implement in core/src/infra/extension/host_functions.rs:

#[no_mangle]
pub extern "C" fn vdfs_query_entries(...) -> u32;
#[no_mangle]
pub extern "C" fn model_create(...) -> u32;
#[no_mangle]
pub extern "C" fn model_get_by_content(...) -> u32;
#[no_mangle]
pub extern "C" fn add_tag_to_content(...) -> u32;
#[no_mangle]
pub extern "C" fn model_register(...) -> u32;
#[no_mangle]
pub extern "C" fn ai_infer(...) -> u32;

Phase 2: SDK Implementation

Replace todo!() with actual WASM host calls:

pub async fn get_model_by_content<T>(content_uuid: Uuid) -> Result<T> {
    // Serialize request
    let request = ModelRequest { content_uuid, model_type: T::MODEL_TYPE };
    let req_bytes = serde_json::to_vec(&request)?;

    // Call host function
    let result_ptr = unsafe {
        model_get_by_content(req_bytes.as_ptr(), req_bytes.len())
    };

    // Deserialize response
    let response_bytes = unsafe { read_host_memory(result_ptr) };
    let model: T = serde_json::from_slice(&response_bytes)?;

    Ok(model)
}

Phase 3: Macro Code Generation

Implement real macros:

#[model(scope = "content")]
struct PhotoAnalysis { ... }

// Generates:
impl ExtensionModel for PhotoAnalysis {
    const MODEL_TYPE: &'static str = "PhotoAnalysis";
    fn uuid(&self) -> Uuid { self.id }
}

impl PhotoAnalysis {
    pub async fn save_for_content(ctx: &VdfsContext, content_uuid: Uuid, self) -> Result<()> {
        ctx.create_model_for_content(content_uuid, self).await
    }
}

Testing

Compile Test

cd crates/sdk
cargo check
# Should compile (all stubs)

Extension Test

cd extensions/photos
cargo check --target wasm32-unknown-unknown
# Should type-check (won't run, but compiles)

Runtime Test

cd extensions/test-extension
cargo build --target wasm32-unknown-unknown --release
# Should build and run (uses only implemented methods)

Breaking Changes

None! The existing test-extension API is preserved:

  • ctx.log()
  • ctx.checkpoint()
  • ctx.check_interrupt()
  • ctx.report_progress()
  • ctx.increment_items()
  • ctx.increment_bytes()

New methods are additive only.


The SDK is now complete for type-checking. Extensions can be written and will compile. Runtime implementation is the next phase.