> ## Documentation Index
> Fetch the complete documentation index at: https://docs.thoras.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# AIScaleTarget

The specification describes the `AIScaleTarget` Custom Resource that defines how
Thoras scales workloads.

The following is a sample `AIScaleTarget` definition:

```yaml ast.yaml theme={null}
apiVersion: thoras.ai/v1
kind: AIScaleTarget
metadata:
  name: "{{ YOUR_AST_NAME }}"
  namespace: "{{ YOUR_NAMESPACE }}"
spec:
  scaleTargetRef:
    kind: Deployment
    name: "{{ YOUR_AST_NAME }}"
  model:
    forecast_blocks: 4h
    forecast_buffer_percentage: 0%
    forecast_interval: 1h
    mode: balanced
  horizontal:
    mode: recommendation
    scaling_behavior:
      scale_up:
        type: percent
        percent: 50
      scale_down:
        type: percent
        percent: 50
  vertical:
    containers:
      - name: "{{ CONTAINER_NAME }}"
        cpu:
          lowerbound: 20m
          upperbound: 1
        memory:
          lowerbound: 50Mi
          upperbound: 2G
    mode: recommendation
    scaling_behavior:
      scale_up:
        type: percent
        percent: 50
      scale_down:
        type: percent
        percent: 50
    update_policy:
      update_mode: in_place_or_recreate
```

## `metadata`

```yaml ast.yaml theme={null}
metadata:
  name: {{ YOUR_AST_NAME }}
  namespace: {{ YOUR_NAMESPACE }}
```

It's recommended that `metadata.name` matches the name of the workload being
scaled.

## `scaleTargetRef`

```yaml ast.yaml theme={null}
scaleTargetRef:
  kind: Deployment
  name: {{YOUR_AST_NAME}}
  namespace: {{YOUR_NAMESPACE}}
  apiVersion: apps/v1
```

`scaleTargetRef` is the reference to a specific workload that Thoras will scale
up or down. The workload must reside in the same namespace as the
`AIScaleTarget`.

`apiVersion` specifies the API group and version that defines the resource,
while `kind` identifies the type of resource (e.g., `Deployment`, `StatefulSet`,
`Rollout`, etc).

**Note:** `scaleTargetRef` and `selector` are mutually exclusive. Use
`scaleTargetRef` to target a single workload by name, or use `selector` to
target multiple workloads by label.

## `selector`

```yaml ast.yaml theme={null}
selector:
  matchLabels:
    app: my-service
    environment: production
```

`selector` allows you to target multiple workloads by
[pod labels](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/)
instead of specifying a single workload by name. This is useful when you want to
apply the same scaling policy to multiple workloads that share common labels.

**Important constraints:**

* `selector` and `scaleTargetRef` are mutually exclusive, you must use one or the
  other, not both.
* When using `selector`, horizontal scaling is **not supported**. You must leave
  `spec.horizontal` empty or omit it entirely.
* Pods must be managed by a Deployment, StatefulSet, or Argo Rollout. Standalone
  pods or other controller types are not supported.

**How it works:**

When a `selector` is defined, Thoras identifies all pod controllers
(Deployments, StatefulSets, Rollouts) in the same namespace whose pods match the
label selector.

In `autonomous` mode:

* Thoras restarts each matching pod controller one by one when a resource
  request suggestion comes in from the forecaster.
* If `update_policy.update_mode` is set to `in_place` or `in_place_or_recreate`,
  pods are resized in place without restarts (when possible).

## `model`

```yaml ast.yaml theme={null}
model:
  forecast_blocks: 4h
  forecast_buffer_percentage: 0%
  forecast_interval: 1h
```

## `model.forecast_blocks`

Describes how far into the future Thoras' scaler should prepare you for. For
example, if `forecast_blocks` is `4h` Thoras will forecast the maximum load of
this workload over the next 4 hours and then scale to that maximum.

**Important:** `forecast_blocks` should be at least **equal** to your forecast
interval. For example, if forecasts run every `1h`, your `forecast_blocks`
should be at least `1h`. This ensures forecasts always cover the next scaling
window and prevents gaps in scaling coverage.

You will need to specify either the "m" (for minutes) or "h" (for hours) unit
for the value of [model.forecast\_blocks](#model-forecast-blocks).

## `model.forecast_buffer_percentage`

Defines an additional buffer applied on top of the forecasted resource usage to
reduce the risk of under-provisioning.

For example, if the forecasted CPU usage is 500m and
`forecast_buffer_percentage` is set to 10%, the final recommended value will be
550m. Setting this to 0% means no buffer will be added.

## `model.forecast_interval`

Controls how frequently Thoras generates forecasts and evaluates scaling.
Accepts a plain duration string (e.g., `"15m"`, `"1h"`). Shorter intervals can
lead to more accurate forecasts but may result in more frequent scaling.

`forecast_interval` controls how often forecasts are made, while
[model.forecast\_blocks](#model-forecast-blocks) defines the time window each
forecast covers.

## `model.mode`

Defines the optimization strategy that controls how tightly forecast
recommendations track expected usage.

Thoras modes span a spectrum from efficiency-focused to assurance-focused:

`undershoot` → `cost_optimized` → `balanced` → `assurance_optimized` →
`overshoot`

Available modes:

* `undershoot` - Most aggressive strategy, allowing recommendations to run below
  usage more often
* `cost_optimized` - Tighter recommendations with increased efficiency focus
* `balanced` (default) - Balanced reliability and efficiency for general
  production workloads
* `assurance_optimized` - More conservative recommendations that track above
  typical usage patterns
* `overshoot` - Most conservative strategy, maintaining additional headroom
  based on historical usage
* `percentile` - Deterministic history-based forecast set at a specific
  percentile of observed usage. Requires `percentile_value` (0–100). An integer
  or quoted decimal is accepted (e.g. `75` or `"99.9"`).

If not specified, the default is `balanced`.

**Visit
[Choosing a Forecast Optimization Strategy](/guides/forecast-optimization-strategies)
for detailed guidance on selecting the right mode for your workload.**

## `model.metrics`

```yaml ast.yaml theme={null}
model:
  mode: balanced
  metrics:
    cpu:
      mode: undershoot
    memory:
      mode: balanced
      forecast_buffer_percentage: 10%
```

Per-metric overrides allow you to configure different forecast settings for CPU
and memory independently, overriding the global `model.mode` and
`model.forecast_buffer_percentage` settings.

**Common use cases:**

* Apply aggressive cost optimization to CPU while maintaining conservative
  memory forecasts
* Add extra buffer to memory forecasts to prevent OOM errors while keeping CPU
  forecasts tight
* Test different optimization strategies per resource type

**Configuration:**

Each metric override supports:

* `mode` - Override the global forecast mode for this metric (`balanced`,
  `cost_optimized`, or `undershoot`)
* `forecast_buffer_percentage` - Override the global buffer percentage for this
  metric

**Example: Mixed strategy**

```yaml ast.yaml theme={null}
model:
  mode: balanced # Global default
  forecast_buffer_percentage: 0%
  metrics:
    cpu:
      mode: undershoot # More aggressive for CPU
    memory:
      mode: balanced
      forecast_buffer_percentage: 10% # Extra buffer for memory
```

In this configuration:

* CPU uses `undershoot` mode (ignoring global `balanced`)
* Memory uses `balanced` mode with 10% buffer (ignoring global 0%)

If a metric is not specified in `model.metrics`, it inherits the global
`model.mode` and `model.forecast_buffer_percentage` settings.

## `model.min_lookback_to_scale`

Minimum lookback window required before autonomous scaling is enabled.
Autonomous scaling will only occur if historical data spans at least this
duration.

**Default:** `3h` (inherited from helm chart value
`thorasForecast.minLookbackToScale` if not specified)

**Format:** Duration string (e.g., `"24h"`, `"7h30m"`). Cannot exceed metrics
retention window (typically 14 days).

**Example:**

```yaml ast.yaml theme={null}
model:
  min_lookback_to_scale: "24h" # Override default: require 24 hours before autonomous scaling
```

This setting ensures sufficient historical data exists before allowing
autonomous scaling decisions. Only applies when `mode` is set to `autonomous` -
has no effect in recommendation mode.

## `horizontal`

```yaml ast.yaml theme={null}
horizontal:
  mode: recommendation
  minReplicas: 2
  maxReplicas: 20
```

Thoras horizontal scaling adjusts the number of replicas based on forecasted or
observed workload demands.

The `mode` field controls whether Thoras provides recommendations or actively
scales your workload:

In `autonomous` mode:

* Horizontally-scaled workloads **must** have CPU and/or memory requests defined
  in the pod spec if you plan to scale on `averageUtilization`.
* **The target deployment of your `AIScaleTarget` must have a single existing
  [Horizontal Pod Autoscaler (HPA)](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/).**

  > **Note:** Only one scaling direction (horizontal or vertical) can be in
  > `autonomous` mode at a time. See
  > [Understanding Vertical and Horizontal Scaling Modes](/guides/configuring-asts#understanding-vertical-and-horizontal-scaling-modes)
  > for additional details.

In `recommendation` mode :

* System does **not** require an existing HPA. Thoras will assume an 80%
  utilization target for CPU and memory when generating scaling suggestions.

Thoras does **not** replace your HPA, it works alongside it by feeding forecasted
metrics into your existing HPA configuration. You can customize behavior by:

* Opting in additional metrics using `spec.horizontal.additional_metrics`
* Opting out of default metrics using `spec.horizontal.exclude_metrics`

This setup gives you full control over how scaling decisions are made while
benefiting from Thoras' predictive intelligence.

To enable horizontal scaling recommendations, set `spec.horizontal.mode` to
`recommendation`. It is recommended to start in `recommendation` mode to allow
the model time to learn workload patterns and validate scaling suggestions. Once
the recommendations align with your performance expectations, you can switch to
`autonomous` mode for automated scaling.

## `horizontal.minReplicas`

The lower bound for the number of replicas Thoras will scale down to. When set,
this value takes precedence over the `minReplicas` configured on the target HPA.

If not set, Thoras falls back to the HPA's `minReplicas` value.

**Note:** `minReplicas` must not be greater than `maxReplicas` when both are set
,  the CRD will reject the configuration if this constraint is violated.

## `horizontal.maxReplicas`

The upper bound for the number of replicas Thoras will scale up to. When set,
this value takes precedence over the `maxReplicas` configured on the target HPA.

If not set, Thoras falls back to the HPA's `maxReplicas` value.

**Note:** `maxReplicas` must not be less than `minReplicas` when both are set ,
the CRD will reject the configuration if this constraint is violated.

**Example: Constraining replicas independently of HPA**

```yaml ast.yaml theme={null}
horizontal:
  mode: autonomous
  minReplicas: 2
  maxReplicas: 20
```

This gives teams fine-grained control over Thoras scaling behavior independent
of HPA configuration. The HPA bounds remain in place and continue to govern
Kubernetes' own scaling, but Thoras will only apply replica counts within the
AST-defined range.

**Visit [Predictive Horizontal Pod Autoscaling with Thoras Guide](/guides/hpa)
for more info.**

## `vertical`

```yaml ast.yaml theme={null}
vertical:
  containers:
    - name: {{CONTAINER_NAME}}
      cpu:
        lowerbound: 20m # Mandatory
        upperbound: 1 # Optional
      memory:
        lowerbound: 50Mi # Mandatory
        upperbound: 2G # Optional
  mode: recommendation
  update_policy:
    update_mode: in_place_or_recreate
```

The Thoras vertical scaler adjusts container-level CPU and memory resource
requests based on forecasted utilization.

The `mode` field controls whether Thoras provides recommendations or actively
scales your workload:

To define a vertical scaling policy, you'll want to set the following fields:

* `vertical.mode: recommendation` - we recommend running Thoras in
  `recommendation` mode for at least a day for the model to train before
  enabling `autonomous`.

* `vertical.containers[0].memory.lowerbound` is always required and
  `vertical.containers[0].memory.upperbound` is optional if you had a preference
  for a memory floor and ceiling for your target workload.

* `vertical.containers[].RESOURCE.limit` allows Thoras to modify the container's
  limit along with the request for this resource and is optional. If unset,
  Thoras will *not* modify limits for this resource.

* `vertical.containers[].RESOURCE.limit.ratio` the ratio used to keep the limit
  inline with the suggested request for this resource. E.g. for a ratio `2.5`
  Thoras will make the limit `2.5G` if the suggestion for the request was for
  `1G`.

### Limit Enforcement

Thoras respects resource limits to ensure safe right-sizing operations:

* **AIScaleTarget `upperbound`**: Thoras won't right-size a pod above the
  configured `upperbound` during steady-state operation. The one exception is
  [OOM remediation](/guides/oom-remediation), an opt-in emergency response that
  intentionally bypasses `memory.upperbound` to recover a workload from an
  out-of-memory kill loop; the bound is re-applied once its stabilization window
  expires.
* **Pod spec `limit`** is also enforced, Thoras will never right-size a pod
  above its defined limit
* **Combined enforcement**: When both `upperbound` and pod `limit` are set,
  Thoras uses the lower value as the maximum for right-sizing

  > **Note:** Only one scaling direction (horizontal or vertical) can be in
  > `autonomous` mode at a time. See
  > [Understanding Vertical and Horizontal Scaling Modes](/guides/configuring-asts#understanding-vertical-and-horizontal-scaling-modes)
  > for details on how vertical and horizontal modes interact.

**Visit
[Predictive Vertical Pod Rightsizing Guide](/guides/vertical-pod-rightsizing)
for more info.**

### Container Name Wildcard Matching

The `vertical.containers[].name` field supports wildcard patterns using `*` to
match multiple containers with a single configuration. This is useful when you
have multiple containers with similar naming patterns or when you want to apply
the same resource bounds to all containers.

**Important:** Container configurations are matched in order from top to bottom.
The first matching pattern is used. Place more specific patterns before generic
ones.

**Matching Rules:**

* **Exact match**: Pattern matches the container name exactly
* **Wildcard `*`**: Matches any sequence of characters (including empty string)
* **Prefix match**: `app-*` matches any container starting with `app-`
* **Suffix match**: `*-sidecar` matches any container ending with `-sidecar`
* **Contains match**: `*cache*` matches any container containing `cache`
* **Multiple wildcards**: `app-*-prod-*` matches containers with `app-` prefix
  and `-prod-` in the middle

**Important:** If a pattern doesn't start with `*`, it must match from the
beginning. If it doesn't end with `*`, it must match to the end.

**Examples:**

```yaml ast.yaml theme={null}
vertical:
  containers:
    # Specific patterns first - these will match before the wildcard
    # Match containers starting with "app-"
    - name: "app-*"
      cpu:
        lowerbound: 100m
        upperbound: 2
      memory:
        lowerbound: 128Mi
        upperbound: 4G

    # Match containers ending with "-sidecar"
    - name: "*-sidecar"
      cpu:
        lowerbound: 10m
        upperbound: 500m
      memory:
        lowerbound: 32Mi
        upperbound: 512Mi

    # Match containers containing "cache"
    - name: "*cache*"
      cpu:
        lowerbound: 50m
        upperbound: 1
      memory:
        lowerbound: 256Mi
        upperbound: 8G

    # Match specific pattern with multiple wildcards
    - name: "app-*-prod-*"
      cpu:
        lowerbound: 200m
        upperbound: 4
      memory:
        lowerbound: 512Mi
        upperbound: 16G

    # Generic wildcard last - catches any remaining containers
    # This will only match containers that didn't match patterns above
    - name: "*"
      cpu:
        lowerbound: 20m
        upperbound: 1
      memory:
        lowerbound: 50Mi
        upperbound: 2G
```

**Pattern Matching Examples:**

| Pattern      | Container Name         | Matches | Reason                         |
| ------------ | ---------------------- | ------- | ------------------------------ |
| `*`          | `any-container`        | ✅       | Wildcard matches everything    |
| `app-*`      | `app-frontend`         | ✅       | Starts with `app-`             |
| `app-*`      | `my-app-frontend`      | ❌       | Doesn't start with `app-`      |
| `*-sidecar`  | `logging-sidecar`      | ✅       | Ends with `-sidecar`           |
| `*-sidecar`  | `sidecar-proxy`        | ❌       | Doesn't end with `-sidecar`    |
| `*cache*`    | `redis-cache-primary`  | ✅       | Contains `cache`               |
| `*cache*`    | `redis-primary`        | ❌       | Doesn't contain `cache`        |
| `app-*-prod` | `app-frontend-prod`    | ✅       | Matches pattern                |
| `app-*-prod` | `app-frontend-prod-v2` | ❌       | Has extra content after `prod` |

### `update_policy`

```yaml theme={null}
vertical:
  update_policy:
    update_mode: in_place_or_recreate
    recreate_resources: ["memory"]
```

The `update_policy` field controls pod update behavior during vertical scaling
operations.

**Prerequisites for in-place updates:**

* Kubernetes 1.33+

**Configuration:**

* `update_mode` (string): Determines how pod updates are applied:
  * `recreate` - Pods are recreated when resource changes are applied
  * `in_place` - Changes applied without pod recreation (requires Kubernetes
    1.33+)
  * `in_place_or_recreate` - Attempts in-place updates with fallback to
    recreation (recommended)
  * `initial` - Recommendations apply only during pod creation
* `recreate_resources` (optional array): Specifies which resources (`memory`,
  `cpu`) trigger pod recreation when using recreate or fallback modes

**How it works:**

1. When `update_mode` is `in_place` or `in_place_or_recreate`, Thoras attempts
   to resize pods in place using the Kubernetes resize API
2. When `update_mode` is `in_place_or_recreate` and the pod's
   [QoS class](https://kubernetes.io/docs/concepts/workloads/pods/pod-qos/)
   would change or the resize operation is not supported, Thoras falls back to
   evicting the pod
3. Evicted pods are rolled out gradually rather than all at once, minimizing
   service disruption. The eviction batch size and cadence are controlled by
   [`disruption`](#disruption)

**Benefits:**

* Reduced disruption: Pods are not recreated unless necessary when using
  in-place updates
* Faster scaling: Resource adjustments take effect immediately
* Better node utilization: Enables more efficient bin-packing and cost savings,
  especially when combined with node autoscaling tools like Karpenter
* Graceful fallback: Automatic eviction when in-place resizing is not possible
* Fine-grained control: Specify which resources trigger pod recreation

### `oom_remediation`

```yaml theme={null}
vertical:
  mode: autonomous
  oom_remediation:
    enabled: true
    stabilization_window: 2h
```

Enables automatic memory adjustment when OOM kills are detected. Only applies
when `vertical.mode` is `autonomous`.

**Fields:**

| Field                  | Type     | Required | Default                      | Description                                                                                                                                                                               |
| ---------------------- | -------- | -------- | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `enabled`              | bool     | Yes      | `false`                      | Activates OOM remediation for this workload.                                                                                                                                              |
| `stabilization_window` | duration | No       | `max(forecast_interval, 1h)` | How long the adjusted memory is held as a floor after an OOM. Forecasts can raise memory above the floor but cannot lower it until the window expires. Resets on each new OOM adjustment. |

**Visit [OOM Remediation Guide](/guides/oom-remediation) for detailed
configuration guidance.**

### `disruption`

```yaml ast.yaml theme={null}
vertical:
  disruption:
    interval: 60s
    max_disrupted: "20%"
```

`disruption` controls how aggressively Thoras evicts pods when applying vertical
recommendations that require pod recreation. When a recommendation can't be
applied in place, so Thoras has to recreate (evict) pods to apply it, these
settings pace those evictions in batches so the workload is not disrupted all at
once.

This setting applies only when vertical scaling is in `autonomous` mode and the
update policy results in pod recreation (see [`update_policy`](#update_policy)).
It has no effect in `recommendation` mode, and pods that can be resized in place
are never subject to it. If `disruption` is omitted, Thoras applies the defaults
below.

**Configuration:**

* `interval` (string, default `60s`): The minimum time Thoras waits after a
  batch of evictions before starting the next batch. Must be a positive
  duration.
* `max_disrupted` (integer or percentage, default `20%`): The maximum number of
  pods that may be disrupted at once across the workload. Accepts an absolute
  integer (e.g. `2`) or a percentage of the workload's desired replicas (e.g.
  `"25%"`). Must be a positive integer or a percentage between 1% and 100%.

**How it works:**

* Thoras evicts stale pods in batches. After issuing a batch it waits `interval`
  before issuing the next one, so evictions roll out gradually instead of all at
  once.
* A pod counts as "disrupted" if it is terminating or its `Ready` condition is
  not `True`. Each batch is sized against the remaining headroom
  (`max_disrupted` minus the pods already disrupted), so batches that haven't
  finished rolling shrink the next batch.
* Unlike a `PodDisruptionBudget`'s `maxUnavailable`, this cap is enforced by
  Thoras itself: pods that are unavailable for reasons unrelated to Thoras (e.g.
  failing readiness probes) still consume the budget.
* Thoras still honors your `PodDisruptionBudget`s. If an eviction is blocked by
  a PDB, Thoras defers the remaining evictions to a later run rather than
  forcing them.

## `additional_configuration`

### `scaling_behavior`

Controls scaling thresholds to prevent unnecessary scaling actions. Thresholds
can be configured independently for `scale_up` and `scale_down`. Both
**percentage** and **absolute** thresholds can be used for horizontal and
vertical scaling.

If `scaling_behavior` is not configured, defaults are applied automatically
based on your `model.mode`. Explicit configuration here overrides those
defaults. The default mode is `balanced`, so if no `model.mode` is set, the
5%/5% thresholds apply.

**Default thresholds by model mode:**

| Mode                  | Scale up threshold | Scale down threshold |
| --------------------- | ------------------ | -------------------- |
| `undershoot`          | 20%                | 5%                   |
| `cost_optimized`      | 10%                | 5%                   |
| `balanced`            | 5%                 | 5%                   |
| `assurance_optimized` | 5%                 | 10%                  |
| `overshoot`           | 5%                 | 20%                  |

Cost-leaning modes (`undershoot`, `cost_optimized`) have a higher scale-up
threshold and a lower scale-down threshold: they scale down readily and require
a larger change before scaling up. Assurance-leaning modes
(`assurance_optimized`, `overshoot`) do the opposite. See
[`model.mode`](#model-mode) for mode details.

**Percentage Thresholds** - Trigger scaling when change exceeds a percentage of
current usage (works for both horizontal and vertical):

```yaml theme={null}
horizontal:
  scaling_behavior:
    scale_up:
      type: percent
      percent: 20 # Scale up when increase is at least 20%
    scale_down:
      type: percent
      percent: 15

vertical:
  scaling_behavior:
    scale_up:
      type: percent
      percent: 20 # Scale up when increase is at least 20%
    scale_down:
      type: percent
      percent: 15
```

**Absolute Thresholds** - Trigger scaling when change exceeds fixed amounts
(works for both horizontal and vertical):

For horizontal scaling, use `pods`:

```yaml theme={null}
horizontal:
  scaling_behavior:
    scale_up:
      type: absolute
      absolute:
        pods: "5" # Scale up only if adding at least 5 pods
    scale_down:
      type: absolute
      absolute:
        pods: "3" # Scale down only if removing at least 3 pods
```

For vertical scaling, use `memory` and `cpu`:

```yaml theme={null}
vertical:
  scaling_behavior:
    scale_up:
      type: absolute
      absolute:
        memory: "100Mi" # Scale up only if memory increase is at least 100Mi
        cpu: "500m" # Scale up only if CPU increase is at least 500m
    scale_down:
      type: absolute
      absolute:
        memory: "50Mi" # Scale down only if memory decrease is at least 50Mi
        cpu: "250m" # Scale down only if CPU decrease is at least 250m
```

**Best Practices:**

* Use absolute thresholds for small workloads (percentage changes can be
  misleading)
* Use percentage thresholds for large workloads (scales naturally with
  deployment size)
* Set asymmetric thresholds (conservative scale-up, aggressive scale-down)
* If you are unsure where to start, rely on the mode defaults and only override
  if you observe unwanted scaling behavior

### `additional_metrics` and `exclude_metrics`

```yaml ast.yaml theme={null}
horizontal:
  mode: recommendation
  additional_metrics:
    - external:
        metric:
          name: {{EXTERNAL_METRIC_NAME}}
          selector:
            matchLabels:
              app: {{APP_NAME}}
        target:
          averageValue: "1"
          type: AverageValue
      type: External
  exclude_metrics:
    - name: cpu
    - type: Resource
```

When you enable horizontal scaling in Thoras, it defaults to using the same
metrics as your HPA (Horizontal Pod Autoscaler) to guide predictive scaling,
whether they're resource, external or custom metrics. However, there may be
cases where you want Thoras to:

* **Exclude certain metrics** (e.g., CPU) from its predictions, even if they are
  used by the HPA.
* **Add new metrics** for prediction that are not part of the HPA.

### Example 1: **Exclude CPU from Predictive Scaling**

This configuration tells Thoras **not to use CPU** for predictive scaling, even
if it's included in the HPA:

```yaml ast.yaml theme={null}
spec:
  horizontal:
    exclude_metrics:
      - name: cpu
        type: Resource
```

### Example 2: **Add Custom Metric for Predictive Scaling**

In this example, custom metric is configured to **predictively scale on CPU** by
explicitly including it in `additional_metrics`. Thoras will predictively scale
on CPU even though it is not defined in the HPA.

```yaml ast.yaml theme={null}
spec:
  horizontal:
    additional_metrics:
      - external:
          metric:
            name: {{EXTERNAL_METRIC_NAME}}
            selector:
              matchLabels:
                app: {{APP_NAME}}
          target:
            averageValue: "1"
            type: AverageValue
        type: External
```
