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
51 changes: 42 additions & 9 deletions app/services/grok/services/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ async def _stream_retry() -> AsyncGenerator[str, None]:
token_mgr=token_mgr,
token=current_token,
model_info=model_info,
tried_tokens=tried_tokens,
prompt=prompt,
n=n,
response_format=response_format,
Expand Down Expand Up @@ -212,6 +213,7 @@ async def _collect_ws(
token_mgr: Any,
token: str,
model_info: Any,
tried_tokens: set[str],
prompt: str,
n: int,
response_format: str,
Expand All @@ -226,9 +228,9 @@ async def _collect_ws(
calls_needed = max(1, int(math.ceil(n / expected_per_call)))
calls_needed = min(calls_needed, n)

async def _fetch_batch(call_target: int):
async def _fetch_batch(call_target: int, call_token: str):
upstream = image_service.stream(
token=token,
token=call_token,
prompt=prompt,
aspect_ratio=aspect_ratio,
n=call_target,
Expand All @@ -246,7 +248,7 @@ async def _fetch_batch(call_target: int):
for i in range(calls_needed):
remaining = n - (i * expected_per_call)
call_target = min(expected_per_call, remaining)
tasks.append(_fetch_batch(call_target))
tasks.append(_fetch_batch(call_target, token))

results = await asyncio.gather(*tasks, return_exceptions=True)
for batch in results:
Expand All @@ -268,16 +270,47 @@ async def _fetch_batch(call_target: int):
remaining = n - len(all_images)
extra_attempts = int(get_config("image.blocked_parallel_attempts") or 5)
extra_attempts = max(0, min(extra_attempts, 10))
parallel_enabled = bool(get_config("image.blocked_parallel_enabled", True))
if extra_attempts > 0:
logger.warning(
f"Image finals insufficient ({len(all_images)}/{n}), running "
f"{extra_attempts} parallel recovery attempts for remaining={remaining}"
f"{extra_attempts} recovery attempts for remaining={remaining}, "
f"parallel_enabled={parallel_enabled}"
)
extra_tasks = [
_fetch_batch(min(expected_per_call, remaining))
for _ in range(extra_attempts)
]
extra_results = await asyncio.gather(*extra_tasks, return_exceptions=True)
extra_tasks = []
if parallel_enabled:
recovery_tried = set(tried_tokens)
recovery_tokens: List[str] = []
for _ in range(extra_attempts):
recovery_token = await pick_token(
token_mgr,
model_info.model_id,
recovery_tried,
)
if not recovery_token:
break
recovery_tried.add(recovery_token)
recovery_tokens.append(recovery_token)

if recovery_tokens:
logger.info(
f"Recovery using {len(recovery_tokens)} distinct tokens"
)
for recovery_token in recovery_tokens:
extra_tasks.append(
_fetch_batch(min(expected_per_call, remaining), recovery_token)
)
else:
extra_tasks = [
_fetch_batch(min(expected_per_call, remaining), token)
for _ in range(extra_attempts)
]

if not extra_tasks:
logger.warning("No tokens available for recovery attempts")
extra_results = []
else:
extra_results = await asyncio.gather(*extra_tasks, return_exceptions=True)
for batch in extra_results:
if isinstance(batch, Exception):
logger.warning(f"WS recovery batch failed: {batch}")
Expand Down
1 change: 1 addition & 0 deletions app/static/admin/js/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ const LOCALE_MAP = {
"nsfw": { title: "NSFW 模式", desc: "WebSocket 请求是否启用 NSFW。" },
"medium_min_bytes": { title: "中等图最小字节", desc: "判定中等质量图的最小字节数。" },
"final_min_bytes": { title: "最终图最小字节", desc: "判定最终图的最小字节数(通常 JPG > 100KB)。" },
"blocked_parallel_enabled": { title: "启用并行补偿", desc: "疑似审查/拦截时,是否启用并行补偿生成。" },
"blocked_parallel_attempts": { title: "拦截补偿并发次数", desc: "疑似审查/拦截导致无最终图时,自动并行补偿生成次数。" }
},

Expand Down
2 changes: 2 additions & 0 deletions config.defaults.toml
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ medium_min_bytes = 30000
final_min_bytes = 100000
# 遇到疑似审查/拦截时的并行补偿生成次数
blocked_parallel_attempts = 5
# 是否启用并行补偿(启用时优先使用不同 token)
blocked_parallel_enabled = true


# ==================== SuperImage 配置 ====================
Expand Down
2 changes: 1 addition & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ curl http://localhost:8000/v1/chat/completions \
- `grok-superimage-1.0` 与瀑布流 imagine 生成链路一致,可直接通过 `/v1/chat/completions` 调用;其 `n/size/response_format` 由服务端 `[superimage]` 统一控制。
- `grok-superimage-1.0` 在 `/v1/chat/completions` 的流式输出仅返回最终成图,不返回中间预览图。
- `grok-superimage-1.0` 流式 URL 出图会保持原始图片名(不追加 `-final` 后缀)。
- 当图片疑似被审查拦截导致无最终图时,服务端会按 `image.blocked_parallel_attempts` 自动并行补偿生成;若仍无满足 `image.final_min_bytes` 的最终图则返回失败。
- 当图片疑似被审查拦截导致无最终图时,若开启 `image.blocked_parallel_enabled`,服务端会按 `image.blocked_parallel_attempts` 自动并行补偿生成,并优先使用不同 token;若仍无满足 `image.final_min_bytes` 的最终图则返回失败。
- `grok-imagine-1.0-edit` 必须提供图片,多图默认取**最后 3 张**与最后一个文本。
- `grok-imagine-1.0-video` 支持文生视频与图生视频(通过 `image_url` 传参考图,**仅取第 1 张**)。
- 除上述外的其他参数将自动丢弃并忽略。
Expand Down
Loading