Skip to content

Commit da72d58

Browse files
committed
feat(config): 支持后台持久化 max_retries 配置
1 parent e072589 commit da72d58

8 files changed

Lines changed: 185 additions & 35 deletions

File tree

src/api/admin.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -508,8 +508,9 @@ class CaptchaScoreTestRequest(BaseModel):
508508

509509

510510
class GenerationConfigRequest(BaseModel):
511-
image_timeout: int
512-
video_timeout: int
511+
image_timeout: Optional[int] = None
512+
video_timeout: Optional[int] = None
513+
max_retries: Optional[int] = None
513514

514515

515516
class CallLogicConfigRequest(BaseModel):
@@ -1132,7 +1133,8 @@ async def get_generation_config(token: str = Depends(verify_admin_token)):
11321133
"success": True,
11331134
"config": {
11341135
"image_timeout": config.image_timeout,
1135-
"video_timeout": config.video_timeout
1136+
"video_timeout": config.video_timeout,
1137+
"max_retries": config.max_retries,
11361138
}
11371139
}
11381140

@@ -1143,7 +1145,11 @@ async def update_generation_config(
11431145
token: str = Depends(verify_admin_token)
11441146
):
11451147
"""Update generation timeout configuration"""
1146-
await db.update_generation_config(request.image_timeout, request.video_timeout)
1148+
await db.update_generation_config(
1149+
image_timeout=request.image_timeout,
1150+
video_timeout=request.video_timeout,
1151+
max_retries=request.max_retries,
1152+
)
11471153

11481154
# 🔥 Hot reload: sync database config to memory
11491155
await db.reload_config_to_memory()
@@ -1390,7 +1396,11 @@ async def update_generation_timeout(
13901396
token: str = Depends(verify_admin_token)
13911397
):
13921398
"""Update generation timeout configuration"""
1393-
await db.update_generation_config(request.image_timeout, request.video_timeout)
1399+
await db.update_generation_config(
1400+
image_timeout=request.image_timeout,
1401+
video_timeout=request.video_timeout,
1402+
max_retries=request.max_retries,
1403+
)
13941404

13951405
# 🔥 Hot reload: sync database config to memory
13961406
await db.reload_config_to_memory()

src/core/config.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,16 @@ def flow_max_retries(self) -> int:
6868
except Exception:
6969
return 3
7070

71+
def set_flow_max_retries(self, retries: int):
72+
"""Set flow max retries"""
73+
if "flow" not in self._config:
74+
self._config["flow"] = {}
75+
try:
76+
normalized = max(1, int(retries))
77+
except Exception:
78+
normalized = 3
79+
self._config["flow"]["max_retries"] = normalized
80+
7181
@property
7282
def flow_image_request_timeout(self) -> int:
7383
"""图片生成单次 HTTP 请求超时(秒)。"""

src/core/database.py

Lines changed: 68 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -130,16 +130,24 @@ async def _ensure_config_rows(self, db, config_dict: dict = None):
130130
if count[0] == 0:
131131
image_timeout = 300
132132
video_timeout = 1500
133+
max_retries = 3
133134

134135
if config_dict:
135136
generation_config = config_dict.get("generation", {})
137+
flow_config = config_dict.get("flow", {})
136138
image_timeout = generation_config.get("image_timeout", 300)
137139
video_timeout = generation_config.get("video_timeout", 1500)
140+
max_retries = flow_config.get("max_retries", 3)
141+
142+
try:
143+
max_retries = max(1, int(max_retries))
144+
except Exception:
145+
max_retries = 3
138146

139147
await db.execute("""
140-
INSERT INTO generation_config (id, image_timeout, video_timeout)
141-
VALUES (1, ?, ?)
142-
""", (image_timeout, video_timeout))
148+
INSERT INTO generation_config (id, image_timeout, video_timeout, max_retries)
149+
VALUES (1, ?, ?, ?)
150+
""", (image_timeout, video_timeout, max_retries))
143151

144152
# Ensure call_logic_config has a row
145153
cursor = await db.execute("SELECT COUNT(*) FROM call_logic_config")
@@ -432,6 +440,20 @@ async def check_and_migrate_db(self, config_dict: dict = None):
432440
except Exception as e:
433441
print(f" ✗ Failed to add column '{col_name}': {e}")
434442

443+
# Check and add missing columns to generation_config table
444+
if await self._table_exists(db, "generation_config"):
445+
generation_columns_to_add = [
446+
("max_retries", "INTEGER DEFAULT 3"),
447+
]
448+
449+
for col_name, col_type in generation_columns_to_add:
450+
if not await self._column_exists(db, "generation_config", col_name):
451+
try:
452+
await db.execute(f"ALTER TABLE generation_config ADD COLUMN {col_name} {col_type}")
453+
print(f" ✓ Added column '{col_name}' to generation_config table")
454+
except Exception as e:
455+
print(f" ✗ Failed to add column '{col_name}': {e}")
456+
435457
# Check and add missing columns to captcha_config table
436458
if await self._table_exists(db, "captcha_config"):
437459
captcha_columns_to_add = [
@@ -647,6 +669,7 @@ async def init_db(self):
647669
id INTEGER PRIMARY KEY DEFAULT 1,
648670
image_timeout INTEGER DEFAULT 300,
649671
video_timeout INTEGER DEFAULT 1500,
672+
max_retries INTEGER DEFAULT 3,
650673
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
651674
)
652675
""")
@@ -1292,14 +1315,49 @@ async def get_generation_config(self) -> Optional[GenerationConfig]:
12921315
return GenerationConfig(**dict(row))
12931316
return None
12941317

1295-
async def update_generation_config(self, image_timeout: int, video_timeout: int):
1318+
async def update_generation_config(
1319+
self,
1320+
image_timeout: Optional[int] = None,
1321+
video_timeout: Optional[int] = None,
1322+
max_retries: Optional[int] = None,
1323+
):
12961324
"""Update generation configuration"""
12971325
async with self._connect(write=True) as db:
1298-
await db.execute("""
1299-
UPDATE generation_config
1300-
SET image_timeout = ?, video_timeout = ?, updated_at = CURRENT_TIMESTAMP
1301-
WHERE id = 1
1302-
""", (image_timeout, video_timeout))
1326+
db.row_factory = aiosqlite.Row
1327+
cursor = await db.execute("SELECT * FROM generation_config WHERE id = 1")
1328+
row = await cursor.fetchone()
1329+
current = dict(row) if row else {}
1330+
1331+
normalized_image_timeout = (
1332+
image_timeout
1333+
if image_timeout is not None
1334+
else current.get("image_timeout", 300)
1335+
)
1336+
normalized_video_timeout = (
1337+
video_timeout
1338+
if video_timeout is not None
1339+
else current.get("video_timeout", 1500)
1340+
)
1341+
try:
1342+
normalized_max_retries = (
1343+
max(1, int(max_retries))
1344+
if max_retries is not None
1345+
else max(1, int(current.get("max_retries", 3)))
1346+
)
1347+
except Exception:
1348+
normalized_max_retries = 3
1349+
1350+
if row:
1351+
await db.execute("""
1352+
UPDATE generation_config
1353+
SET image_timeout = ?, video_timeout = ?, max_retries = ?, updated_at = CURRENT_TIMESTAMP
1354+
WHERE id = 1
1355+
""", (normalized_image_timeout, normalized_video_timeout, normalized_max_retries))
1356+
else:
1357+
await db.execute("""
1358+
INSERT INTO generation_config (id, image_timeout, video_timeout, max_retries)
1359+
VALUES (1, ?, ?, ?)
1360+
""", (normalized_image_timeout, normalized_video_timeout, normalized_max_retries))
13031361
await db.commit()
13041362

13051363
async def get_call_logic_config(self) -> CallLogicConfig:
@@ -1531,6 +1589,7 @@ async def reload_config_to_memory(self):
15311589
if generation_config:
15321590
config.set_image_timeout(generation_config.image_timeout)
15331591
config.set_video_timeout(generation_config.video_timeout)
1592+
config.set_flow_max_retries(generation_config.max_retries)
15341593

15351594
# Reload call logic config
15361595
call_logic_config = await self.get_call_logic_config()

src/core/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ class GenerationConfig(BaseModel):
138138
id: int = 1
139139
image_timeout: int = 300 # seconds
140140
video_timeout: int = 1500 # seconds
141+
max_retries: int = 3 # 请求最大重试次数
141142

142143

143144
class CallLogicConfig(BaseModel):

src/services/flow_client.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -631,7 +631,7 @@ async def create_project(self, st: str, title: str) -> str:
631631
"toolName": "PINHOLE"
632632
}
633633
}
634-
max_retries = max(2, min(4, int(getattr(config, "flow_max_retries", 3) or 3)))
634+
max_retries = config.flow_max_retries
635635
request_timeout = max(self._get_control_plane_timeout(), min(self.timeout, 15))
636636
last_error: Optional[Exception] = None
637637

@@ -839,7 +839,7 @@ async def upload_image(
839839
"tool": "ASSET_MANAGER"
840840
}
841841
}
842-
max_retries = max(1, getattr(config, "flow_max_retries", 3))
842+
max_retries = config.flow_max_retries
843843
last_error: Optional[Exception] = None
844844

845845
for retry_attempt in range(max_retries):
@@ -1106,8 +1106,8 @@ async def upsample_image(
11061106
"""
11071107
url = f"{self.api_base_url}/flow/upsampleImage"
11081108

1109-
# 403/reCAPTCHA/500 重试逻辑 - 最多重试3次
1110-
max_retries = 3
1109+
# 403/reCAPTCHA/500 重试逻辑 - 使用配置的最大重试次数
1110+
max_retries = config.flow_max_retries
11111111
last_error = None
11121112

11131113
for retry_attempt in range(max_retries):
@@ -1226,8 +1226,8 @@ async def generate_video_text(
12261226
"""
12271227
url = f"{self.api_base_url}/video:batchAsyncGenerateVideoText"
12281228

1229-
# 403/reCAPTCHA 重试逻辑 - 最多重试3次
1230-
max_retries = 3
1229+
# 403/reCAPTCHA 重试逻辑 - 使用配置的最大重试次数
1230+
max_retries = config.flow_max_retries
12311231
last_error = None
12321232

12331233
for retry_attempt in range(max_retries):
@@ -1350,8 +1350,8 @@ async def generate_video_reference_images(
13501350
"""
13511351
url = f"{self.api_base_url}/video:batchAsyncGenerateVideoReferenceImages"
13521352

1353-
# 403/reCAPTCHA 重试逻辑 - 最多重试3次
1354-
max_retries = 3
1353+
# 403/reCAPTCHA 重试逻辑 - 使用配置的最大重试次数
1354+
max_retries = config.flow_max_retries
13551355
last_error = None
13561356

13571357
for retry_attempt in range(max_retries):
@@ -1483,8 +1483,8 @@ async def generate_video_start_end(
14831483
"""
14841484
url = f"{self.api_base_url}/video:batchAsyncGenerateVideoStartAndEndImage"
14851485

1486-
# 403/reCAPTCHA 重试逻辑 - 最多重试3次
1487-
max_retries = 3
1486+
# 403/reCAPTCHA 重试逻辑 - 使用配置的最大重试次数
1487+
max_retries = config.flow_max_retries
14881488
last_error = None
14891489

14901490
for retry_attempt in range(max_retries):
@@ -1614,8 +1614,8 @@ async def generate_video_start_image(
16141614
"""
16151615
url = f"{self.api_base_url}/video:batchAsyncGenerateVideoStartImage"
16161616

1617-
# 403/reCAPTCHA 重试逻辑 - 最多重试3次
1618-
max_retries = 3
1617+
# 403/reCAPTCHA 重试逻辑 - 使用配置的最大重试次数
1618+
max_retries = config.flow_max_retries
16191619
last_error = None
16201620

16211621
for retry_attempt in range(max_retries):
@@ -1742,8 +1742,8 @@ async def upsample_video(
17421742
"""
17431743
url = f"{self.api_base_url}/video:batchAsyncGenerateVideoUpsampleVideo"
17441744

1745-
# 403/reCAPTCHA 重试逻辑 - 最多重试3次
1746-
max_retries = 3
1745+
# 403/reCAPTCHA 重试逻辑 - 使用配置的最大重试次数
1746+
max_retries = config.flow_max_retries
17471747
last_error = None
17481748

17491749
for retry_attempt in range(max_retries):
@@ -1855,7 +1855,7 @@ async def check_video_status(self, at: str, operations: List[Dict]) -> dict:
18551855
json_data = {
18561856
"operations": operations
18571857
}
1858-
max_retries = max(1, getattr(config, "flow_max_retries", 3))
1858+
max_retries = config.flow_max_retries
18591859
last_error: Optional[Exception] = None
18601860

18611861
for retry_attempt in range(max_retries):

src/services/generation_handler.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1321,8 +1321,8 @@ async def _image_progress_callback(status_text: str, progress: int):
13211321
if stream:
13221322
yield self._create_stream_chunk(f"正在放大图片到 {resolution_name}...\n")
13231323

1324-
# 4K/2K 图片重试逻辑 - 最多重试3次
1325-
max_retries = 3
1324+
# 4K/2K 图片重试逻辑 - 使用配置的最大重试次数
1325+
max_retries = config.flow_max_retries
13261326
for retry_attempt in range(max_retries):
13271327
try:
13281328
# 调用 upsample API

0 commit comments

Comments
 (0)