Echo ProgramLog in

Documentation

One call, a sealed engine, a verdict. Everything a training team or its coding agent needs to take a forecast, act on it, and report what happened.

Reference clientOpenAPIPlain text

What the service does

Before you spend GPU time curating data toward a target domain, send a sample of the target and a sample of the candidate pool. The engine answers in seconds, on CPU, without touching your model. The corpus never leaves your machines; only samples travel, over HTTPS, processed in memory and never written to disk.

SELECT
Meaning Curating toward this target is forecast to help.
What you do Curate. Rank the pool with the scoring table and keep the top.
DO-NOT-SELECT
Meaning Curating is forecast to backfire, or the target carries no usable signal.
What you do Train on a random sample of the same size and keep the compute.
NO-FORECAST
Meaning Too close to the calibrated boundary to call.
What you do Treat the gain as a coin flip. Add target data and run again.

Every verdict carries a confidence tier (high, medium, low), a scope card, notes, a fingerprint of the exact text it was made on, and a forecast_id you use later to report the outcome. Every forecast is timestamped before your first training step. The receipt that ties forecast to outcome is the product.

Quick start

You need Python 3 and your API key from the dashboard. The reference client has no dependencies.

bash
curl -O https://echo-program.com/client/echospace_pilot.py
export ECHOSPACE_URL=https://api.echo-program.com
export ECHOSPACE_API_KEY=es_pilot_...

python3 echospace_pilot.py selftest
python3 echospace_pilot.py forecast --target ./target_docs/ --pool ./pool_docs/ --label first-try \
    --model-params 124000000 --keep 0.25 --language en --regime pretraining --table --out fc_first.json
python3 echospace_pilot.py select-local --forecast fc_first.json --pool ./pool_docs/ --keep 0.25 --out keep.json
python3 echospace_pilot.py outcome --forecast fc_xxxxxxxxxxxx --selected 1.91 --baseline 2.05

keep.json holds the indices of the pool documents to keep, in the order the client read them: files in sorted name order, walking folders recursively, then line order inside .jsonl files.

Documents and sizes

A document is one string. The client reads a folder recursively: every .jsonl line with a text field is one document, and any other file is one document. Over raw HTTP you send lists of strings.

Target minimum
Value about 2,600 words in two or more documents (20 chunks of 128 words)
Pool minimum
Value at least 4 times the target, by chunks
Pools larger than 250,000 chunks
Value a seeded random sample of whole documents is used; the response says so
Per call
Value up to 96 MB compressed on the wire, 512 MB inflated, 400,000 documents
Per key
Value 50 engine calls per 24 hours (forecast and select); raised on request
Client sample defaults
Value about 5,000,000 pool words and 2,000,000 target words, seed 0

More target data sharpens the reading. A target that arrives as a single document is accepted but noted; send one entry per document. English, whitespace-delimited text is the validated range; text without word boundaries is flagged in script_check.

Authentication

Every call carries the key as a bearer token. Keys are stored hashed on the service and revoked on request; one key per organisation. Send a User-Agent naming your tool; a bare default Python agent is rejected at the edge before it reaches the service.

bash
curl -s https://api.echo-program.com/v1/me -H "Authorization: Bearer $ECHOSPACE_API_KEY" -H "User-Agent: my-pipeline/1.0"
json
{"key_id": "key_a6326f70", "org": "Acme Research", "daily_quota": 50, "used_last_24h": 2, "forecasts_total": 7}

Forecast

POST/v1/forecast

Samples in, verdict out. Counts as one engine call.

target
Type string[]
Meaning sample of target documents, one string each
pool
Type string[]
Meaning sample of candidate-pool documents
label
Type string, optional
Meaning your name for this run, up to 120 characters
table
Type boolean, default false
Meaning on SELECT, also return the scoring table
context
Type object, optional
Meaning model_params (int), keep_fraction (0 to 1), language (ISO code), regime (pretraining, continued, finetuning). Outside the validated range the confidence drops one tier and the response says why; the verdict does not change.
provenance
Type object, optional
Meaning how the samples were drawn: client, pool_total_documents, target_total_documents, sample_seed. Recorded on the receipt, not verified.
Raw HTTP, gzip optional
curl -s https://api.echo-program.com/v1/forecast \
  -H "Authorization: Bearer $ECHOSPACE_API_KEY" -H "Content-Type: application/json" -H "User-Agent: my-pipeline/1.0" \
  -d '{"target": ["doc one ...", "doc two ..."], "pool": ["...", "..."], "label": "first-try",
       "context": {"model_params": 124000000, "keep_fraction": 0.25, "language": "en", "regime": "pretraining"}, "table": true}'
Response
{
  "forecast_id": "fc_7e2cc32e34e0",
  "verdict": "SELECT",
  "confidence": "medium",
  "reason": "...",
  "unstable": false,
  "target": {"documents": 6, "chunks": 42, "words": 5376},
  "pool": {"documents": 60, "chunks": 281, "words": 35968, "subsampled": false},
  "notes": [],
  "script_check": {"unspaced_fraction": 0.0},
  "scope": {
    "validated": {"model_size": "...", "keep_fraction": "...", "text": "...", "regime": "...", "selector": "..."},
    "outside_validated_range": [],
    "confidence_before_scope": "medium"
  },
  "fingerprint": {"target_sha256": "...", "pool_sha256": "...", "hasher": "es-h1", "engine": "pilot-0.2"},
  "provenance": {"client": "my-pipeline/1.0", "pool_total_documents": 812000, "target_total_documents": 3100, "sample_seed": 0},
  "engine": "pilot-0.2",
  "elapsed_s": 0.31,
  "scoring_table": {"format": "float32-le-base64", "buckets": 16384, "hasher": "es-h1", "sha256": "...", "values": "..."}
}

Selection

Two ways to turn a SELECT into a keep list. The local way is the one to use for any pool you would not want to upload.

POST/v1/selections

Record a keep list you computed locally from the scoring table. The pool never left your machine. Not an engine call.

forecast_id
Type string
Meaning the forecast the table came from
keep_fraction
Type number
Meaning fraction of the pool kept
pool_total_documents
Type int
Meaning documents scored
kept
Type int
Meaning documents kept
table_sha256, keep_sha256
Type string, optional
Meaning hashes of the table and of the keep list, for the receipt
client
Type string, optional
Meaning what computed it
Response
{"selection_id": "sel_1db5b923d18a", "forecast_id": "fc_7e2cc32e34e0", "forecast_verdict": "SELECT", "mode": "local", "recorded": {"keep_fraction": 0.25, "pool_total_documents": 812000, "kept": 203000, "table_sha256": "...", "keep_sha256": "..."}}
POST/v1/select

Server-side keep list for a small pool. Takes the same body as a forecast plus keep_fraction (default 0.25) and an optional forecast_id to reuse a verdict; without one it runs the forecast first. Counts as one engine call.

Response
{"selection_id": "sel_...", "forecast_id": "fc_...", "verdict": "SELECT", "mode": "targeted", "keep_fraction": 0.25, "pool_documents": 4000, "kept": 1000, "keep_indices": [3, 7, 12, ...], "hasher": "es-h1", "engine": "pilot-0.2", "note": "..."}

The scoring table and the hasher

The table is a plain published importance-weighting selector fitted on your own samples: one float per bigram bucket, sent as float32-le-base64 with its sha256. It says nothing about how the verdict was reached. To score a document, hash its word bigrams into buckets, average the table's values over them, and keep the top fraction of the pool by that score. The reference client does exactly this; the service's own selection agrees with it bit for bit.

GET/v1/hasher

The hashing a local scorer must reproduce, with test vectors. Not an engine call.

json
{"hasher": "es-h1", "buckets": 16384,
 "rule": "tokens = re.findall(r'\\S+', text.lower()); h = zlib.crc32(token.encode('utf-8')) & 0xFFFFFFFF; bucket = (h[i] * 1000003 + h[i+1]) % buckets",
 "test_vectors": [{"text": "The quick brown fox jumps over the lazy dog.", "word_hashes": [...], "buckets": [...]}, ...]}

If you port the scorer into your own pipeline, run the test vectors before every selection. A hasher that drifts produces a keep list that is silently wrong. The version (es-h1) is stamped on every forecast, table and selection.

Outcome

POST/v1/outcome

Report what the training run did. This is the receipt; a forecast without an outcome teaches neither of us anything. Not an engine call.

forecast_id
Type string
Meaning the forecast this run followed
acted
Type selected, random, both, none
Meaning which arms you trained
metric_name
Type string, default held-out loss
Meaning what the numbers measure
selected_value, baseline_value
Type number, optional
Meaning the curated arm and the random arm
lower_is_better
Type boolean, default true
Meaning direction of the metric
notes
Type string, optional
Meaning anything else, up to 2,000 characters
Response
{"outcome_id": "oc_a1f8bc1852a8", "forecast_id": "fc_...", "forecast_verdict": "SELECT", "forecast_agreed_with_outcome": true, "thanks": "recorded; this is the receipt"}

Where you can, train both arms: the curated keep list and a random keep list of the same size, same model, same budget, and report the held-out metric of each. One arm is still worth reporting.

Other calls

GET/v1/me

Your key: organisation, quota, calls used in the last 24 hours, forecasts on record.

GET/v1/forecasts?limit=50

Your forecasts, newest first, up to 500. The same rows appear in the dashboard ledger.

GET/v1/health

Service and engine version. No key needed.

The interactive OpenAPI reference is at https://api.echo-program.com/v1/docs and the schema at https://api.echo-program.com/v1/openapi.json.

Errors

401
Meaning missing, invalid or revoked API key
404
Meaning forecast_id not found for this key
409
Meaning /v1/select on a NO-FORECAST verdict
413
Meaning too many documents in one call
422
Meaning target or pool too small, empty, or malformed; the message says which
429
Meaning daily quota reached

Engine calls run one at a time; a call may wait a few seconds behind another. Retry on network failure is safe: a forecast that did not return was not recorded.

Data handling

For coding agents

Give your agent one paragraph. It does the rest.

Paste this into Claude Code, Cursor, Codex or any agent with a shell. It reads the plain-text reference, fetches the client, installs a skill that knows the workflow and the rules, and runs the self-test with your key.

Prompt
Set up the Echo Program pilot in this repository.
1. Read https://echo-program.com/llms-full.txt.
2. Download https://echo-program.com/client/echospace_pilot.py into tools/ (or the scripts folder this repo uses).
3. Save https://echo-program.com/agents/SKILL.md as .claude/skills/echospace/SKILL.md (or append https://echo-program.com/agents/AGENTS.md to AGENTS.md if this repo uses that).
4. Put ECHOSPACE_URL=https://api.echo-program.com in the environment and ask me for ECHOSPACE_API_KEY; never commit the key.
5. Run "python3 tools/echospace_pilot.py selftest" and show me the result.

The skill, the AGENTS.md section, llms.txt and this page are generated from one source, so they say the same thing. Below is what the skill teaches.

Setup

bash
curl -O https://echo-program.com/client/echospace_pilot.py      # Python 3, no dependencies
export ECHOSPACE_URL=https://api.echo-program.com
export ECHOSPACE_API_KEY=es_pilot_...                     # from the member's dashboard; never commit it
python3 echospace_pilot.py selftest                       # once per machine

Workflow

SELECT
Action curate with the scoring table via select-local; keep the top fraction
DO-NOT-SELECT
Action random sample of the same size; keep the compute
NO-FORECAST
Action no curation decision; add target data, rerun

Rules

Reference

The pilotBenchmarksBack to the ocean

Questions: [email protected]