This is the canonical operating guide for coding agents working in this
checkout or writing code that uses GABRIEL. Keep it concise and update it when
public behavior changes. The README explains the package; the tutorial notebook
contains runnable examples; src/gabriel/api.py is the source of truth for
public signatures.
The recommendations below are practical heuristics, not universal rules. They reflect recurring successful workflows and package behavior at the date shown; they may be stale, unavailable in a particular organization, too expensive, or wrong for a specific dataset. User intent, the live API/package, and evidence from a representative pilot take precedence.
Before improvising a workflow, read gabriel_tutorial_notebook.ipynb carefully.
It is the most complete capability map and shows how tasks, modalities, custom
endpoints, checkpoints, visualization, and downstream analysis fit together.
- Inspect the actual environment first. Confirm
gabriel.__file__, the installedopenai-gabrielversion, and the live function signature instead of assuming the PyPI package, this checkout, and an old example are identical. - Inspect unfamiliar scripts and inputs before executing them. For a paid run, confirm the row count, prompt count, model, output directory, and expected cost, then pilot a small representative sample before scaling.
- Prefer the public
gabriel.*wrappers. Useawaitin notebooks andasyncio.run(...)in ordinary Python scripts. - Never place API keys, private data, machine-specific paths, or generated research artifacts in committed examples.
Never invent a model slug or copy one blindly from an old notebook. Check the official model catalog, the pricing page, and the models actually provisioned for the user's organization or gateway before overriding a default or publishing a release. Availability, aliases, rate limits, and feature support can vary by organization.
At this guide's last review (2026-07-13), GABRIEL used this snapshot:
| Workload | Exact model slug | Typical use |
|---|---|---|
| Fast and inexpensive | gpt-5.6-luna |
Often a sensible starting point, especially for large-scale runs; default for rate, classify, rank, extract, filter, and merge |
| Balanced | gpt-5.6-terra |
Consider when a representative pilot materially improves over Luna and does not require Sol |
| Full capability | gpt-5.6-sol |
Very complex or subtle work with many moving parts, niche knowledge, difficult synthesis, or important writing quality |
| Audio understanding | gpt-audio-1.5 |
Audio input through Chat Completions |
These are dated facts, not permanent aliases. Verify that a prospective model supports the required endpoint, modality, reasoning setting, and structured output behavior. Do not mechanically replace an audio model with a text model.
A common starting point is Luna, particularly at scale. If quality is uncertain, run a small representative comparison across Luna, Terra, and Sol and inspect task-specific accuracy, omissions, consistency, latency, and cost. Prefer the smallest model that reliably meets the research standard. Escalate to Sol when the work is genuinely complex or subtle, has many interacting constraints, depends on niche internal knowledge, or when the quality of the reasoning or writing is itself important. Do not choose model size mechanically from the GABRIEL function name alone.
- Generally keep
n_parallels=650, including for small tests. It is a ceiling, not a fixed worker count, so a small job does not create 650 unnecessary requests. GABRIEL ramps workers, observes rate limits, retries transient failures, and reduces actual concurrency for sustained errors, web search, and media. Temporary retries and a few slow stragglers are normal; while a run is progressing, it is usually best to let it finish. Raising the ceiling can be reasonable when a large workload needs more throughput and the account or gateway supports it. Reducing it can be appropriate for a known deployment constraint or persistent failure, but is rarely needed merely because a test is small. These are operational suggestions, not hard limits. - Do not pass
max_output_tokens. GABRIEL accepts it only for compatibility, emits aFutureWarning, and ignores it. - Keep
reset_files=Falseto resume a compatible checkpoint. If the model, prompt, labels, attributes, batching, or meaning of a run changes, use a newsave_diror intentionally reset; never mix two specifications in one cache. - Normally leave
n_attributes_per_run=None. Current models can often handle dozens of labels or fields together—including 30, 40, or 50—while retaining useful cross-attribute context. The parameter remains available if an actual pilot reveals a context, schema-adherence, quality, or provider constraint; do not split attributes preemptively. - Reuse GABRIEL's retries and checkpoints. Do not wrap it in ad hoc thread pools or rerun an entire corpus when only a small failed subset needs repair.
For fewer than roughly 100,000 rows, the simplest approach is usually to pass the full DataFrame directly to one GABRIEL run and let its checkpointing and adaptive concurrency operate. At or above roughly 100,000 rows, deterministic batches processed separately may make scheduling, recovery, parallel job compute, and exports easier. This is a rough operational threshold, not a quality boundary. If batching helps, preserve stable source identifiers and the same specification across batches, then union and audit the results.
extract, classify, and rate cover a large share of real projects.
extract is often the most flexible because a single call can combine several
different kinds of structured judgment. The other primitives remain useful
when their specialized behavior matches the question.
| Need | Use | Important behavior |
|---|---|---|
| Mixed structured outputs | extract |
Often the most flexible choice: one pass can return facts, classifications, ratings, evidence, explanations, and mixed boolean, categorical, numeric, or text fields. It can also expand one source into several extracted-entity rows. |
| One or more label columns | classify |
Labels are independent booleans by default, so one row can receive several classes. For exactly one class, make labels mutually exclusive and say "choose exactly one" in additional_instructions; audit the result because this is prompted behavior, not a hard invariant. |
| Continuous 0-100 measurements | rate |
Convenient when several attributes share a clearly defined numeric scale and the built-in rating aggregation is useful. |
| Relative ordering | rank |
Pairwise comparisons produce relative z-scores; potentially useful when fine distinctions are easier than absolute scores. |
| Matching passages inside documents | codify |
Use when the evidence snippet matters, not merely a document-level label. |
| Broad inexpensive screening | filter |
Reduce a candidate universe before a more expensive task. |
| Building an emergent taxonomy | bucket |
Takes a large universe of terms, lists, or term-to-definition mappings and iteratively proposes and votes on a compact set of relatively distinct bucket names and definitions. It returns the taxonomy, not an assignment for every source row; the reviewed buckets can then inform classify or another mapping step. |
| Finding features that distinguish groups | discover |
Contrasts two groups of texts or paired columns to propose recurring discriminating features that can later become reviewed measurement definitions. |
| Fuzzy crosswalks and normalization | merge, deduplicate |
Preserve raw values beside canonical mappings for auditability. |
| Controlled rewriting or privacy transforms | paraphrase, deidentify |
Inspect samples before trusting a large transformed corpus. |
| Diverse generation | seed, poll, ideate |
Generation, synthetic-persona, survey, and ranked-idea workflows. |
| Fully custom prompts | whatever |
Keeps GABRIEL's parallelism, retries, checkpoints, attachments, and optional JSON parsing. |
Attribute, field, and label descriptions are the measurement specification.
Write concrete inclusion/exclusion criteria and operational examples; names alone
are rarely enough. Use additional_instructions for cross-label constraints.
For extract, the types mapping is downstream coercion, not a replacement for
clear field descriptions. If delivery requires one row per original source,
explicitly aggregate multi-entity results while preserving every extracted value
rather than silently dropping extra rows.
extract is not limited to literal named entities. It can act as a broad
structured-analysis primitive for multi-field classification, scores plus
explanations, linked evidence, or several records embedded in one source.
classify may be simpler when the desired output is only independent boolean
label columns, while rate provides purpose-built repeated numeric ratings.
Treat these as possibilities to adapt, not required recipes:
- Build a representative pilot that includes easy cases, hard cases, long inputs, missing information, multilingual or messy examples, and any edge case that could change the conclusion. Inspect rows, not just averages.
- One possible large-scale funnel is
filterfor broad screening, thenclassify/extract/rateon survivors, andrankonly when relative ordering or the extreme top tail matters. Skip stages that add no value. - For exploratory work,
discoverorbucketcan propose a vocabulary. Human review and sharpen those definitions before treating them as a fixed measurement instrument forclassifyorrate. - One possible open-ended workflow is to
extractfree-form entities, events, or judgments;bucketthose values into a candidate taxonomy; review the definitions; thenclassifythe full corpus andrateintensity or quality where a continuous measure is useful. Each stage is optional. - Prefer explicit field and label definitions over clever orchestration. Examples, inclusion/exclusion rules, an entity unit, and instructions for unknown values often improve results more than adding another pipeline layer.
- Use multiple runs when model variation matters, but first decide whether the downstream statistic should be a mean, vote, union, or disagreement flag. Preserve per-run outputs when disagreement itself is informative.
- On large jobs, keep stable source IDs, use a new descriptive
save_dirfor a changed specification, let checkpointing work, audit the completed table, and repair only missing or failed rows/cells. Separately managed batches are an operational option at very large scale, not an automatic quality improvement. - Keep raw responses, the full parsed dataset, and any filtered/ranked shortlist as separate artifacts when each serves a different audit purpose. This is especially useful for ideation and multi-stage research workflows.
- Cost estimates are planning aids. Recheck the live printed estimate, current pricing, prompt count, retries, tool use, long-context surcharges, audio/media billing, and output shape before a large run.
- Use GABRIEL's built-in printouts as the first monitoring surface. The startup summary and live progress updates expose useful information about the model, workload, rate limits, planned concurrency, estimated cost, throughput, retries, and remaining work. Keep an eye on those signals during long runs; temporary retries and a few slow stragglers are expected while progress continues. Preserve the console log for remote or expensive jobs when useful, but still audit the completed table afterward because a finished progress bar is not proof that every requested field parsed successfully.
- When a result looks odd, inspect the rendered prompt, raw response, cached run metadata, installed package path/version, and a handful of source rows before rewriting the whole workflow.
GABRIEL generally accepts a pandas DataFrame and a column_name identifying the
content to analyze. Useful input units range from individual terms to long-form
documents and media. These are common patterns, not restrictions:
- Prefer one row per meaningful source unit: for example a speech, article, interview response, product listing, archival page, technology, job title, legal case, image, audio clip, or PDF. Keep an explicit stable source ID and preserve useful provenance or grouping columns beside the analyzed column.
- The model generally analyzes the selected
column_name, not every metadata column in the DataFrame. If date, speaker, location, surrounding text, or another field should affect the judgment, construct an explicit context column or use the task's supported context parameters rather than assuming the model sees the rest of the row. - Use
modality="text"for passages or documents whose supplied words are the evidence. Usemodality="entity"for short names or concepts where model knowledge is part of the task.modality="web"can add current web context for entities or questions when that is appropriate. - A text cell can be a sentence, transcript, article, or substantial document;
an entity cell can be a short term; image, audio, and PDF modalities normally
use file paths. Separate mixed modalities into coherent calls unless a custom
whateverworkflow is genuinely useful. - Whole documents can work when full context matters. Split at meaningful boundaries only when document length, precision, or the desired unit of analysis makes that useful, and retain source/chunk IDs plus an explicit recombination plan.
- CSV, TSV, Excel, Parquet, and Feather tables can be loaded directly.
gabriel.load(...)can also construct a DataFrame from folders of text, images, audio, or PDFs while retaining filenames and directory layers. - Direct PDF modality preserves layout, figures, and images. Text extraction may be preferable when only words matter or when text from several file types should share one run. Images and audio can be analyzed directly rather than first forcing them through OCR or transcription when visual or acoustic evidence matters.
- Preserve the raw source column even when creating cleaned or shortened input. Examples, definitions, instructions for unknown values, and the intended unit of analysis often matter more than elaborate preprocessing.
- Decide the desired output unit before running.
rateandclassifynormally preserve one row per source;extractcan either return several fields on one source row or intentionally expand one source into multiple entity rows. If a later deliverable needs one source row, aggregate explicitly without losing the extracted values.
- Use
gabriel.load(...)to turn folders of supported files into DataFrames, and make the call'smodalitymatch the input column. - For image understanding, pass
image_detail="low","high","original", or"auto". This becomesdetailon each APIinput_image. It is different from image-generationquality. - Audio input requires an audio-capable model and uses Chat Completions. Verify the current audio slug before running; audio is not supported in batch mode.
- Web search and JSON mode cannot currently be combined; GABRIEL warns and disables JSON mode. Inspect the raw response shape before parsing a web run.
- When supplying a custom structured-output schema, use the API's supported strict JSON Schema subset and pilot the exact schema; a syntactically valid Python dictionary is not proof the API will accept every schema feature.
response_fnreplaces one model call.get_all_responses_fnreplaces the orchestration layer. Use the narrowest override that solves the problem.
- Put paid-run outputs and datasets outside the source checkout. Give each
experiment a stable, descriptive
save_dir. - Preserve the cleaned result, raw-response checkpoint, and run metadata.
- After a run, audit expected identifiers, row counts, duplicate IDs, nulls,
parse failures, value ranges, and any task-specific
Successfulfield. - A completed process is not proof of a complete table. Repair missing rows or cells from the checkpoint rather than paying to regenerate successful work.
extractmay legitimately increase row count. Decide explicitly whether the downstream unit is the extracted entity or the original source.- Spot-check examples with
gabriel.viewand read raw responses when the output distribution looks surprising.
- Preserve unrelated user changes in a dirty worktree.
- Keep public defaults synchronized across
src/gabriel/api.py, task configs, parsing fallbacks, pricing, README, tutorial examples, and tests. - Deprecate public arguments with a warning and compatibility path before removal. Test the exact request payload, not only the returned DataFrame.
- Unit tests must not require paid API calls. Use dummy responses or mocks.
- Run
pytest,git diff --check, validate the notebook JSON, and build both a wheel and source distribution before calling a release ready. - Keep useful deterministic tutorial outputs when they aid readers, but clear stale, private, failed, or excessively noisy outputs. Keep generated run artifacts out of Git.