Skip to main content
Aikeedo imports conversation archives in the background: a user uploads a file, a job is created, and the cron tick feeds the file through an adapter in batches. A plugin supplies the adapter for a new source.

How an import runs

The job tracks its own progress, so each tick continues where the last one stopped. Batches are bounded, which keeps a large archive from exhausting memory or time.

The interface

Yield conversations

Each yielded conversation must carry the source’s own identifier in its metadata as _original_id. The runner uses it to build a stable external ID, which is how re-running an import skips what’s already there.
src/NotesAdapter.php
Check the exact constructor and factory signatures of ConversationEntity and MessageEntity in the source before you build them: they take value objects, and the message factories set the role for you.

Rules

  • Respect $offset and $limit. Yielding the whole archive on the first call defeats the batching, and large imports will time out.
  • Yield, don’t persist. The runner persists, deduplicates and counts.
  • Be restartable. prepare() and parse() run again on the next tick, so make both safe to repeat.
  • Set _original_id on every conversation. Without it, the conversation is skipped.
  • Keep memory flat. Stream the archive rather than decoding it all into an array where you can.

Register the adapter

The runner finds an adapter by matching the job’s source against the key you registered under, so the key must equal the source value your upload endpoint sets:
src/Plugin.php

Create jobs

Give users a page to upload their archive, then create the file and the job:
Store the upload with FileSystemInterface under var/, not the CDN: it’s a working file, not something to serve. Poll the job from your page to show progress, and remember that nothing happens until a cron tick runs.

Testing

A small archive imports completely, and conversations appear in the library.
A large archive imports across several cron ticks without timing out.
Re-importing the same archive skips existing conversations instead of duplicating them.
A corrupt archive fails the job with a readable error, rather than looping forever.
Temporary files are removed when the job finishes.

Official import adapters

The official Migration plugin already imports from ChatGPT, Claude and Grok. It’s available on the Aikeedo Marketplace: