Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .semgrepignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ src/amzn_nova_forge/model/result/eval_result.py

# Safe exec usage in controlled environment for reward verification
src/amzn_nova_forge/util/reward_verifier.py
tests/unit/manager/test_smtj_dataprep_runtime_manager.py
30 changes: 30 additions & 0 deletions docs/spec/model-customizer.md
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,36 @@ print(f"Training job started: {result.job_id}")
print(f"Checkpoint path: {result.model_artifacts.checkpoint_s3_path}")
```
---

#### `get_config()`
Returns the overridable training parameters. Delegates to `ForgeTrainer.get_config()`.

> **Deprecated**: Use `ForgeTrainer.get_config()` instead.

**Signature:**
```python
def get_config(
self,
overrides: Optional[Dict[str, Any]] = None,
) -> RecipeConfig
```

**Parameters:**
- `overrides` (Optional[Dict[str, Any]]): Optional overrides to merge with defaults

**Returns:**
- `RecipeConfig`: Frozen dataclass containing all overridable parameters with defaults and constraints. See `ForgeTrainer.get_config()` for details.

**Example:**
```python
config = customizer.get_config()
print(config)

# With overrides
config = customizer.get_config(overrides={"lr": 1e-5})
print(config.to_dict())
```
---
#### `evaluate()`
Generates the recipe YAML, configures runtime, and launches an evaluation job.

Expand Down
43 changes: 43 additions & 0 deletions docs/spec/service-classes.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,49 @@ print(f"Checkpoint path: {result.model_artifacts.checkpoint_s3_path}")
```
---

##### `get_config()`
Returns the overridable training parameters for the current model/method/platform without starting a job or running validation. Use this to inspect defaults, valid ranges, and available parameters before calling `train()`.

**Signature:**
```python
def get_config(
self,
overrides: Optional[TrainingOverrides] = None,
) -> RecipeConfig
```

**Parameters:**
- `overrides` (Optional[TrainingOverrides]): Optional overrides to merge with defaults. When provided, `ConfigParameter.default` values in the result reflect the merged values. Plain `dict` values are also accepted

**Returns:**
- `RecipeConfig`: Frozen dataclass with:
- `model` (Model): The model
- `method` (TrainingMethod): The training method
- `platform` (Platform): The infrastructure platform
- `parameters` (tuple[ConfigParameter, ...]): All overridable parameters, each with `name`, `type`, `default`, `description`, `min`, `max`, `enum`, `required`
- `to_dict()`: Returns `{name: default}` mapping directly passable to `train(overrides=...)`

**Example:**
```python
# Inspect default configuration
config = trainer.get_config()
print(config)
# RecipeConfig(model=NOVA_MICRO, method=SFT_LORA, platform=SMTJ)
# lr: float = 5e-06 [1e-06..0.0001]
# max_epochs: integer = 2 [1..5]
# warmup_steps: integer = 10 [0..100]
# global_batch_size: integer = 64 [1..512]

# Preview with your overrides applied
config = trainer.get_config(overrides={"lr": 1e-5, "max_epochs": 4})
print(config.to_dict())
# {"lr": 1e-5, "max_epochs": 4, "warmup_steps": 10, "global_batch_size": 64, ...}
```

**Note:** `get_config()` is not supported for `RFT_MULTITURN_*` training methods. For those methods, use `train(dry_run=True)` to inspect the recipe.

---

##### `get_logs()`
Retrieves and displays CloudWatch logs for a training job.

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ dependencies = [
"filetype",
"matplotlib",
"pydantic>=2.0",
"pyarrow",
"pyarrow>=14.0.1",
"sagemaker>=3.5.0",
]
[project.optional-dependencies]
Expand Down
14 changes: 13 additions & 1 deletion samples/nova_quickstart.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,18 @@
"print(f\" Method: SFT with LoRA\")"
]
},
{
"cell_type": "markdown",
"source": "### Inspect Available Training Parameters\n\nBefore starting training, you can use `get_config()` to see what parameters are available, their defaults, and valid ranges. This is more convenient than running `train(dry_run=True)` just to inspect the recipe.",
"metadata": {}
},
{
"cell_type": "code",
"source": "# View all overridable parameters with their defaults and valid ranges\nconfig = trainer.get_config()\nprint(config)\n\n# Preview what the config looks like with your planned overrides\nconfig_with_overrides = trainer.get_config(overrides={\"lr\": 5e-6, \"warmup_steps\": 100})\nprint(\"\\nWith overrides applied:\")\nprint(config_with_overrides.to_dict())",
"metadata": {},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {},
Expand Down Expand Up @@ -1214,4 +1226,4 @@
},
"nbformat": 4,
"nbformat_minor": 4
}
}
2 changes: 1 addition & 1 deletion samples/rft_multiturn_quickstart.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -1025,7 +1025,7 @@
" \"max_new_tokens\": 4096,\n",
" \"max_length\": 16384,\n",
" \"global_batch_size\": 64,\n",
" \"reasoning_effort\": \"null\",\n",
" \"reasoning_effort\": None,\n",
" \"timeout\": 1800,\n",
" },\n",
" rft_multiturn_infra=rft_infra, # Pass RFT infrastructure\n",
Expand Down
15 changes: 14 additions & 1 deletion src/amzn_nova_forge/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
# 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.
from .core.data_mixing_config import DataMixingConfig
from .core.enums import (
DeploymentMode,
DeployPlatform,
Expand All @@ -29,7 +30,15 @@
BaseJobResult,
JobStatus,
)
from .core.types import DeploymentResult, EndpointInfo, ForgeConfig, ModelArtifacts
from .core.training_overrides import TrainingOverrides
from .core.types import (
ConfigParameter,
DeploymentResult,
EndpointInfo,
ForgeConfig,
ModelArtifacts,
RecipeConfig,
)
from .dataset import (
ArrowDatasetLoader,
CSVDatasetLoader,
Expand Down Expand Up @@ -121,6 +130,10 @@ def __getattr__(name: str):
"DeployPlatform",
"Platform",
"NovaModelCustomizer",
"ConfigParameter",
"DataMixingConfig",
"RecipeConfig",
"TrainingOverrides",
"ForgeConfig",
"ForgeTrainer",
"ForgeEvaluator",
Expand Down
2 changes: 1 addition & 1 deletion src/amzn_nova_forge/__version__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@
# 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.
VERSION = "1.4.6" # pragma: no cover
VERSION = "1.4.7" # pragma: no cover
4 changes: 4 additions & 0 deletions src/amzn_nova_forge/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,14 @@
)
from amzn_nova_forge.core.runtime import RuntimeManager
from amzn_nova_forge.core.types import (
ConfigParameter,
DeploymentResult,
EndpointInfo,
ForgeConfig,
JobConfig,
ModelArtifacts,
ModelConfigDict,
RecipeConfig,
validate_region,
)

Expand Down Expand Up @@ -94,12 +96,14 @@
# runtime
"RuntimeManager",
# types
"ConfigParameter",
"DeploymentResult",
"EndpointInfo",
"ForgeConfig",
"JobConfig",
"ModelArtifacts",
"ModelConfigDict",
"RecipeConfig",
# result classes (lazy-loaded)
"BaseJobResult",
"BedrockStatusManager",
Expand Down
6 changes: 6 additions & 0 deletions src/amzn_nova_forge/core/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@
DEFAULT_BATCH_TRACE_CACHE_DIR = "~/.nova-forge/batch_trace_cache"
BATCH_TRACE_LOG_SUBDIR = "batch_tracing"

# Data mixing field name patterns
DATAMIX_NOVA_PREFIX = "nova_"
DATAMIX_PERCENT_SUFFIX = "_percent"
DATAMIX_CUSTOMER_DATA_FIELD = "customer_data_percent"
DATAMIX_DATASET_CATALOG_FIELD = "dataset_catalog"

REGION_TO_ESCROW_ACCOUNT_MAPPING = {
"us-east-1": "708977205387",
"us-west-2": "176779409107",
Expand Down
42 changes: 42 additions & 0 deletions src/amzn_nova_forge/core/data_mixing_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Copyright Amazon.com, Inc. or its affiliates

# 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.
"""Typed configuration for data mixing percentages."""

from typing_extensions import TypedDict


class DataMixingConfig(TypedDict, total=False):
"""Typed configuration for data mixing between customer and Nova curated data.

All percent fields must be 0-100. When customer_data_percent < 100, the
nova_*_percent fields must sum to 100. Unrecognized nova_*_percent fields
are accepted since TypedDict does not reject unknown keys at runtime.

Can be passed directly to ``DataMixing.set_config()`` — plain dicts
satisfy this annotation without construction.

Example::

config: DataMixingConfig = {
"customer_data_percent": 50.0,
"nova_code_percent": 30.0,
"nova_general_percent": 70.0,
}
"""

customer_data_percent: float
nova_code_percent: float
nova_general_percent: float
nova_math_percent: float
nova_image_percent: float
67 changes: 67 additions & 0 deletions src/amzn_nova_forge/core/training_overrides.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Copyright Amazon.com, Inc. or its affiliates

# 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.
"""TypedDict for training recipe overrides."""

from typing_extensions import TypedDict


class TrainingOverrides(TypedDict, total=False):
"""Typed hints for training recipe override keys.

Provides IDE autocomplete and static type checking for common override
parameters passed to ``ForgeTrainer.train()``, ``ForgeEvaluator.evaluate()``,
and ``RecipeBuilder.build_and_validate()``. All keys are optional
(``total=False``).

Plain dicts remain fully supported — this is structural typing, so
``{"lr": 0.001}`` satisfies the annotation without constructing a
``TrainingOverrides`` instance. Unknown keys from new recipe templates
pass through at runtime without restriction.

Example::

overrides: TrainingOverrides = {
"lr": 5e-6,
"max_steps": 1000,
"global_batch_size": 128,
}
"""

lr: float
loraplus_lr_ratio: float
lora_plus_lr_ratio: float

max_steps: int
max_epochs: int
save_steps: int
warmup_steps: int

global_batch_size: int
max_length: int

alpha: float

beta: float

reasoning_enabled: bool
reasoning_effort: str | None

val_check_interval: int
validation_s3_path: str
validation_data_s3_path: str

top_logprobs: int


NULLABLE_OVERRIDE_FIELDS: frozenset[str] = frozenset({"reasoning_effort"})
46 changes: 45 additions & 1 deletion src/amzn_nova_forge/core/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
DEFAULT_JOB_CACHE_DIR,
REGION_TO_ESCROW_ACCOUNT_MAPPING,
)
from amzn_nova_forge.core.enums import DeployPlatform, Platform, TrainingMethod
from amzn_nova_forge.core.enums import DeployPlatform, Model, Platform, TrainingMethod

if TYPE_CHECKING:
from amzn_nova_forge.core.job_cache import JobCachingConfig
Expand Down Expand Up @@ -157,3 +157,47 @@ class JobConfig:
method: Optional[TrainingMethod] = None # Training method (required for Bedrock)
data_mixing_config: Optional[Dict[str, Any]] = None # Datamix percent fields (SMTJServerless)
# TODO: The mlflow config is populated in recipe for both SMTJ and SMHP but will only work for SMHP as SMTJ support for mlflow is only through boto3, fix this with sagemaker 3 update


@dataclass(frozen=True)
class ConfigParameter:
"""A single overridable parameter in a training recipe."""

name: str
type: str
default: Any
description: Optional[str] = None
min: Optional[float] = None
max: Optional[float] = None
enum: Optional[tuple] = None
required: bool = False


@dataclass(frozen=True)
class RecipeConfig:
"""Frozen snapshot of the overridable configuration for a training recipe."""

model: Model
method: TrainingMethod
platform: Platform
parameters: tuple[ConfigParameter, ...]

def to_dict(self) -> Dict[str, Any]:
return {p.name: p.default for p in self.parameters}

def __repr__(self) -> str:
lines = [
f"RecipeConfig(model={self.model.name}, method={self.method.name}, platform={self.platform.name})"
]
for p in self.parameters:
constraint = ""
if p.min is not None and p.max is not None:
constraint = f" [{p.min}..{p.max}]"
elif p.min is not None:
constraint = f" [>={p.min}]"
elif p.max is not None:
constraint = f" [<={p.max}]"
if p.enum is not None:
constraint = f" enum={list(p.enum)}"
lines.append(f" {p.name}: {p.type} = {p.default}{constraint}")
return "\n".join(lines)
Loading
Loading