Packages

Dataset management and caching for AI research benchmarks

Retired package: Deprecated - Use 0.5.0+

Current section

Files

Jump to
crucible_datasets lib dataset_manager data_files.ex
Raw

lib/dataset_manager/data_files.ex

defmodule CrucibleDatasets.DataFiles do
@moduledoc """
Resolve dataset file paths by config and split using HuggingFace Hub metadata.
Uses HfHub.Api.dataset_configs/2, HfHub.Api.dataset_splits/2, and
HfHub.Api.list_repo_tree/2 to infer configs and map split names to file paths.
"""
alias CrucibleDatasets.Format
@type file_info :: %{
path: String.t(),
format: atom(),
size: non_neg_integer() | nil
}
@type resolved :: %{
config: String.t() | nil,
splits: %{String.t() => [file_info()]}
}
@doc """
Resolve dataset file paths for each split.
## Options
* `:config` - Dataset config name (optional)
* `:split` - Dataset split name (optional)
* `:revision` - Git revision (default: "main")
* `:token` - HuggingFace token
"""
@spec resolve(String.t(), keyword()) :: {:ok, resolved()} | {:error, term()}
def resolve(repo_id, opts \\ []) when is_binary(repo_id) do
config_opt = Keyword.get(opts, :config)
split_opt = Keyword.get(opts, :split)
revision = Keyword.get(opts, :revision, "main")
token = Keyword.get(opts, :token)
with {:ok, tree} <-
HfHub.Api.list_repo_tree(repo_id,
repo_type: :dataset,
revision: revision,
token: token,
recursive: true
) do
configs = fetch_configs(repo_id, revision, token)
config = config_opt || default_config(configs, tree)
splits = resolve_splits(repo_id, config, tree, split_opt, revision, token)
case split_opt && Map.get(splits, to_string(split_opt)) do
nil when split_opt != nil ->
{:error, {:split_not_found, split_opt}}
_ ->
{:ok, %{config: config, splits: splits}}
end
end
end
defp fetch_configs(repo_id, revision, token) do
case HfHub.Api.dataset_configs(repo_id, revision: revision, token: token) do
{:ok, configs} -> configs
{:error, _} -> []
end
end
defp default_config([], tree) do
tree_configs = HfHub.DatasetFiles.configs_from_tree(tree)
pick_default_config(tree_configs)
end
defp default_config(configs, _tree) do
pick_default_config(configs)
end
defp pick_default_config(configs) do
cond do
"default" in configs -> "default"
"main" in configs -> "main"
configs == [] -> nil
true -> hd(configs)
end
end
defp resolve_splits(repo_id, config, tree, split_opt, revision, token) do
splits =
if split_opt do
[to_string(split_opt)]
else
splits_from_api(repo_id, config, revision, token, tree)
end
size_by_path = Map.new(tree, fn entry -> {entry.path, entry.size} end)
splits
|> Enum.reduce(%{}, fn split, acc ->
case HfHub.DatasetFiles.resolve_from_tree(tree, config, split) do
{:ok, paths} ->
file_infos = Enum.map(paths, &file_info(&1, size_by_path))
Map.put(acc, split, file_infos)
{:error, _} ->
acc
end
end)
end
defp splits_from_api(repo_id, config, revision, token, tree) do
case HfHub.Api.dataset_splits(repo_id, config: config, revision: revision, token: token) do
{:ok, splits} when splits != [] ->
splits
_ ->
splits = HfHub.DatasetFiles.splits_from_tree(tree, config)
if splits == [], do: ["train"], else: splits
end
end
defp file_info(path, size_by_path) do
%{
path: path,
format: Format.detect(path),
size: Map.get(size_by_path, path)
}
end
end