> For the complete documentation index, see [llms.txt](https://gilad-rubin.gitbook.io/hypster/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://gilad-rubin.gitbook.io/hypster/in-depth/hp-call-types/select-and-multi-select.md).

# Selectable Types

Use `hp.select()` and `hp.multi_select()` for categorical choices.

Selected choices are part of Hypster's reproducibility surface. They are what `instantiate_with_params(...).params` records and what you pass back through `values=...` to replay a run.

## Signatures

{% code overflow="wrap" %}

```python
hp.select(options, *, name, default=NO_DEFAULT, options_only=False, allow_none=False, hpo_spec=None)
hp.multi_select(options, *, name, default=None, options_only=False, allow_none=False)
```

{% endcode %}

## List Form

Use list form when the logged choice and returned value are the same simple value:

{% code overflow="wrap" %}

```python
from hypster import HP, instantiate

def config(hp: HP):
    model = hp.select(
        ["claude-haiku-4-5", "claude-sonnet-4-6"],
        name="model",
        default="claude-haiku-4-5",
    )
    features = hp.multi_select(["cache", "trace"], name="features", default=["cache"])
    return {"model": model, "features": features}

instantiate(
    config,
    values={"model": "claude-sonnet-4-6", "features": ["cache", "trace"]},
)
# => {"model": "claude-sonnet-4-6", "features": ["cache", "trace"]}
```

{% endcode %}

List-form choices must be logging-safe scalar values: `None`, `bool`, `int`, `float`, or `str`. If you need a complex object, use dictionary form.

## Dictionary Form

Use dictionary form when a simple logged key should return a different value. The key is logged and replayed; the mapped value is returned from the config.

{% code overflow="wrap" %}

```python
from hypster import HP, instantiate_with_params

def config(hp: HP):
    model = hp.select(
        {
            "small": {"layers": 2, "units": [64, 32]},
            "large": {"layers": 4, "units": [256, 128]},
        },
        name="model",
        default="small",
    )
    return {"model": model}

run = instantiate_with_params(config, values={"model": "large"})

assert run.value == {"model": {"layers": 4, "units": [256, 128]}}
assert run.params == {"model": "large"}
```

{% endcode %}

Use `options_only=True` with dictionary form when the logged keys are a closed enum:

{% code overflow="wrap" %}

```python
def strict_config(hp: HP):
    model = hp.select(
        {
            "small": {"layers": 2},
            "large": {"layers": 4},
        },
        name="model",
        default="small",
        options_only=True,
    )
    return {"model": model}

run = instantiate_with_params(strict_config, values={"model": "large"})

assert run.value == {"model": {"layers": 4}}
assert run.params == {"model": "large"}
```

{% endcode %}

Dictionary form is the recommended way to return:

* objects or callables
* dictionaries, lists, or tuples that your runtime actually consumes
* long provider/model IDs behind short aliases

{% code overflow="wrap" %}

```python
architecture = hp.select(
    {
        "small": {"layers": 2, "units": [64, 32]},
        "large": {"layers": 4, "units": [256, 128]},
    },
    name="architecture",
    default="small",
)
```

{% endcode %}

For nullable choices, you can use `None` directly in list-form options with `allow_none=True`.

## Explicit None

If `None` itself is a selectable choice or override, mark the parameter as nullable with `allow_none=True`:

{% code overflow="wrap" %}

```python
def config(hp: HP):
    thinking_level = hp.select(
        [None, "low", "medium", "high"],
        name="thinking_level",
        default=None,
        allow_none=True,
    )
    features = hp.multi_select(
        [None, "cache", "trace"],
        name="features",
        default=[None],
        allow_none=True,
    )
    return {"thinking_level": thinking_level, "features": features}
```

{% endcode %}

Without `allow_none=True`, `None` defaults, choices, and overrides raise with guidance.

## Empty Nullable Selects

An empty option list can default to `None` when the parameter is explicitly nullable:

{% code overflow="wrap" %}

```python
def config(hp: HP):
    return hp.select([], name="choice", allow_none=True)

assert instantiate(config) is None
```

{% endcode %}

Without `allow_none=True`, an empty option list with no explicit default raises because Hypster has no safe value to select.

## Custom Choices

By default, `options_only=False`, so callers may provide a custom choice outside the declared options:

{% code overflow="wrap" %}

```python
def config(hp: HP):
    return hp.select(["claude-haiku-4-5", "claude-sonnet-4-6"], name="model")

assert instantiate(config, values={"model": "claude-opus-4-7"}) == "claude-opus-4-7"
```

{% endcode %}

Custom choices must still be logging-safe scalar values. Use `options_only=True` to reject anything outside the declared options:

{% code overflow="wrap" %}

```python
def config(hp: HP):
    return hp.select(
        ["claude-haiku-4-5", "claude-sonnet-4-6"],
        name="model",
        options_only=True,
    )

instantiate(config, values={"model": "claude-opus-4-7"})
# ValueError: 'claude-opus-4-7' not in allowed options
```

{% endcode %}

## Names

`name=` must be a valid Python identifier and cannot be a Python keyword. Hypster composes dotted parameter paths from nested names, so literal dots, spaces, and hyphens are not allowed in individual names.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://gilad-rubin.gitbook.io/hypster/in-depth/hp-call-types/select-and-multi-select.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
