| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324 |
- # Copyright 2026 The HuggingFace Team. All rights reserved.
- #
- # Licensed under the Apache License, Version 2.0 (the "License");
- # you may not use this file except in compliance with the License.
- # You may obtain a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- # See the License for the specific language governing permissions and
- # limitations under the License.
- """Contains commands to interact with collections on the Hugging Face Hub.
- Usage:
- # list collections on the Hub
- hf collections ls
- # list collections for a specific user
- hf collections ls --owner username
- # get info about a collection
- hf collections info username/collection-slug
- # create a new collection
- hf collections create "My Collection" --description "A collection of models"
- # add an item to a collection
- hf collections add-item username/collection-slug username/model-name model
- # delete a collection
- hf collections delete username/collection-slug
- """
- import enum
- from typing import Annotated, get_args
- import typer
- from huggingface_hub.hf_api import CollectionItemType_T, CollectionSort_T
- from ._cli_utils import FormatWithAutoOpt, LimitOpt, TokenOpt, api_object_to_dict, get_hf_api, typer_factory
- from ._output import OutputFormatWithAuto, out
- # Build enums dynamically from Literal types to avoid duplication
- _COLLECTION_ITEM_TYPES = get_args(CollectionItemType_T)
- CollectionItemType = enum.Enum("CollectionItemType", {t: t for t in _COLLECTION_ITEM_TYPES}, type=str) # type: ignore[misc]
- _COLLECTION_SORT_OPTIONS = get_args(CollectionSort_T)
- CollectionSort = enum.Enum("CollectionSort", {s: s for s in _COLLECTION_SORT_OPTIONS}, type=str) # type: ignore[misc]
- collections_cli = typer_factory(help="Interact with collections on the Hub.")
- @collections_cli.command(
- "list | ls",
- examples=[
- "hf collections ls",
- "hf collections ls --owner nvidia",
- "hf collections ls --item models/teknium/OpenHermes-2.5-Mistral-7B --limit 10",
- ],
- )
- def collections_ls(
- owner: Annotated[
- str | None,
- typer.Option(help="Filter by owner username or organization."),
- ] = None,
- item: Annotated[
- str | None,
- typer.Option(
- help='Filter collections containing a specific item (e.g., "models/gpt2", "datasets/squad", "papers/2311.12983").'
- ),
- ] = None,
- sort: Annotated[
- CollectionSort | None,
- typer.Option(help="Sort results by last modified, trending, or upvotes."),
- ] = None,
- limit: LimitOpt = 10,
- format: FormatWithAutoOpt = OutputFormatWithAuto.auto,
- token: TokenOpt = None,
- ) -> None:
- """List collections on the Hub."""
- api = get_hf_api(token=token)
- sort_key = sort.value if sort else None
- results = [
- api_object_to_dict(collection)
- for collection in api.list_collections(
- owner=owner,
- item=item,
- sort=sort_key, # type: ignore[arg-type]
- limit=limit,
- )
- ]
- out.table(results)
- @collections_cli.command(
- "info",
- examples=[
- "hf collections info username/my-collection-slug",
- ],
- )
- def collections_info(
- collection_slug: Annotated[str, typer.Argument(help="The collection slug (e.g., 'username/collection-slug').")],
- format: FormatWithAutoOpt = OutputFormatWithAuto.auto,
- token: TokenOpt = None,
- ) -> None:
- """Get info about a collection on the Hub."""
- api = get_hf_api(token=token)
- collection = api.get_collection(collection_slug)
- out.dict(collection)
- @collections_cli.command(
- "create",
- examples=[
- 'hf collections create "My Models"',
- 'hf collections create "My Models" --description "A collection of my favorite models" --private',
- 'hf collections create "Org Collection" --namespace my-org',
- ],
- )
- def collections_create(
- title: Annotated[str, typer.Argument(help="The title of the collection.")],
- namespace: Annotated[
- str | None,
- typer.Option(help="The namespace (username or organization). Defaults to the authenticated user."),
- ] = None,
- description: Annotated[
- str | None,
- typer.Option(help="A description for the collection."),
- ] = None,
- private: Annotated[
- bool,
- typer.Option(help="Create a private collection."),
- ] = False,
- exists_ok: Annotated[
- bool,
- typer.Option(help="Do not raise an error if the collection already exists."),
- ] = False,
- format: FormatWithAutoOpt = OutputFormatWithAuto.auto,
- token: TokenOpt = None,
- ) -> None:
- """Create a new collection on the Hub."""
- api = get_hf_api(token=token)
- collection = api.create_collection(
- title=title,
- namespace=namespace,
- description=description,
- private=private,
- exists_ok=exists_ok,
- )
- out.result("Collection created", slug=collection.slug, url=collection.url)
- @collections_cli.command(
- "update",
- examples=[
- 'hf collections update username/my-collection --title "New Title"',
- 'hf collections update username/my-collection --description "Updated description"',
- "hf collections update username/my-collection --private --theme green",
- ],
- )
- def collections_update(
- collection_slug: Annotated[str, typer.Argument(help="The collection slug (e.g., 'username/collection-slug').")],
- title: Annotated[
- str | None,
- typer.Option(help="The new title for the collection."),
- ] = None,
- description: Annotated[
- str | None,
- typer.Option(help="The new description for the collection."),
- ] = None,
- position: Annotated[
- int | None,
- typer.Option(help="The new position of the collection in the owner's list."),
- ] = None,
- private: Annotated[
- bool | None,
- typer.Option(help="Whether the collection should be private."),
- ] = None,
- theme: Annotated[
- str | None,
- typer.Option(help="The theme color for the collection (e.g., 'green', 'blue')."),
- ] = None,
- format: FormatWithAutoOpt = OutputFormatWithAuto.auto,
- token: TokenOpt = None,
- ) -> None:
- """Update a collection's metadata on the Hub."""
- api = get_hf_api(token=token)
- collection = api.update_collection_metadata(
- collection_slug=collection_slug,
- title=title,
- description=description,
- position=position,
- private=private,
- theme=theme,
- )
- out.result("Collection updated", slug=collection.slug, url=collection.url)
- @collections_cli.command(
- "delete",
- examples=[
- "hf collections delete username/my-collection",
- "hf collections delete username/my-collection --missing-ok",
- ],
- )
- def collections_delete(
- collection_slug: Annotated[str, typer.Argument(help="The collection slug (e.g., 'username/collection-slug').")],
- missing_ok: Annotated[
- bool,
- typer.Option(help="Do not raise an error if the collection doesn't exist."),
- ] = False,
- format: FormatWithAutoOpt = OutputFormatWithAuto.auto,
- token: TokenOpt = None,
- ) -> None:
- """Delete a collection from the Hub."""
- api = get_hf_api(token=token)
- api.delete_collection(collection_slug, missing_ok=missing_ok)
- out.result("Collection deleted", slug=collection_slug)
- @collections_cli.command(
- "add-item",
- examples=[
- "hf collections add-item username/my-collection moonshotai/kimi-k2 model",
- 'hf collections add-item username/my-collection Qwen/DeepPlanning dataset --note "Useful dataset"',
- "hf collections add-item username/my-collection Tongyi-MAI/Z-Image space",
- ],
- )
- def collections_add_item(
- collection_slug: Annotated[str, typer.Argument(help="The collection slug (e.g., 'username/collection-slug').")],
- item_id: Annotated[
- str, typer.Argument(help="The ID of the item to add (repo_id for repos, paper ID for papers).")
- ],
- item_type: Annotated[
- CollectionItemType,
- typer.Argument(help="The type of item (model, dataset, space, paper, collection, or bucket)."),
- ],
- note: Annotated[
- str | None,
- typer.Option(help="A note to attach to the item (max 500 characters)."),
- ] = None,
- exists_ok: Annotated[
- bool,
- typer.Option(help="Do not raise an error if the item is already in the collection."),
- ] = False,
- format: FormatWithAutoOpt = OutputFormatWithAuto.auto,
- token: TokenOpt = None,
- ) -> None:
- """Add an item to a collection."""
- api = get_hf_api(token=token)
- collection = api.add_collection_item(
- collection_slug=collection_slug,
- item_id=item_id,
- item_type=item_type.value, # type: ignore[arg-type]
- note=note,
- exists_ok=exists_ok,
- )
- out.result("Item added to collection", slug=collection_slug, url=collection.url)
- @collections_cli.command(
- "update-item",
- examples=[
- 'hf collections update-item username/my-collection ITEM_OBJECT_ID --note "Updated note"',
- "hf collections update-item username/my-collection ITEM_OBJECT_ID --position 0",
- ],
- )
- def collections_update_item(
- collection_slug: Annotated[str, typer.Argument(help="The collection slug (e.g., 'username/collection-slug').")],
- item_object_id: Annotated[
- str,
- typer.Argument(help="The ID of the item in the collection (from 'item_object_id' field, not the repo_id)."),
- ],
- note: Annotated[
- str | None,
- typer.Option(help="A new note for the item (max 500 characters)."),
- ] = None,
- position: Annotated[
- int | None,
- typer.Option(help="The new position of the item in the collection."),
- ] = None,
- format: FormatWithAutoOpt = OutputFormatWithAuto.auto,
- token: TokenOpt = None,
- ) -> None:
- """Update an item in a collection."""
- api = get_hf_api(token=token)
- api.update_collection_item(
- collection_slug=collection_slug,
- item_object_id=item_object_id,
- note=note,
- position=position,
- )
- out.result("Item updated in collection", slug=collection_slug)
- @collections_cli.command("delete-item")
- def collections_delete_item(
- collection_slug: Annotated[str, typer.Argument(help="The collection slug (e.g., 'username/collection-slug').")],
- item_object_id: Annotated[
- str,
- typer.Argument(
- help="The ID of the item in the collection (retrieved from `item_object_id` field returned by 'hf collections info'."
- ),
- ],
- missing_ok: Annotated[
- bool,
- typer.Option(help="Do not raise an error if the item doesn't exist."),
- ] = False,
- format: FormatWithAutoOpt = OutputFormatWithAuto.auto,
- token: TokenOpt = None,
- ) -> None:
- """Delete an item from a collection."""
- api = get_hf_api(token=token)
- api.delete_collection_item(
- collection_slug=collection_slug,
- item_object_id=item_object_id,
- missing_ok=missing_ok,
- )
- out.result("Item deleted from collection", slug=collection_slug)
|