fix(ai-outfit): per-resolution timeout + _to_int default fallback
- §8 dynamic timeout: add resolution_timeout() (512/1K/2K/4K -> 180/240/360/600s); generate() uses it for the POST and image download. timeout_seconds now defaults to 0 = auto-by-resolution; an explicit positive value overrides. Validation allows 0, rejects negatives. - _to_int now returns the supplied default on parse failure (was 0), so a bad connect_timeout_seconds falls back to 30 instead of failing as 0. - Tests: +5 (resolution map, auto vs override timeout, connect fallback). - tasks.md §19.1/§19.2: tick unit-test item, note runner is pure-logic. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -215,6 +215,59 @@ class TestImageApiClient(unittest.TestCase):
|
||||
self.assertEqual(session.last_headers["Authorization"], "Bearer secret")
|
||||
self.assertEqual(session.last_json["model"], "model-x")
|
||||
|
||||
def test_generate_uses_resolution_timeout_when_unset(self):
|
||||
from services.ai_image_service import ImageApiClient
|
||||
|
||||
session = _FakeSession()
|
||||
config = {"url": "https://api.example.test", "model": "m", "api_key": "k", "api_type": "chat"}
|
||||
|
||||
ImageApiClient(config, session=session).generate("p", self.image_path, resolution="4K")
|
||||
|
||||
# timeout_seconds absent -> (connect 30, read 600 for 4K)
|
||||
self.assertEqual(session.last_timeout, (30, 600))
|
||||
|
||||
def test_generate_explicit_timeout_overrides_resolution(self):
|
||||
from services.ai_image_service import ImageApiClient
|
||||
|
||||
session = _FakeSession()
|
||||
config = {
|
||||
"url": "https://api.example.test",
|
||||
"model": "m",
|
||||
"api_key": "k",
|
||||
"api_type": "chat",
|
||||
"timeout_seconds": 120,
|
||||
}
|
||||
|
||||
ImageApiClient(config, session=session).generate("p", self.image_path, resolution="4K")
|
||||
|
||||
self.assertEqual(session.last_timeout, (30, 120))
|
||||
|
||||
|
||||
class TestTimeoutHelpers(unittest.TestCase):
|
||||
def test_resolution_timeout_mapping(self):
|
||||
from services.ai_image_service import resolution_timeout
|
||||
|
||||
self.assertEqual(resolution_timeout("512"), 180)
|
||||
self.assertEqual(resolution_timeout("1K"), 240)
|
||||
self.assertEqual(resolution_timeout("2k"), 360) # case-insensitive
|
||||
self.assertEqual(resolution_timeout("4K"), 600)
|
||||
self.assertEqual(resolution_timeout("weird"), 240) # unknown -> default
|
||||
|
||||
def test_blank_timeout_is_auto_and_valid(self):
|
||||
from services.ai_image_service import AiModelConfig, api_config_errors
|
||||
|
||||
cfg = AiModelConfig.from_dict({"url": "https://x", "model": "m", "api_key": "k"})
|
||||
self.assertEqual(cfg.timeout_seconds, 0) # 0 = auto by resolution
|
||||
self.assertEqual(api_config_errors(cfg), [])
|
||||
|
||||
def test_invalid_connect_timeout_falls_back_to_default(self):
|
||||
from services.ai_image_service import AiModelConfig
|
||||
|
||||
cfg = AiModelConfig.from_dict(
|
||||
{"url": "https://x", "model": "m", "api_key": "k", "connect_timeout_seconds": "bad"}
|
||||
)
|
||||
self.assertEqual(cfg.connect_timeout_seconds, 30)
|
||||
|
||||
|
||||
class _FakeResponse:
|
||||
def __init__(self, payload=None, content=b"downloaded"):
|
||||
@@ -239,6 +292,7 @@ class _FakeSession:
|
||||
self.last_post_url = None
|
||||
self.last_headers = None
|
||||
self.last_json = None
|
||||
self.last_timeout = None
|
||||
|
||||
def get(self, url, timeout=60):
|
||||
self.last_url = url
|
||||
@@ -248,6 +302,7 @@ class _FakeSession:
|
||||
self.last_post_url = url
|
||||
self.last_headers = headers
|
||||
self.last_json = json
|
||||
self.last_timeout = timeout
|
||||
return _FakeResponse()
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user