Skip to content

Commit 71e45a1

Browse files
feat(agentic-rl): normalize int error_code to str in output models
Add a shared before-validator so RolloutOutput/RewardOutput accept int error codes without hard-failing validation, while downstream always receives str (the protocol type stays string).
1 parent 7c2bcff commit 71e45a1

4 files changed

Lines changed: 54 additions & 4 deletions

File tree

‎dashscope/finetune/reinforcement/component/data/base_data_model.py‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,17 @@ def _generate_ro_id(length: int) -> str:
2626
return "ro-" + hashlib.sha1(uuid.uuid4().bytes).hexdigest()[:length]
2727

2828

29+
def _normalize_error_code(v: Any) -> Any:
30+
"""Coerce ``int`` error codes to ``str``; pass other values through.
31+
32+
The protocol keeps ``error_code`` as a string, so passing an int no
33+
longer hard-fails validation and downstream always receives a str.
34+
"""
35+
if isinstance(v, int) and not isinstance(v, bool):
36+
return str(v)
37+
return v
38+
39+
2940
# ========================================================================== #
3041
# Enum Definitions #
3142
# ========================================================================== #

‎dashscope/finetune/reinforcement/component/data/reward_output.py‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@
55
Data model definitions for Reward processor output results.
66
"""
77

8-
from typing import Dict, Optional
9-
from pydantic import BaseModel, Field
8+
from typing import Any, Dict, Optional
9+
from pydantic import BaseModel, Field, field_validator
1010

1111
from dashscope.finetune.reinforcement.component.data.base_data_model import (
1212
TaskStatus,
13+
_normalize_error_code,
1314
)
1415

1516

@@ -60,5 +61,11 @@ class RewardOutput(BaseModel):
6061
description="Error code if the reward computation failed.",
6162
)
6263

64+
@field_validator("error_code", mode="before")
65+
@classmethod
66+
def normalize_error_code(cls, v: Any) -> Any:
67+
"""Normalize int error codes to str (protocol stays string)."""
68+
return _normalize_error_code(v)
69+
6370
class Config:
6471
extra = "allow"

‎dashscope/finetune/reinforcement/component/data/rollout_output.py‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@
66
Corresponds to the RolloutOutput structure in protocol.py.
77
"""
88

9-
from typing import Optional
10-
from pydantic import BaseModel, Field
9+
from typing import Any, Optional
10+
from pydantic import BaseModel, Field, field_validator
1111

1212
from dashscope.finetune.reinforcement.component.data.base_data_model import (
1313
TaskStatus,
1414
AgentOutput,
15+
_normalize_error_code,
1516
)
1617

1718

@@ -45,5 +46,11 @@ class RolloutOutput(BaseModel):
4546
description="Error code (when failed).",
4647
)
4748

49+
@field_validator("error_code", mode="before")
50+
@classmethod
51+
def normalize_error_code(cls, v: Any) -> Any:
52+
"""Normalize int error codes to str (protocol stays string)."""
53+
return _normalize_error_code(v)
54+
4855
class Config:
4956
extra = "allow"

‎tests/unit/test_agentic_rl_components.py‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,22 @@ def test_rollout_output_failure(self):
279279
assert output.error == "Model inference timeout"
280280
assert output.agent_output is None
281281

282+
def test_rollout_output_error_code_int_normalized(self):
283+
output = RolloutOutput(
284+
status=TaskStatus.FAILED,
285+
error="Model inference timeout",
286+
error_code=3001,
287+
)
288+
assert output.error_code == "3001"
289+
assert isinstance(output.error_code, str)
290+
291+
def test_rollout_output_error_code_str_passthrough(self):
292+
output = RolloutOutput(
293+
status=TaskStatus.FAILED,
294+
error_code="RATE_LIMIT",
295+
)
296+
assert output.error_code == "RATE_LIMIT"
297+
282298
def test_rollout_output_defaults(self):
283299
output = RolloutOutput()
284300
assert output.status == TaskStatus.SUCCESS
@@ -335,6 +351,15 @@ def test_reward_output_failure(self):
335351
)
336352
assert output.status == TaskStatus.FAILED
337353

354+
def test_reward_output_error_code_int_normalized(self):
355+
output = RewardOutput(
356+
reward=Reward(reward_score=0.0),
357+
status=TaskStatus.FAILED,
358+
error_code=3002,
359+
)
360+
assert output.error_code == "3002"
361+
assert isinstance(output.error_code, str)
362+
338363
def test_reward_output_missing_reward(self):
339364
with pytest.raises(ValidationError):
340365
RewardOutput()

0 commit comments

Comments
 (0)