Skip to content

Commit 1f900cb

Browse files
committed
fix: update sandbox and template models to use Pydantic Field for TTL aliases
This commit modifies the `sandbox_ttlin_seconds` and `sandbox_idle_ttlin_seconds` fields in the `Sandbox` and `Template` models to utilize Pydantic's `Field` for aliasing to `sandboxTTLInSeconds` and `sandboxIdleTTLInSeconds`, respectively. Additionally, tests have been added to ensure correct parsing and serialization of these fields with their aliases. Co-developed-by: Aone Copilot <noreply@alibaba-inc.com> Signed-off-by: Sodawyx <sodawyx@126.com>
1 parent 6d22cb6 commit 1f900cb

File tree

9 files changed

+111
-29
lines changed

9 files changed

+111
-29
lines changed

agentrun/integration/utils/tool.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1562,8 +1562,8 @@ def _build_openapi_schema(
15621562
if isinstance(schema, dict):
15631563
properties[name] = {
15641564
**schema,
1565-
"description": (
1566-
param.get("description") or schema.get("description", "")
1565+
"description": param.get("description") or schema.get(
1566+
"description", ""
15671567
),
15681568
}
15691569
if param.get("required"):

agentrun/sandbox/__sandbox_async_template.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
Union,
1616
)
1717

18+
from pydantic import Field
19+
1820
from agentrun.sandbox.model import TemplateType
1921
from agentrun.utils.config import Config
2022
from agentrun.utils.model import BaseModel
@@ -56,7 +58,9 @@ class Sandbox(BaseModel):
5658
"""沙箱全局唯一资源名称 / Sandbox ARN"""
5759
sandbox_id: Optional[str] = None
5860
"""沙箱 ID / Sandbox ID"""
59-
sandbox_idle_ttlin_seconds: Optional[int] = None
61+
sandbox_idle_ttlin_seconds: Optional[int] = Field(
62+
None, alias="sandboxIdleTTLInSeconds"
63+
)
6064
"""沙箱空闲 TTL(秒) / Sandbox Idle TTL (seconds)"""
6165
sandbox_idle_timeout_seconds: Optional[int] = None
6266
"""沙箱空闲超时时间(秒) / Sandbox Idle Timeout (seconds)"""

agentrun/sandbox/__template_async_template.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
from typing import Dict, List, Optional
88

9+
from pydantic import Field
10+
911
from agentrun.sandbox.model import (
1012
PageableInput,
1113
TemplateContainerConfiguration,
@@ -52,7 +54,9 @@ class Template(BaseModel):
5254
"""执行角色 ARN / Execution Role ARN"""
5355
sandbox_idle_timeout_in_seconds: Optional[int] = None
5456
"""沙箱空闲超时时间(秒) / Sandbox Idle Timeout (seconds)"""
55-
sandbox_ttlin_seconds: Optional[int] = None
57+
sandbox_ttlin_seconds: Optional[int] = Field(
58+
None, alias="sandboxTTLInSeconds"
59+
)
5660
"""沙箱存活时间(秒) / Sandbox TTL (seconds)"""
5761
share_concurrency_limit_per_sandbox: Optional[int] = None
5862
"""每个沙箱的最大并发会话数 / Max Concurrency Limit Per Sandbox"""

agentrun/sandbox/model.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from typing import Any, Dict, List, Optional, TYPE_CHECKING
99
import uuid
1010

11-
from pydantic import model_validator
11+
from pydantic import Field, model_validator
1212

1313
from agentrun.utils.model import BaseModel
1414

@@ -264,7 +264,9 @@ class TemplateInput(BaseModel):
264264
"""执行角色 ARN / Execution Role ARN"""
265265
sandbox_idle_timeout_in_seconds: Optional[int] = 1800
266266
"""沙箱空闲超时时间(秒) / Sandbox Idle Timeout (seconds)"""
267-
sandbox_ttlin_seconds: Optional[int] = 21600
267+
sandbox_ttlin_seconds: Optional[int] = Field(
268+
21600, alias="sandboxTTLInSeconds"
269+
)
268270
"""沙箱存活时间(秒) / Sandbox TTL (seconds)"""
269271
share_concurrency_limit_per_sandbox: Optional[int] = 200
270272
"""每个沙箱的最大并发会话数 / Max Concurrency Limit Per Sandbox"""

agentrun/sandbox/sandbox.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
Union,
2626
)
2727

28+
from pydantic import Field
29+
2830
from agentrun.sandbox.model import TemplateType
2931
from agentrun.utils.config import Config
3032
from agentrun.utils.model import BaseModel
@@ -66,7 +68,9 @@ class Sandbox(BaseModel):
6668
"""沙箱全局唯一资源名称 / Sandbox ARN"""
6769
sandbox_id: Optional[str] = None
6870
"""沙箱 ID / Sandbox ID"""
69-
sandbox_idle_ttlin_seconds: Optional[int] = None
71+
sandbox_idle_ttlin_seconds: Optional[int] = Field(
72+
None, alias="sandboxIdleTTLInSeconds"
73+
)
7074
"""沙箱空闲 TTL(秒) / Sandbox Idle TTL (seconds)"""
7175
sandbox_idle_timeout_seconds: Optional[int] = None
7276
"""沙箱空闲超时时间(秒) / Sandbox Idle Timeout (seconds)"""

agentrun/sandbox/template.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
from typing import Dict, List, Optional
1818

19+
from pydantic import Field
20+
1921
from agentrun.sandbox.model import (
2022
PageableInput,
2123
TemplateContainerConfiguration,
@@ -62,7 +64,9 @@ class Template(BaseModel):
6264
"""执行角色 ARN / Execution Role ARN"""
6365
sandbox_idle_timeout_in_seconds: Optional[int] = None
6466
"""沙箱空闲超时时间(秒) / Sandbox Idle Timeout (seconds)"""
65-
sandbox_ttlin_seconds: Optional[int] = None
67+
sandbox_ttlin_seconds: Optional[int] = Field(
68+
None, alias="sandboxTTLInSeconds"
69+
)
6670
"""沙箱存活时间(秒) / Sandbox TTL (seconds)"""
6771
share_concurrency_limit_per_sandbox: Optional[int] = None
6872
"""每个沙箱的最大并发会话数 / Max Concurrency Limit Per Sandbox"""

tests/unittests/integration/test_langchain_agui_integration.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -664,9 +664,7 @@ async def invoke_agent(request: AgentRequest):
664664
json={
665665
"messages": [{
666666
"role": "user",
667-
"content": (
668-
"查询当前的时间,并获取天气信息,同时输出我的密钥信息"
669-
),
667+
"content": "查询当前的时间,并获取天气信息,同时输出我的密钥信息",
670668
}],
671669
"stream": True,
672670
},
@@ -729,9 +727,7 @@ async def invoke_agent(request: AgentRequest):
729727
json={
730728
"messages": [{
731729
"role": "user",
732-
"content": (
733-
"查询当前的时间,并获取天气信息,同时输出我的密钥信息"
734-
),
730+
"content": "查询当前的时间,并获取天气信息,同时输出我的密钥信息",
735731
}],
736732
"stream": True,
737733
},

tests/unittests/toolset/api/test_openapi.py

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -545,9 +545,7 @@ def test_post_with_ref_schema(self):
545545
"content": {
546546
"application/json": {
547547
"schema": {
548-
"$ref": (
549-
"#/components/schemas/CreateOrderRequest"
550-
)
548+
"$ref": "#/components/schemas/CreateOrderRequest"
551549
}
552550
}
553551
},
@@ -758,9 +756,7 @@ def test_invalid_ref_gracefully_handled(self):
758756
"content": {
759757
"application/json": {
760758
"schema": {
761-
"$ref": (
762-
"#/components/schemas/NonExistent"
763-
)
759+
"$ref": "#/components/schemas/NonExistent"
764760
}
765761
}
766762
}
@@ -793,9 +789,7 @@ def test_external_ref_not_resolved(self):
793789
"content": {
794790
"application/json": {
795791
"schema": {
796-
"$ref": (
797-
"https://example.com/schemas/external.json"
798-
)
792+
"$ref": "https://example.com/schemas/external.json"
799793
}
800794
}
801795
}
@@ -915,9 +909,7 @@ def _get_coffee_shop_schema():
915909
"content": {
916910
"application/json": {
917911
"schema": {
918-
"$ref": (
919-
"#/components/schemas/CreateOrderRequest"
920-
)
912+
"$ref": "#/components/schemas/CreateOrderRequest"
921913
}
922914
}
923915
},
@@ -953,9 +945,7 @@ def _get_coffee_shop_schema():
953945
"content": {
954946
"application/json": {
955947
"schema": {
956-
"$ref": (
957-
"#/components/schemas/UpdateOrderStatusRequest"
958-
)
948+
"$ref": "#/components/schemas/UpdateOrderStatusRequest"
959949
}
960950
}
961951
},

tests/unittests/utils/test_model.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,3 +204,81 @@ def test_is_final_instance_method(self):
204204
"""测试实例方法 is_final"""
205205
assert Status.READY.is_final() is True
206206
assert Status.CREATING.is_final() is False
207+
208+
209+
class TestTTLAliasFixIssue53:
210+
"""测试 TTL 字段的显式 alias 修复 (Issue #53)
211+
212+
验证含有连续大写缩写词 (TTL) 的字段能正确从 API 返回的 camelCase key 解析。
213+
"""
214+
215+
def test_template_sandbox_ttlin_seconds_from_api_data(self):
216+
"""Template.sandbox_ttlin_seconds 应能通过 sandboxTTLInSeconds 正确解析"""
217+
from agentrun.sandbox.template import Template
218+
219+
api_data = {
220+
"templateName": "code-interpreter-01",
221+
"sandboxIdleTimeoutInSeconds": 900,
222+
"sandboxTTLInSeconds": 3600,
223+
}
224+
t = Template.model_validate(api_data, by_alias=True)
225+
assert t.sandbox_idle_timeout_in_seconds == 900
226+
assert t.sandbox_ttlin_seconds == 3600
227+
assert t.model_extra.get("sandboxTTLInSeconds") is None
228+
229+
def test_template_sandbox_ttlin_seconds_by_field_name(self):
230+
"""Template.sandbox_ttlin_seconds 也应支持通过字段名直接赋值"""
231+
from agentrun.sandbox.template import Template
232+
233+
t = Template(sandbox_ttlin_seconds=1800)
234+
assert t.sandbox_ttlin_seconds == 1800
235+
236+
def test_template_sandbox_ttlin_seconds_serialization(self):
237+
"""Template 序列化时应使用正确的 alias sandboxTTLInSeconds"""
238+
from agentrun.sandbox.template import Template
239+
240+
t = Template(sandbox_ttlin_seconds=7200)
241+
data = t.model_dump(by_alias=True)
242+
assert data["sandboxTTLInSeconds"] == 7200
243+
244+
def test_template_input_sandbox_ttlin_seconds_serialization(self):
245+
"""TemplateInput.sandbox_ttlin_seconds 序列化应使用 sandboxTTLInSeconds"""
246+
from agentrun.sandbox.model import TemplateInput, TemplateType
247+
248+
inp = TemplateInput(
249+
template_type=TemplateType.CODE_INTERPRETER,
250+
sandbox_ttlin_seconds=600,
251+
)
252+
data = inp.model_dump(by_alias=True)
253+
assert data["sandboxTTLInSeconds"] == 600
254+
255+
def test_sandbox_idle_ttlin_seconds_from_api_data(self):
256+
"""Sandbox.sandbox_idle_ttlin_seconds 应能通过 sandboxIdleTTLInSeconds 正确解析"""
257+
from agentrun.sandbox.sandbox import Sandbox
258+
259+
api_data = {
260+
"sandboxId": "sb-123",
261+
"sandboxIdleTTLInSeconds": 300,
262+
"sandboxIdleTimeoutSeconds": 600,
263+
}
264+
s = Sandbox.model_validate(api_data, by_alias=True)
265+
assert s.sandbox_idle_ttlin_seconds == 300
266+
assert s.sandbox_idle_timeout_seconds == 600
267+
assert s.model_extra.get("sandboxIdleTTLInSeconds") is None
268+
269+
def test_template_from_inner_object_with_ttl(self):
270+
"""Template.from_inner_object 应正确解析 sandboxTTLInSeconds"""
271+
from agentrun.sandbox.template import Template
272+
273+
class MockDaraModel:
274+
275+
def to_map(self):
276+
return {
277+
"templateName": "test-template",
278+
"sandboxIdleTimeoutInSeconds": 900,
279+
"sandboxTTLInSeconds": 3600,
280+
}
281+
282+
t = Template.from_inner_object(MockDaraModel())
283+
assert t.sandbox_ttlin_seconds == 3600
284+
assert t.sandbox_idle_timeout_in_seconds == 900

0 commit comments

Comments
 (0)