⌇ ariadne
Find the conversation.
Turn scattered X/Twitter archives and tweet datasets into readable, attributable reply branches.
documentation · PyPI · Python API · MIT
A social export remembers posts. The conversation around them is often somewhere else: a parent in another archive, a quote in a community dataset, an older post in the cache.
Ariadne merges those sources, selects the posts you care about, follows every known reply-parent chain toward its root, attaches quote context, and renders the result root → target.
It does not pretend sparse data is complete. Missing posts stay visible as
placeholders and warnings unless you ask for --strict.
Start here
Requires Python 3.11 or newer. The distribution is ariadne-x; the command and
import are both ariadne.
uv tool install ariadne-x
ariadne interactive
Or build directly from a personal archive:
ariadne build \
--archive ~/Downloads/twitter-archive.zip \
--for-user alice \
--since 2024-01-01 \
--format markdown \
--output conversations.md
That is the whole basic loop:
archives + dumps + cache
↓
choose targets
↓
follow known parent IDs
↓
quotes + root-to-target branches
Choose your path
| You have… | Use… |
|---|---|
| One X/Twitter export | ariadne build --archive PATH … |
| CSV, JSON, JSONL, or NDJSON | ariadne build --tweets-file PATH … |
| Tweet IDs or X URLs | Pass them after ariadne build |
| Archives you will reuse | ariadne dumps import PATH |
| A public X account | ariadne build --target-user USER … |
| A Bluesky handle | ariadne bluesky HANDLE … |
Imported archives form a local, searchable library:
ariadne dumps import ~/Downloads/twitter-archive.zip --name personal
ariadne dumps search "remembered phrase" --user alice
ariadne dumps show https://x.com/alice/status/1234567890123456789
# Imported dumps join ordinary builds automatically.
ariadne build --for-user alice --since 2024-01-01 --format raft -o alice.jsonl
Each import becomes a self-contained SQLite database under ~/.ariadne/dumps.
The source is never modified, and removing an import never removes the source.
Parquet imports additionally need DuckDB:
uv tool install 'ariadne-x[parquet]'
Read the archive library guide →
From Python
The CLI is a thin front end over a typed synchronous API:
from pathlib import Path
import ariadne
options = ariadne.BuildOptions(
archive=Path.home() / "Downloads" / "twitter-archive.zip",
for_user="alice",
since="2024-01-01",
)
result = ariadne.build(options)
for conversation in result:
print(conversation.target_id)
documents = result.raft_documents()
result.save("out/branches.jsonl", "raft")
Use no_dumps=True when a build must ignore the persistent archive library.
Named failures derive from AriadneError, including ConfigurationError,
NoTargetsError, ReconstructionError, and SourceError.
Read the Python API reference →
Pick an output
| Format | Shape | Good for |
|---|---|---|
messages |
enriched JSON conversations | chat-like data with tweet metadata; the CLI default |
openai |
reduced JSON conversations | nested role, name, and content messages |
json |
normalized graph + tweets | analysis, provenance, and custom rendering |
markdown |
text | humans, notebooks, and review |
raft |
one JSON object per line | retrieval, chunking, and embedding |
The openai renderer keeps Ariadne's conversation envelope; consumers extract
conversations[i].messages. Roles follow authorship: the collected user's
tweets speak as assistant, everyone else's as user (participant in the
raft format) — even when a conversation's own tweets can't tell, because the
build knows who was collected. In markdown, consecutive tweets by the same
person merge into one message: one role header, the metadata of every tweet in
the run, then the texts separated by --- rules.
Pass --responses-only (API: responses_only=True) to keep only
conversations in which the subject actually responds — replies to or
quote-tweets someone else. Standalone tweets and pure self-threads are
dropped; a reply to a deleted tweet counts as a response, since the missing
parent was somebody. ariadne interactive asks the same question.
What Ariadne follows
- One target's ancestor path back to its root—not sibling replies or a whole tree.
- Older parents even when
--sincelimits the starting targets. - Reply and quote edges across different imported dumps.
- Quote context, with root quote-tweets spliced onto their quoted post by default.
Ordinary archive builds stay local. --target-user is the convenience exception: it
tries unofficial RSS and oEmbed unless disabled. Those sources can recover recent text
but usually cannot prove reply edges. X API reads are separately opt-in through
--fetch and --fetch-user-timeline and may be billable.
Read the source and network policy →
A few useful commands
ariadne inspect-archive ~/Downloads/twitter-archive.zip
ariadne dumps interactive
ariadne build --help
# Bluesky uses its public API and the same renderers.
ariadne bluesky alice.bsky.social --since 2024-01-01 --format raft
Picking up where a build left off
Every build that fetches anything writes a cache (.ariadne-cache.json by
default) — and since 0.6 it also records the tweet ids it could not
resolve, so the holes survive the session:
ariadne cache list # what each cache holds, and what is still missing
ariadne cache missing # the missing ids, one per line
ariadne cache retry # fetch them now, updating the cache in place
cache retry (API: ariadne.retry_cache()) tries local dumps and free
oEmbed by default; add --community-archive, --twitterapi-key, or
--fetch with an X API token for the stubborn ones, and --limit to bound
a run. Re-running the original ariadne build afterwards picks the
recovered tweets up from the cache. The cache write is last-writer-wins, so
retry after a build using the same cache has finished, not alongside it.
Reference
- Documentation site
- Persistent archive library
- Python API
- Source behavior
- Output schemas
- Raft handoff
Development
uv sync --extra dev --extra parquet
uv run pytest
uv run ruff check .
uv run mypy src
MIT licensed. The thread was there all along.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ariadne_x-0.6.0.tar.gz.
File metadata
- Download URL: ariadne_x-0.6.0.tar.gz
- Upload date:
- Size: 100.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d616bd52569fcbf3b953c0d38559611e7a095f36892ac8acc79abc2e32a01bd8
|
|
| MD5 |
8bd880fa45fc35c67b7f46b5e11ba8db
|
|
| BLAKE2b-256 |
bf11975b3460110c130424cc439fd917bed27ca887d7c841f3b9d67fc4c1c15c
|
Provenance
The following attestation bundles were made for ariadne_x-0.6.0.tar.gz:
Publisher:
publish.yml on lumpenspace/ariadne
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ariadne_x-0.6.0.tar.gz -
Subject digest:
d616bd52569fcbf3b953c0d38559611e7a095f36892ac8acc79abc2e32a01bd8 - Sigstore transparency entry: 2555312862
- Sigstore integration time:
-
Permalink:
lumpenspace/ariadne@8d78a371b0ce6e47f810a5356127af2518888a46 -
Branch / Tag:
refs/tags/v0.6.0 - Owner: https://github.com/lumpenspace
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@8d78a371b0ce6e47f810a5356127af2518888a46 -
Trigger Event:
release
-
Statement type:
File details
Details for the file ariadne_x-0.6.0-py3-none-any.whl.
File metadata
- Download URL: ariadne_x-0.6.0-py3-none-any.whl
- Upload date:
- Size: 73.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
396b40276043162da4ec1ea473463c62f7366a7eba4cd436ab98004276cdb81a
|
|
| MD5 |
7e5a93923329973c377baaaf7272aa52
|
|
| BLAKE2b-256 |
a0e70ebae4f96943e53728d7465c8d74c1f86f7de0dcbff2f1ac678c02cdf1ad
|
Provenance
The following attestation bundles were made for ariadne_x-0.6.0-py3-none-any.whl:
Publisher:
publish.yml on lumpenspace/ariadne
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ariadne_x-0.6.0-py3-none-any.whl -
Subject digest:
396b40276043162da4ec1ea473463c62f7366a7eba4cd436ab98004276cdb81a - Sigstore transparency entry: 2555313020
- Sigstore integration time:
-
Permalink:
lumpenspace/ariadne@8d78a371b0ce6e47f810a5356127af2518888a46 -
Branch / Tag:
refs/tags/v0.6.0 - Owner: https://github.com/lumpenspace
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@8d78a371b0ce6e47f810a5356127af2518888a46 -
Trigger Event:
release
-
Statement type: