Skip to content

Rewrite hf-cli into a full Hugging Face discovery tool #3

Description

@tamnd

Rewrite hf-cli into a full Hugging Face discovery tool

Spec: ~/notes/Spec/3001/ (00 overview, 01 surfaces, 02 html, 03 model, 04 graph, 05 commands, 06 implementation).

The goal is to read everything huggingface.co publishes and turn it into structured, linked data.
Two output planes: records (one shape per entity) and a graph (nodes and edges with stable hf:// URIs).
The API is the primary source and the rendered HTML is the fallback, so no field on a page is lost just because no endpoint returns it.

This issue is the checklist. It gets ticked as PRs land.

M0 foundations

  • Drop the scaffold: hf/hf.go and hf/domain.go and their tests
  • Point the domain at the real host, huggingface.co, with hf.co and www. as aliases
  • Meta envelope on every record: uri, url, kind, sources, fetchedAt, aliasOf
  • Extra map[string]json.RawMessage on every record, filled by a shared decoder
  • Gated three state decode: false, "auto", "manual"
  • StringList decode for card fields that are a scalar or a list
  • Time decode for the several timestamp shapes the hub sends
  • URI space: URI, SplitURI, composite ids for discussion, commit, ref, file, split
  • Classify for every input form: hf://, hub URL, arxiv URL, @handle, bare id
  • Locate for every kind
  • Client: auth from --token, HF_TOKEN, HUGGING_FACE_HUB_TOKEN, $HF_HOME/token
  • Client: shared pacer so N workers make one polite stream
  • Client: retry on 429 and 5xx, honour Retry-After, exponential backoff with jitter
  • Client: on disk response cache, long TTL for taxonomy, no expiry for sha pinned URLs
  • Cursor pagination over the Link header, stop on missing next rather than a short page
  • Status to exit code mapping: 2 usage, 3 no results, 4 auth, 5 rate limit, 6 not found, 7 unsupported, 8 network
  • Redirect detection so a legacy id fills aliasOf

M1 repositories

  • Repo base shared by model, dataset, space, kernel
  • Model with config, transformersInfo, safetensors, gguf, modelIndex, inference providers
  • EvalResult flattened one row per metric out of model-index
  • Dataset with promoted card fields, configs, datasetInfo
  • Space with runtime, hardware, replicas, domains
  • Kernel
  • Card front matter parse, universal plus model plus dataset plus space keys
  • Card parse failure is not fatal, record keeps going with cardError
  • hf model, hf dataset, hf space, hf kernel, hf get
  • Expand parameter sets per repo kind, verified against the server's own 400 error list

M2 lists and search

  • hf models, hf datasets, hf spaces, hf kernels with full cursor pagination
  • Filter sugar: --task, --library, --language, --license, --sdk all folding into filter=
  • --author, --search, --sort, --direction, --gated, --full
  • hf search across every entity kind, with per type counts
  • hf trending
  • hf tags, the controlled vocabulary by type
  • hf tasks
  • hf sitemap

M3 namespaces, collections, papers

  • User and Org records with every count field
  • UserRef name normalisation, the user and name keys are the same value
  • Avatar URLs absolutised
  • user or org resolution with a per process memo
  • hf user, hf org, hf ns
  • Collection with slug split into namespace, short slug, object id
  • CollectionItem including the curator note and the page only numParameters
  • hf collection, hf collections, including --item reverse lookup
  • Paper with authors, daily feed fields, ai summary and keywords
  • hf paper, hf papers, hf citations

M4 social

  • Post with the token array content, rendered to a body
  • BlogPost from the RSS index
  • Discussion with the event thread, comments, reactions
  • hf posts, hf blogs, hf discussions, hf discussion
  • hf likers, hf likes, hf followers, hf following, hf members
  • the >=10 minimum limit on social endpoints handled, not passed through as an error

M4b the HTML plane

  • Byte scanner for SVELTE_HYDRATER payloads, no DOM parse
  • Secret deny list at extraction time: csrf, jwt, sessionUuid, postLoginRedirectUrl, apiUrlPrefix, anything matching token, secret, password
  • JSON-LD blocks, OG and Twitter meta, canonical link
  • Unknown components kept, so a page hf has never seen still round trips
  • Page record and hf page
  • Merge precedence ladder: dedicated endpoint, detail endpoint, page payload, JSON-LD, meta, DOM, list stub
  • --deep on repo, user, org, collection, paper reads
  • A page fetch failure never fails a read that already succeeded

M5 repository contents

  • Ref, TreeEntry, Commit, LFSInfo, SecurityStatus, pickle import scan
  • Computed download and raw URLs on every tree entry
  • hf refs, hf tree, hf files, hf commits, hf paths
  • hf cat and hf readme as byte output escape hatches
  • hf card

M6 dataset viewer

  • Validity, Split, Size, Row, Feature, ColumnStats, Histogram, ParquetShard
  • Pointer numeric stats so an all zero column is not confused with an absent one
  • hf valid, hf splits, hf size, hf rows, hf head, hf schema, hf stats
  • hf dsearch, hf filter, hf parquet, hf croissant
  • Rows stream as pages arrive, so hf rows x | head is cheap

M7 the graph

  • Node and Edge types with a source on every edge: api, tag, card, page, derived
  • Tag parsing for every namespace: license, arxiv, doi, base_model with its four relations, dataset, region, language
  • Taxonomy lookup so a bare word becomes a library or a task rather than a free tag
  • The full predicate vocabulary from spec 04, all six groups
  • hf:derivesFrom emitted alongside each of the four specific derivation predicates
  • Eval results reified as blank nodes, the one place blank nodes are used
  • Contradictions between card and tag both emitted, with different sources, never silently resolved
  • hf graph, hf edges, hf uri, hf url

M8 crawl

  • Breadth first frontier with a visited set on canonical URI
  • --follow predicate allow list, structural by default
  • --depth, --max-nodes, --max-requests, clean stop at a budget with a report
  • Streaming emission, never buffer the whole graph
  • Resumable through --db
  • Reverse edges only when explicitly followed
  • Refuse a tag to repo expansion above the node budget and say so

M9 RDF and bulk

  • N-Triples, Turtle, JSON-LD, N-Quads with the source URL as graph name
  • Typed literals: xsd:integer, xsd:dateTime, xsd:boolean
  • schema.org alignment emitted alongside the hf predicates
  • Croissant passthrough, not a reimplementation
  • hf rdf, hf dump, hf export
  • Confirmation prompt when a bulk estimate exceeds 100k requests

Tests

  • Recorded fixtures from the live hub for every endpoint in spec 01
  • make fixtures to refresh them, not part of make test
  • Every fixture asserts extra is empty, so a new upstream field fails loudly and names itself
  • Table driven unit tests for Classify, Locate, tag parsing, StringList, Gated, Time
  • Golden output tests per format
  • No test touches the network

Docs

  • README rewritten around the two planes
  • docs site pages for surfaces, records, graph, commands
  • Examples that are real pipelines, not toy invocations

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions