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
14 changes: 6 additions & 8 deletions dashscope/_error_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -486,14 +486,12 @@ def format_message(

SDK_AGENTIC_RL_FUNCTION_LAYER_ERROR = SdkErrorDef(
name="sdk.agentic_rl.FunctionLayerError",
message="The function dependency layer is not usable.",
solution=(
"The dependency layer could not be created, or its build failed, "
"timed out, or its status could not be queried. For a build "
"failure, check that requirements.txt or setup.py is valid and "
"every dependency is installable, then review the layer build "
"logs. For a timeout, raise FC_LAYER_STATUS_TIMEOUT to allow more "
"time. For a status query error, retry once connectivity recovers."
message="Creating a layer of function failed.",
solution=(
"Failed to create the function dependency layer. Check that "
"the requirements.txt or setup.py is valid and all "
"dependencies are installable. Review the layer build logs "
"for details."
),
allow_retry=True,
vars=[],
Expand Down
55 changes: 52 additions & 3 deletions dashscope/applications/finetune/reinforcement/common/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,34 @@
ValueErrorWithCode,
)

_LAYER_CREATE_REJECTED_SOLUTION = (
"Review the server response and layer request. Fix invalid requirements "
"or request parameters before retrying."
)
_LAYER_CREATE_LOCAL_SOLUTION = (
"Check that the requirements file exists, is readable, and contains "
"valid dependency declarations before retrying."
)
_LAYER_CREATE_TRANSIENT_SOLUTION = (
"Retry after connectivity recovers. If the error persists, check the "
"Function Compute layer service and request logs."
)
_LAYER_STATUS_QUERY_SOLUTION = (
"Retry the layer status query after connectivity recovers."
)
_LAYER_BUILD_FAILED_SOLUTION = (
"Check that requirements.txt or setup.py is valid and every dependency "
"is installable, then review the layer build logs before retrying."
)
_LAYER_STATUS_MISSING_SOLUTION = (
"Retry the status query. If the response still has no status, report the "
"response and layer code to the service owner."
)
_LAYER_BUILD_TIMEOUT_SOLUTION = (
"Increase FC_LAYER_STATUS_TIMEOUT if the layer is still building, then "
"query the same layer code again instead of creating a duplicate layer."
)


class MountStorage(BaseModel):
model_config = ConfigDict(extra="forbid")
Expand Down Expand Up @@ -322,11 +350,12 @@ class FunctionComponentModel(BaseModel):

def generate_id(self, func_type: FunctionType) -> str:
"""Generate unique OSS resource identifier."""
self.oss_id = generate_random_id(func_type.value)
oss_id = generate_random_id(func_type.value)
self.oss_id = oss_id
logger.debug(
f"Generated OSS ID | Type: {func_type.name}, ID: {self.oss_id}",
f"Generated OSS ID | Type: {func_type.name}, ID: {oss_id}",
)
return self.oss_id
return oss_id

async def get_oss(self, oss_id: Optional[str] = None) -> str:
"""Retrieve OSS signed URL for deployment package."""
Expand Down Expand Up @@ -393,6 +422,8 @@ async def create_layer(
if result.get("status", {}).get("code", 500) != 200:
raise FunctionLayerError(
f"create rejected by server | {result}",
allow_retry=False,
solution=_LAYER_CREATE_REJECTED_SOLUTION,
)

layer_code = result.get("output", {}).get("layer_code", "")
Expand All @@ -403,11 +434,21 @@ async def create_layer(

except FunctionLayerError:
raise
except (InputError, OSError) as e:
raise FunctionLayerError(
f"create input error | {e}",
error_code=2013,
layer_name=layer_name,
allow_retry=False,
solution=_LAYER_CREATE_LOCAL_SOLUTION,
) from e
except Exception as e:
raise FunctionLayerError(
f"create error | {e}",
error_code=2013,
layer_name=layer_name,
allow_retry=True,
solution=_LAYER_CREATE_TRANSIENT_SOLUTION,
) from e

return layer_code
Expand Down Expand Up @@ -487,6 +528,8 @@ async def get_layer(self, layer_code: str) -> str:
f"status query error | layer_code: {layer_code} | {e}",
error_code=2014,
layer_code=layer_code,
allow_retry=True,
solution=_LAYER_STATUS_QUERY_SOLUTION,
) from e

status = result.get("output", {}).get("status", "")
Expand All @@ -504,6 +547,8 @@ async def get_layer(self, layer_code: str) -> str:
f"{layer_code} | {result}",
error_code=2015,
layer_code=layer_code,
allow_retry=False,
solution=_LAYER_BUILD_FAILED_SOLUTION,
)

if not status:
Expand All @@ -512,6 +557,8 @@ async def get_layer(self, layer_code: str) -> str:
f"{layer_code} | {result}",
error_code=2016,
layer_code=layer_code,
allow_retry=True,
solution=_LAYER_STATUS_MISSING_SOLUTION,
)

if attempt == 1:
Expand All @@ -536,6 +583,8 @@ async def get_layer(self, layer_code: str) -> str:
f"FC_LAYER_STATUS_TIMEOUT if the build needs longer",
error_code=2017,
layer_code=layer_code,
allow_retry=True,
solution=_LAYER_BUILD_TIMEOUT_SOLUTION,
)

def clean_temp_files(self, tmp_path: str) -> None:
Expand Down
21 changes: 21 additions & 0 deletions tests/legacy_1x/unit/test_agentic_rl_layer_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,19 @@ def _component(with_runtime=True):
return component


async def test_create_layer_rejection_is_not_retryable(monkeypatch):
_stub_responses(
monkeypatch,
[{"status": {"code": 400}, "message": "invalid requirements"}],
)

with pytest.raises(FunctionLayerError) as excinfo:
await FunctionComponentModel().create_layer(requirements_file="")

assert excinfo.value.allow_retry is False
assert "Fix invalid requirements" in excinfo.value.solution


async def test_get_layer_returns_ready_status(monkeypatch):
calls = _stub_responses(monkeypatch, [{"output": {"status": "SUCCESS"}}])

Expand Down Expand Up @@ -108,6 +121,8 @@ async def test_get_layer_raises_on_failed_build(monkeypatch):
assert "Function layer failed: build reported FAILED" in message
assert excinfo.value.layer_code == LAYER_CODE
assert excinfo.value.layer_name is None
assert excinfo.value.allow_retry is False
assert "requirements.txt" in excinfo.value.solution


async def test_get_layer_raises_on_region_rejection(monkeypatch):
Expand All @@ -117,6 +132,8 @@ async def test_get_layer_raises_on_region_rejection(monkeypatch):
await FunctionComponentModel().get_layer(LAYER_CODE)

assert "cn-wulanchabu" in str(excinfo.value)
assert excinfo.value.allow_retry is True
assert "Retry the status query" in excinfo.value.solution
assert calls["count"] == 1


Expand All @@ -132,6 +149,8 @@ async def test_get_layer_raises_when_build_stalls(monkeypatch):
assert "timed out" in message
assert "RUNNING" in message
assert "FC_LAYER_STATUS_TIMEOUT" in message
assert excinfo.value.allow_retry is True
assert "FC_LAYER_STATUS_TIMEOUT" in excinfo.value.solution
assert calls["count"] > 3


Expand All @@ -142,6 +161,8 @@ async def test_get_layer_raises_on_transport_error(monkeypatch):
await FunctionComponentModel().get_layer(LAYER_CODE)

assert "connection reset" in str(excinfo.value)
assert excinfo.value.allow_retry is True
assert "connectivity recovers" in excinfo.value.solution


async def test_load_reports_layer_failure_as_such(monkeypatch):
Expand Down
13 changes: 13 additions & 0 deletions tests/test_error_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,19 @@ def test_internal_entries_well_formed():
assert all(isinstance(v, str) for v in entry.vars)


def test_function_layer_error_matches_generated_registry_source():
entry = _by_name(SDK)["sdk.agentic_rl.FunctionLayerError"]
assert entry.message == "Creating a layer of function failed."
assert entry.solution == (
"Failed to create the function dependency layer. Check that the "
"requirements.txt or setup.py is valid and all dependencies are "
"installable. Review the layer build logs for details."
)
# Python-only runtime controls belong at the raise site, not in the
# cross-language generated catalog.
assert "FC_LAYER_STATUS_TIMEOUT" not in entry.solution


def test_internal_vars_match_message_placeholders():
"""Every declared var is used, and every placeholder is declared."""
for entry in SDK:
Expand Down
Loading