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
42 changes: 42 additions & 0 deletions app/services/grok/services/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,48 @@ async def _fetch_batch(call_target: int):
if len(all_images) >= n:
break

# If upstream likely blocked/reviewed some images, run extra parallel attempts
# and only keep valid finals selected by ws_imagine classification.
if len(all_images) < n:
remaining = n - len(all_images)
extra_attempts = int(get_config("image.blocked_parallel_attempts") or 5)
extra_attempts = max(0, min(extra_attempts, 10))
if extra_attempts > 0:
logger.warning(
f"Image finals insufficient ({len(all_images)}/{n}), running "
f"{extra_attempts} parallel recovery attempts for remaining={remaining}"
)
extra_tasks = [
_fetch_batch(min(expected_per_call, remaining))
for _ in range(extra_attempts)
]
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}")
continue
for img in batch:
if img not in seen:
seen.add(img)
all_images.append(img)
if len(all_images) >= n:
break
if len(all_images) >= n:
break

if len(all_images) < n:
logger.error(
f"Image generation failed after recovery attempts: finals={len(all_images)}/{n}"
)
raise UpstreamException(
"Image generation blocked or no valid final image",
details={
"error_code": "blocked_no_final_image",
"final_images": len(all_images),
"requested": n,
},
)

try:
await token_mgr.consume(token, self._get_effort(model_info))
except Exception as e:
Expand Down
9 changes: 9 additions & 0 deletions app/services/reverse/ws_imagine.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,10 @@ async def _stream_once(
and completed == 0
and now - medium_received_time > blocked_grace
):
logger.warning(
"Imagine stream blocked suspected: received medium preview but no valid final image "
f"within {blocked_grace:.1f}s (request_id={request_id})"
)
raise _BlockedError()
if completed > 0 and now - last_activity > 10:
logger.info(
Expand Down Expand Up @@ -258,6 +262,11 @@ async def _stream_once(
and completed == 0
and time.monotonic() - medium_received_time > final_timeout
):
logger.warning(
"Imagine stream final-timeout suspected review/block: "
f"no final image reached threshold in {final_timeout:.1f}s "
f"(request_id={request_id})"
)
raise _BlockedError()

elif ws_msg.type in (
Expand Down
12 changes: 11 additions & 1 deletion app/static/admin/js/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const NUMERIC_FIELDS = new Set([
'final_timeout',
'final_min_bytes',
'medium_min_bytes',
'blocked_parallel_attempts',
'concurrent',
'batch_size'
]);
Expand Down Expand Up @@ -96,7 +97,16 @@ const LOCALE_MAP = {
"final_timeout": { title: "最终图超时", desc: "收到中等图后等待最终图的超时秒数。" },
"nsfw": { title: "NSFW 模式", desc: "WebSocket 请求是否启用 NSFW。" },
"medium_min_bytes": { title: "中等图最小字节", desc: "判定中等质量图的最小字节数。" },
"final_min_bytes": { title: "最终图最小字节", desc: "判定最终图的最小字节数(通常 JPG > 100KB)。" }
"final_min_bytes": { title: "最终图最小字节", desc: "判定最终图的最小字节数(通常 JPG > 100KB)。" },
"blocked_parallel_attempts": { title: "拦截补偿并发次数", desc: "疑似审查/拦截导致无最终图时,自动并行补偿生成次数。" }
},


"superimage": {
"label": "SuperImage 配置",
"n": { title: "生成数量", desc: "仅用于 grok-superimage-1.0 的服务端统一生成数量(1-10)。" },
"size": { title: "图片尺寸", desc: "仅用于 grok-superimage-1.0 的服务端统一尺寸。" },
"response_format": { title: "响应格式", desc: "仅用于 grok-superimage-1.0 的服务端统一返回格式。" }
},


Expand Down
13 changes: 13 additions & 0 deletions config.defaults.toml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,19 @@ nsfw = true
medium_min_bytes = 30000
# 判定为最终图的最小字节数
final_min_bytes = 100000
# 遇到疑似审查/拦截时的并行补偿生成次数
blocked_parallel_attempts = 5


# ==================== SuperImage 配置 ====================
[superimage]
# 仅对 grok-superimage-1.0 生效,由服务端统一控制,不使用客户端 image_config
n = 1
# 图片尺寸:1280x720 / 720x1280 / 1792x1024 / 1024x1792 / 1024x1024
size = "1024x1024"
# 响应格式:url / b64_json / base64
response_format = "url"



# ==================== SuperImage 配置 ====================
Expand Down
1 change: 1 addition & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +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` 的最终图则返回失败。
- `grok-imagine-1.0-edit` 必须提供图片,多图默认取**最后 3 张**与最后一个文本。
- `grok-imagine-1.0-video` 支持文生视频与图生视频(通过 `image_url` 传参考图,**仅取第 1 张**)。
- 除上述外的其他参数将自动丢弃并忽略。
Expand Down
Loading