npm package: @informedai/sync
Description: Sync data from your backend to InformedAI knowledge library
Author: Marolence
Latest version: 0.3.1

# @informedai/sync

Sync data from your backend to InformedAI's knowledge library.

## Installation

```bash
npm install @informedai/sync
```

## Setup

Before using this SDK, you must create a sync source in the InformedAI dashboard:

1. Go to the InformedAI dashboard
2. Navigate to the Knowledge Library playground
3. Click "+" and select "Sync Source"
4. Enter a name for your sync source
5. Copy the generated API key (`ssk_...`)

## Quick Start

### Pattern 1: Manual Push (Raw SQL backends)

For backends using raw SQL queries (like Express + pg):

```typescript
import { InformedAI } from '@informedai/sync';
import { pool } from './db';

const ai = new InformedAI({
  apiKey: process.env.INFORMEDAI_SYNC_KEY, // ssk_... from dashboard
  onDataRequest: async (req) => {
    // Called when InformedAI needs data that's missing from cache
    if (req.category === 'projects') {
      const result = await pool.query('SELECT * FROM projects WHERE id = $1', [req.externalId]);
      const project = result.rows[0];
      return project ? { title: project.title, description: project.description } : null;
    }
    return null;
  },
});

// Start polling for recovery requests
ai.startPolling();

// Sync after creating/updating a project
app.post('/projects', async (req, res) => {
  const project = await createProject(req.body);

  // Fire-and-forget sync to InformedAI
  ai.sync('projects', project.id, {
    title: project.title,
    description: project.description,
    tech_stack: project.tech_stack,
  }).catch(console.error);

  res.json(project);
});

// Sync on delete
app.delete('/projects/:id', async (req, res) => {
  await deleteProject(req.params.id);
  ai.delete('projects', req.params.id).catch(console.error);
  res.status(204).send();
});
```

### Pattern 2: Prisma Auto-Sync

For backends using Prisma ORM:

```typescript
import { InformedAI } from '@informedai/sync';
import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

const ai = new InformedAI({
  apiKey: process.env.INFORMEDAI_SYNC_KEY,
});

// Automatically sync on Prisma operations
ai.prisma(prisma, {
  models: {
    BlogPost: {
      category: 'blog_posts',
      displayName: 'Blog Posts',
      on: ['create', 'update'], // default
      map: (post) => ({
        title: post.title,
        content: post.content,
        excerpt: post.excerpt,
        author: post.author,
      }),
    },
    Event: {
      category: 'events',
      displayName: 'Golf Events',
      on: ['create', 'update', 'delete'],
      map: (event) => ({
        name: event.name,
        date: event.date.toISOString(),
        description: event.description,
      }),
    },
  },
});

ai.startPolling();
```

### Pattern 3: Batch Sync (Initial Import)

For syncing existing data:

```typescript
import { InformedAI } from '@informedai/sync';

const ai = new InformedAI({
  apiKey: process.env.INFORMEDAI_SYNC_KEY,
});

// Sync all existing projects
const projects = await pool.query('SELECT * FROM projects');

await ai.syncBatch('projects', projects.rows.map(p => ({
  id: p.id,
  data: {
    title: p.title,
    description: p.description,
    tech_stack: p.tech_stack,
  },
})));

console.log('Initial sync complete!');
```

## API Reference

### `new InformedAI(config)`

Create a new sync client.

| Option | Type | Required | Description |
|--------|------|----------|-------------|
| `apiKey` | `string` | Yes | Sync source API key from dashboard (`ssk_...`) |
| `baseUrl` | `string` | No | API base URL (default: `https://api-beta.informedai.app`) |
| `onDataRequest` | `function` | No | Handler for recovery requests |
| `pollInterval` | `number` | No | Polling interval in ms (default: 30000) |
| `debug` | `boolean` | No | Enable debug logging (default: false) |

### Methods

#### `sync(category, id, data)`

Sync a single record.

```typescript
await ai.sync('projects', 'proj_123', {
  title: 'My Project',
  description: 'A great project',
});
```

#### `syncBatch(category, records)`

Sync multiple records at once.

```typescript
await ai.syncBatch('projects', [
  { id: 'proj_1', data: { title: 'Project 1' } },
  { id: 'proj_2', data: { title: 'Project 2' } },
]);
```

#### `delete(category, id)`

Delete a record.

```typescript
await ai.delete('projects', 'proj_123');
```

#### `rebuild(category)`

Force rebuild knowledge for a category.

```typescript
await ai.rebuild('projects');
```

#### `getStatus()`

Get current sync status.

```typescript
const status = await ai.getStatus();
console.log(status.categories);
```

#### `startPolling()` / `stopPolling()`

Start or stop polling for recovery requests.

```typescript
ai.startPolling();

// On shutdown
process.on('SIGTERM', () => {
  ai.stopPolling();
});
```

#### `prisma(client, config)`

Set up automatic Prisma syncing.

```typescript
ai.prisma(prisma, {
  models: {
    ModelName: {
      category: 'category_name',
      displayName: 'Display Name',
      on: ['create', 'update', 'delete'],
      map: (record) => ({ field: record.field }),
      getId: (record) => record.customId, // optional
    },
  },
});
```

## How It Works

1. **Data Storage**: Your data is cached in Redis for fast retrieval and chunked into pgvector-backed knowledge for RAG queries.

2. **Recovery**: If Redis loses data, InformedAI creates recovery requests. Your SDK polls for these and resends the data.

3. **Visualization**: Sync sources appear in the InformedAI playground, showing category counts and sync status.

## Environment Variables

```bash
INFORMEDAI_SYNC_KEY=ssk_your_sync_source_key_here
```

## License

MIT