README
CryptoCuz — 8h crypto digest API
Stato: pre-alpha 0.0.0.1 — prima implementazione della SPEC (SPEC_crypto_8h_digest_api.md)
e dei piani BUILD_01–07. Un endpoint a chiamata singola che, dato un argomento crypto,
restituisce un briefing delle ultime 8 ore: cosa è successo, chi lo sta dicendo, quanto conta,
con fonti e livello di confidenza.
Avvio in locale
uv venv --python 3.12 .venv
uv pip install -e '.[dev]'
cp .env.example .env # compilare le chiavi che si hanno
.venv/bin/uvicorn app.main:app --port 8000
Senza alcuna chiave il servizio parte comunque (V10): x_social disabilitato e dichiarato,
store in-memory (solo dev), e la sintesi risponde 503 onesto finché manca DEEPSEEK_API_KEY.
curl -s localhost:8000/health
curl -s -X POST localhost:8000/v1/digest -H 'Content-Type: application/json' \
-d '{"topic":"hyperliquid","depth":"standard"}'
Test e lint:
.venv/bin/python -m pytest # 239 test, offline, niente rete (i @live sono esclusi)
.venv/bin/ruff check .
Deploy su Render (web service nativo, senza Docker e senza blueprint)
Il servizio si deploya come Web Service con runtime Python, configurato a mano
in dashboard. Niente render.yaml: il deploy resta un gesto esplicito.
| Campo | Valore |
|---|---|
| Runtime | Python 3 |
| Build command | pip install -r requirements.txt |
| Start command | uvicorn app.main:app --host 0.0.0.0 --port $PORT |
| Health check path | /health |
Due file nel repo servono al deploy e a nient'altro:
.python-version(3.12) — Render dal 11/02/2026 crea i servizi con Python 3.14; lo stack è validato su 3.12 e il pin evita che l'interprete cambi sotto i piedi al primo deploy. Alzarlo è una modifica deliberata, con suite verde.requirements.txt— lock delle versioni esatte con cui la suite gira verde.pyproject.tomlresta la fonte dei range ammessi; qui c'è la combinazione effettivamente provata. Il pacchetto non serve installarlo:uvicornmette la working dir in testa asys.path, quindiapp/,config/eregistry/si risolvono dal checkout.
Variabili d'ambiente (i segreti si compilano in dashboard, mai nel repo):
ENV=prod
RELAY_SHARED_SECRET=<segreto condiviso col relay> # oppure API_KEYS
SUPABASE_DB_URL=<connection string del pooler, porta 6543>
DEEPSEEK_API_KEY=<chiave>
SYNTH_MODEL=deepseek-v4-flash
X_BEARER_TOKEN=<opzionale: vuoto = collector X spento e costo social azzerato>
X_DAILY_CAP_USD=<soffitto giornaliero, default 15>
SCHEDULER_ENABLED=false
Tre cose che valgono la pena sapere prima di accendere:
ENV=prodè fail-closed, e va tenuto così. SenzaRELAY_SHARED_SECRET(oAPI_KEYS) il servizio si rifiuta di partire (app/auth.py), e lo stesso senzaSUPABASE_DB_URL(app/storage.py). Non aggirare l'errore mettendoENV=dev: in quel caso l'open_modeautentica chiunque, e ogni chiamata all'endpoint costa lettura X reale.SCHEDULER_ENABLED=falsefinché l'advisory lock non è sistemato.PostgresStorage.run_lockassume chepg_try_advisory_locksia di sessione, ma il pooler Supabase in transaction mode (6543) multiplexa più connessioni sullo stesso backend: due chiamanti ottengono entrambi il lock (è rientrante) e l'unlock singolo lascia il refcount a 1. L'esclusione «una sola esecuzione per orario» non vale (vedi BUILD_06 P4/D-INF-7, da rifare come lease su tabella).- La connection string deve essere quella del pooler (6543). L'host diretto
db.<ref>.supabase.co:5432è solo IPv6 e da Render non si risolve.statement_cache_size=0è già gestito nel codice (D-INF-2).
Le migration si applicano prima del primo avvio, da locale, puntando allo
stesso DB: python scripts/migrate.py (idempotente).
Architettura (SPEC §4)
POST /v1/digest → API layer (auth X-Relay-Key · rate limit · cache 300s)
→ Orchestrator: plan → collect → normalize → dedupe → score
→ select → synthesize (DeepSeek) → verify → assemble
→ DigestResponse (contratto congelato SPEC §5)
app/— API layer (BUILD_01): contratto Pydantic, vocabolario errori chiuso {200, 401, 422, 429, 503, 504}, cache, token bucket, request log.agent/— pipeline (BUILD_02): 9 step puri e testabili;verify()con i 5 guardrail (citazioni, URL, advice, finestra, numeri);confidencedeterministica.collectors/— adapter fonti (BUILD_03):news_rss,coingecko,defillamaattivi;x_socialdietro provider astratto, disabilitato senzaX_BEARER_TOKEN(assunzione aperta SPEC §17.2);MockCollectorcon i 6 scenari d'eval.llm/— client DeepSeek via SDK openai (BUILD_04):validated_call(1 retry trasporto + 1 retry semantico, tetto 3 richieste), parsing difensivo, contabilità token/costo.registry/—aliases.yaml(risoluzione progetti→handle/coingecko/DeFiLlama, config di repo) eRUBRIC.md(criteri). Il seed del registry NON è più qui: vive su Supabase (seed_accounts+registry_features.vote), popolato dall'app di seeding e letto conSeedRegistry.from_storage. La curation resta lavoro umano (quali handle + provenance).db/migrations/— schema Supabase/Postgres (BUILD_06 §3.1) + funzionetake_token.
Come aggiungere un collector
- Nuovo modulo in
collectors/sullo stampo dibase.BaseCollector(enabled(),_build_request,_parse,fetch, ≤max_upstream_calls). - Sezione in
config/sources.yaml(+depth_map). - Registrarlo in
collectors.base.build_collectors. - Test: dialetto + mapping su risposta registrata + invarianti di categoria (lista vuota ≠ errore; mai timestamp inventati).
Come aggiornare il registry
Il seed vive su Supabase (non più in un YAML di repo): la curation la fa l'app di seeding
(Doc B), il servizio lo legge a ogni avvio con SeedRegistry.from_storage e la
meta.registry_version diventa db-<n>. Sanity check del seed caricato:
python scripts/registry_tool.py lint. I criteri sono in registry/RUBRIC.md.
Dove si costruisce e si mantiene il registry (due flussi, due repo)
Il Notability Registry vive in un DB Supabase condiviso e si costruisce in due flussi distinti:
- Aggiornamento incrementale — questo repo. A ogni chiamata all'API di X il registro si
aggiorna con dati più freschi e registra i nuovi candidati; il servizio ne aggiorna score e
tier a regime. Guida operativa:
docs/registry_A_aggiornamento_incrementale.md. - Seeding iniziale — repo separato di data-prep. Popolamento della prima versione delle
tabelle da una lista di handle, arricchita coi dati dell'API di X. Documento autosufficiente:
docs/registry_B_seeding_iniziale.md.
Il voto di notabilità si costruisce col profiling ricco (7 segnali da analista, con un passo LLM di classificazione): il seeding ne calcola la parte da profilo (voto iniziale), questo repo lo fa evolvere coi segnali che richiedono storia. Disegno completo nei due doc sopra.
Cosa NON c'è ancora in questa pre-alpha (dichiarato)
- Backend Postgres: la suite di default gira sul fallback in-memory, ma il percorso asyncpg
è ora esercitato anche contro Supabase reale — schema applicato (
scripts/migrate.py, migrazioni 001–004), seed dei 675 account letto daseed_accounts+registry_features, e volano (registry_record_run) verificato end-to-end. Suite di integrazione opt-in:pytest -m integration. - Layer appreso del registry (BUILD_05 A3–A6): motore di consolidamento e scheduler esistono e le tabelle sono su Supabase, ma non hanno ancora accumulato storia reale da run live.
- Collector
dex_activityegovernance/github(differibili per D-COL-7/8). - Eval harness completo (
evals/, BUILD_02 P10): i casi bloccanti (topic morto, injection, astroturfing, fonte giù) sono già test CI intests/. - Probe di latenza DeepSeek (BUILD_04 P6) e worksheet dei costi (BUILD_07): richiedono chiave reale e vanno fatti prima del listing.
- Metriche Prometheus: per ora contatori JSON su
/internal/metricsdietro flag. - Listing su datapoint.market (BUILD_07 P5): richiede stop umano esplicito, per design.
Assunzioni aperte (SPEC §17 — decidere prima del listing)
Wallet Base (pay_to) · prezzo e budget mensile · dominio HTTPS dell'origin (immutabile
dopo il listing, BUILD_07 D6).
Chiuse il 19/07/2026 — fonte dati X: tier pay-per-use attivo ($0,005/post letto, cap
2M/mese; Basic e Pro sono chiusi ai nuovi signup). Costo per digest: $0,15 quick · $0,30
standard · $0,50 deep. Campi della scheda account verificati con chiamata reale
(scripts/smoke_x_fields.py): affiliation disponibile, verified_followers_count
non servito dal tier — rimosso da USER_FIELDS, segnale anti-bot ricostruito da
authenticity_proxy() (vedi registry/RUBRIC.md). Dettaglio in
docs/TDAT-001-attivazione-x-api.md; piano aggiornato in cryptocuz-piano-produzione-v2.md.
Target del registry — chiuso il 19/07/2026: 150 account seed curati, di cui 50
eval_split: holdout mai usati per la taratura del motore. Non è un numero tondo ma il
dimensionamento dell'eval set (BUILD_05 D-REG-10): sotto 100 etichette il gate G-reg-3 non
discrimina un motore all'80% da uno al 70%. Propagato in SPEC §16, DESIGN, BUILD_05 ×5,
BUILD_07 ×2, README, registry/RUBRIC.md. Contraddizione V4/D-REG-2 risolta.
✅ Residuo tecnico chiuso (19/07/2026): agent/notability.py legge e valida eval_split
(obbligatorio, {tune|holdout}, chiave mancante = errore duro), AccountInfo lo espone e
SeedRegistry.accounts_by_split() dà l'accesso per split. L'assegnazione è ancorata alla
funzione canonica eval_split_for(handle) (hash sha1 mod 3, ~1/3 holdout) — non a discrezione
di chi cura: from_storage DERIVA lo split dall'hash a runtime, a prova
… README truncated here; the full file ships with the clone.
119 files
file contents open where the seller allows a preview; the rest is what you are buying
agent/20
- agent/__init__.py0 B
- agent/assemble.py7 KB
- agent/collect.py4 KB
- agent/config.py1 KB
- agent/dedupe.py3 KB
- agent/models.py10 KB
- agent/normalize.py2 KB
- agent/notability.py13 KB
- agent/orchestrator.py7 KB
- agent/planner.py5 KB
- agent/registry_consolidate.py13 KB
- agent/registry_outcomes.py9 KB
- agent/registry_scoring.py9 KB
- agent/registry_sink.py2 KB
- agent/resolver.py5 KB
- agent/scoring.py6 KB
- agent/select.py2 KB
- agent/synthesizer.py7 KB
- agent/synthetic_season.py7 KB
- agent/verifier.py7 KB
app/14
- app/__init__.py0 B
- app/auth.py2 KB
- app/cache.py2 KB
- app/deps.py3 KB
- app/errors.py913 B
- app/main.py13 KB
- app/observability.py2 KB
- app/ratelimit.py669 B
- app/routes/__init__.py0 B
- app/routes/digest.py5 KB
- app/routes/internal.py1 KB
- app/scheduler.py8 KB
- app/schemas.py5 KB
- app/storage.py39 KB
collectors/8
config/7
db/6
llm/8
- llm/__init__.py0 B
- llm/accounting.py2 KB
- llm/calls.py2 KB
- llm/client.py5 KB
- llm/errors.py405 B
- llm/fake.py3 KB
- llm/parsing.py1 KB
- llm/profiles.py2 KB
out/2
registry/2
scripts/6
tests/25
- tests/conftest.py3 KB
- tests/fixtures/digest_response.schema.json15 KB
- tests/fixtures/digest_response_golden.json3 KB
- tests/integration/__init__.py0 B
- tests/integration/conftest.py4 KB
- tests/integration/test_pg_parity.py10 KB
- tests/test_api_layer.py12 KB
- tests/test_collectors.py12 KB
- tests/test_config_hygiene.py2 KB
- tests/test_eval_split.py4 KB
- tests/test_llm.py7 KB
- tests/test_notability_merge.py5 KB
- tests/test_orchestrator_e2e.py7 KB
- tests/test_pipeline_steps.py12 KB
- tests/test_registry_consolidate.py8 KB
- tests/test_registry_outcomes.py4 KB
- tests/test_registry_scoring.py11 KB
- tests/test_registry_storage.py4 KB
- tests/test_registry_supervision.py5 KB
- tests/test_resolver.py3 KB
- tests/test_scheduler.py6 KB
- tests/test_schema_contract.py2 KB
- tests/test_synthetic_season.py2 KB
- tests/test_verifier.py6 KB
- tests/test_x_budget.py11 KB
- .env.example2 KB
- .gitignore142 B
- .python-version5 B
- BUILD_01_servizio_api_origin.md29 KB
- BUILD_02_worker_pipeline.md32 KB
- BUILD_03_collectors.md29 KB
- BUILD_04_integrazione_deepseek.md27 KB
- BUILD_05_notability_registry.md41 KB
- BUILD_06_infrastruttura.md34 KB
- BUILD_07_marketplace_pricing.md29 KB
- DESIGN_crypto_8h_digest_tecnico.md34 KB
- Dockerfile571 B
- PITCH_criptocuz_business.pdf262 KB
- README.md10 KB
- SPEC_crypto_8h_digest_api.md45 KB
- TRACKER.md17 KB
- docker-compose.yml522 B
- pyproject.toml1 KB
- requirements.txt1 KB