Skip to main content

seriate.py

seriate.py implements seriation—a generalized form of sorting where items are arranged in a meaningful order that may not follow a strict comparison function.

Pathbuild/seriate.py
LanguagePython
Lines212
Sourcebuild/seriate.py
at 406d3e423

Read this when Use this page when tracing Python helper scripts for metadata cleanup, text processing, PDFs, dates, or generated content around seriate.

Overview​

seriate.py implements seriation—a generalized form of sorting where items are arranged in a meaningful order that may not follow a strict comparison function. While traditional sorting puts "ACB" → "ABC", seriation might order "Dog, Horse, Kitty" → "Kitty, Dog, Horse" based on semantic similarity (perhaps size, phylogeny, or another contextual relationship).

The script uses OpenAI's GPT API to iteratively reorder input until it reaches a fixed point (the order stops changing). It then validates that the output is a permutation of the input—no items lost or added.

This enables automatic organization of lists, paragraphs, or other content where "logical order" is context-dependent and hard to specify programmatically.


Usage​

# From command line arguments
OPENAI_API_KEY="sk-XXX" python seriate.py "item1" "item2" "item3"

# From stdin (clipboard example)
OPENAI_API_KEY="sk-XXX" xclip -o | python seriate.py

# From pipe
echo "red, blue, green" | OPENAI_API_KEY="sk-XXX" python seriate.py

Requirements:

  • OPENAI_API_KEY environment variable set
  • OpenAI Python package (pip install openai)

How It Works​

Seriation Algorithm​

Input list
↓
┌─────────────────────────┐
│ Ask LLM to seriate once │
└─────────────────────────┘
↓
┌─────────────────────────┐
│ Result == Previous? │──Yes──→ Done (fixed point)
└─────────────────────────┘
↓ No
┌─────────────────────────┐
│ Result seen before? │──Yes──→ Error (cycle detected)
└─────────────────────────┘
↓ No
┌─────────────────────────┐
│ Iterations < 5? │──No───→ Use current result
└─────────────────────────┘
↓ Yes
Loop back
↓
Validate permutation
↓
Output

Fixed Point Iteration​

The script repeatedly asks the LLM to reorder until the output equals the input (fixed point). This handles cases where:

  • Initial reordering is suboptimal
  • Multiple passes reveal better arrangements
  • The model gradually converges to the "right" order

Permutation Validation​

def is_permutation(a, b):
norm = lambda s: sorted(unicodedata.normalize('NFKC', s).split())
return norm(a) == norm(b)

After seriation, the script verifies that the output is a permutation of the input at the word level: it splits both into words, NFKC-normalizes (so smart-quote/em-dash substitutions and whitespace changes don't trip it), and compares the sorted multisets—a check that nothing was lost or invented, while tolerating pure-punctuation changes.


The Prompt​

The LLM receives a detailed prompt with examples covering:

TypeExample
Magnitudemm; km; cm; m → mm; cm; m; km
Colorsred, purple, blue... → rainbow order
Valuegold platinum silver copper → by value
Processland | take off | taxi... → flight sequence
HierarchyPhD, Bachelor's, High School... → education levels
TemporalRenaissance, Bronze Age... → chronological
SpatialMars, Earth, Jupiter... → distance from sun
AlphabeticalItems without obvious semantic order
Multi-levelNested lists, paragraphs

The prompt emphasizes:

  • Seriate, don't modify: Only reorder, preserve exact text
  • Stable seriation: When ambiguous, keep original order
  • Return empty for non-lists: Single items or non-seriatable input → ""

Key Functions​

seriate_once(target)​

Single-pass seriation via GPT API:

def seriate_once(target):
prompt = """Task: 'seriate' inputs...""" + target + "</input>\n<output>"

completion = client.chat.completions.create(
model="gpt-5-mini",
timeout=60,
messages=[
{"role": "developer", "content": "You are an analyst and editor..."},
{"role": "user", "content": prompt}
],
)
return completion.choices[0].message.content\
.removeprefix("<output>")\
.removesuffix("</output>")

is_permutation(a, b)​

Word-level permutation check (NFKC-normalized):

def is_permutation(a, b):
norm = lambda s: sorted(unicodedata.normalize('NFKC', s).split())
return norm(a) == norm(b)

Configuration
SettingValuePurpose
Modelgpt-5-miniFast, capable model for reordering
Timeout60sAPI call timeout
Max iterations5Prevent infinite loops

Error Handling​

Cycle Detection:

if result in seen:
print(f"ERROR: cycle detected at iteration {i+1}", file=sys.stderr)
sys.exit(1)

If the model produces an output it's seen before (but isn't the current input), there's a cycle—seriation will never converge.

Permutation Check:

if not is_permutation(original, current):
print(f"ERROR: not a permutation", file=sys.stderr)
sys.exit(1)

Catches cases where the model accidentally dropped or invented content.


Use Cases​

Content Organization​

Automatically sort:

  • Lists of related items (books, tools, concepts)
  • Paragraph sequences in notes
  • Image captions or gallery items
  • Bibliography entries by relevance

LLM Writing Pipeline​

The script suggests a seriation-first approach:

  1. Seriate raw notes (constrained to reordering only)
  2. Summarize to create structure/TOC
  3. Rewrite the organized content

This prevents LLMs from inadvertently omitting or rewriting content during organization.


Theoretical Background​

The script's docstring explains:

This sort of distance minimization is known as 'seriation' (or 'ordination'), and can be seen as a generalization of regular sorting

References:


Dependencies​

  • OpenAI Python SDK: pip install openai
  • OPENAI_API_KEY: Environment variable with valid API key

See Also
  • Annotation.hs - Metadata that might benefit from seriation
  • embed.sh - Another LLM-powered text utility (embeddings)
  • Tags.hs - Tag organization that could use seriation