# How to Use Literal Types for Fixed Choice Sets in Needle Tools

> Master Literal types in Needle tools to create fixed choice sets. Automatically generate JSON schema enums restricting LLM inputs to allowed values for precise control.

- Repository: [Cactus Compute, Inc./needle](https://github.com/cactus-compute/needle)
- Tags: how-to-guide
- Published: 2026-09-05

---

**Annotate function parameters with `typing.Literal` to automatically generate JSON schema enums that restrict LLM inputs to specific allowed values.**

Needle is a Python framework that creates OpenAI-compatible JSON schemas directly from function signatures. When you need to constrain user inputs to a predefined set of options, **Literal types for fixed choice sets** eliminate manual schema construction while ensuring type safety.

## How Needle Converts Literal Annotations to JSON Schema Enums

The transformation from Python type hints to structured constraints happens in [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py). The `_json_type` helper function inspects parameter annotations and detects `Literal` types at line 73. When encountered, it extracts the allowed values (the Literal arguments) and constructs a JSON schema entry containing both the primitive type and an `enum` property at lines 74-76.

During schema assembly, the `build_schema` method (lines 28-33) processes each parameter through `_json_type`. If the resulting schema contains an `enum` field, Needle preserves this constraint in the final output. This automated pipeline ensures that any parameter annotated with `Literal` automatically enforces fixed choice validation at the LLM level.

## Implementing String Literal Constraints

To expose a controlled vocabulary to the LLM, apply the `@tool` decorator to a function using `Literal` annotations for fixed choice parameters:

```python
from typing import Literal
from needle import tool

@tool
def set_mode(mode: Literal["fast", "slow", "auto"]) -> str:
    """Select the operation mode."""
    return f"Mode set to {mode}"

```

Needle generates the following OpenAI-compatible schema, storing it in `set_mode._needle_tool`:

```json
{
  "name": "set_mode",
  "description": "Select the operation mode.",
  "parameters": {
    "type": "object",
    "properties": {
      "mode": {
        "type": "string",
        "enum": ["fast", "slow", "auto"]
      }
    },
    "required": ["mode"]
  }
}

```

The resulting `enum` array contains exactly the values specified in the `Literal` annotation, preventing the LLM from hallucinating invalid options.

## Working with Numeric Literals and Enums

**Literal types** support any hashable constants, including integers and enumerated values. When combining standard library `enum` classes with `Literal` annotations, Needle correctly resolves both into appropriate JSON schema constraints:

```python
from typing import Literal
import enum
import needle

class Level(enum.IntEnum):
    LOW = 1
    MEDIUM = 2
    HIGH = 3

@needle.tool
def configure(port: Literal[80, 443, 8080], level: Level) -> None:
    """Configure service with restricted port options and priority levels."""
    pass

```

This definition produces schema properties that enforce both the specific port numbers and the enumerated priority levels:

```json
{
  "port": {"type": "integer", "enum": [80, 443, 8080]},
  "level": {"type": "integer", "enum": [1, 2, 3]}
}

```

## Verification and Testing

The test suite in [`tests/test_tools.py`](https://github.com/cactus-compute/needle/blob/main/tests/test_tools.py) validates this behavior through `test_literal_list_and_dict_types` (lines 36-44). This test confirms that parameters annotated with `Literal["fast", "slow"]` correctly produce string-type properties with `enum: ["fast", "slow"]` in the generated schema, ensuring the mapping from Python type hints to JSON schema remains consistent across Needle versions.

## Summary

- **Type annotation**: Use `typing.Literal[...]` to define fixed choice sets directly in function signatures.
- **Automatic enum generation**: Needle's `_json_type` helper in [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py) converts these annotations into JSON schema `enum` properties.
- **Broad compatibility**: Supports strings, integers, and `enum.IntEnum` values as Literal arguments.
- **Zero configuration**: The `@tool` decorator handles schema construction via `build_schema` without requiring manual JSON editing.

## Frequently Asked Questions

### What happens if I use mixed types within a single Literal annotation?

Needle processes `Literal` arguments according to JSON schema specification requirements. While Python's `typing` module allows heterogeneous literals (e.g., `Literal["fast", 1]`), you should keep types consistent within a single annotation to ensure the generated schema produces valid OpenAI tool definitions. The `_json_type` function preserves the primitive type of the first argument while collecting all values into the `enum` array.

### Can Literal types be combined with Optional or default values?

Yes. Needle correctly handles parameters defined as `Optional[Literal["a", "b"]]` or parameters with default values. The schema generation pipeline in `build_schema` processes the underlying type through `_json_type` before applying nullability constraints or marking parameters as optional in the `required` array.

### How does the LLM know which values are valid?

The generated JSON schema includes the `enum` property containing the exact set of allowed values from your `Literal` annotation. When the LLM calls the tool, the inference provider validates the input against this enum before executing your Python function, effectively preventing out-of-range values from reaching your application logic.

### Where does Needle store the generated schema?

After applying the `@tool` decorator, Needle attaches the complete OpenAI-compatible schema to the function's `_needle_tool` attribute. You can inspect this dictionary to verify that your **Literal types for fixed choice sets** correctly translated into the expected JSON schema constraints.