diff --git a/app/services/grok/services/image.py b/app/services/grok/services/image.py index d7e0771ca..35da0f7f2 100644 --- a/app/services/grok/services/image.py +++ b/app/services/grok/services/image.py @@ -546,33 +546,36 @@ async def process(self, response: AsyncIterable[dict]) -> AsyncGenerator[str, No ) if self.n == 1: - if self._target_id and self._target_id in images: + if ( + self._target_id + and self._target_id in images + and images[self._target_id].get("is_final", False) + ): selected = [(self._target_id, images[self._target_id])] else: + final_candidates = [ + item for item in images.items() if item[1].get("is_final", False) + ] selected = ( - [ - max( - images.items(), - key=lambda x: ( - x[1].get("is_final", False), - x[1].get("blob_size", 0), - ), - ) - ] - if images + [max(final_candidates, key=lambda x: x[1].get("blob_size", 0))] + if final_candidates else [] ) else: selected = [ (image_id, images[image_id]) for image_id in self._index_map - if image_id in images + if image_id in images and images[image_id].get("is_final", False) ] for image_id, item in selected: if self.response_format == "url": + final_image_id = image_id + # Keep original imagine image name for superimage chat stream output. + if self.model != "grok-superimage-1.0": + final_image_id = f"{image_id}-final" output = await self._save_blob( - f"{image_id}-final", + final_image_id, item.get("blob", ""), item.get("is_final", False), ext=item.get("ext"), @@ -672,8 +675,8 @@ async def process(self, response: AsyncIterable[dict]) -> List[str]: images[image_id] = self._pick_best(images.get(image_id), item) selected = sorted( - images.values(), - key=lambda x: (x.get("is_final", False), x.get("blob_size", 0)), + [item for item in images.values() if item.get("is_final", False)], + key=lambda x: x.get("blob_size", 0), reverse=True, ) if self.n: diff --git a/app/services/reverse/ws_imagine.py b/app/services/reverse/ws_imagine.py index e9e648af9..36467a725 100644 --- a/app/services/reverse/ws_imagine.py +++ b/app/services/reverse/ws_imagine.py @@ -37,10 +37,9 @@ def _parse_image_url(self, url: str) -> tuple[Optional[str], Optional[str]]: return match.group(1), match.group(2).lower() def _is_final_image(self, url: str, blob_size: int, final_min_bytes: int) -> bool: - url_lower = (url or "").lower() - if url_lower.endswith((".jpg", ".jpeg")): - return True - return blob_size > final_min_bytes + # Final image must satisfy byte-size threshold to avoid tiny preview + # images being treated as final outputs. + return blob_size >= final_min_bytes def _classify_image(self, url: str, blob: str, final_min_bytes: int, medium_min_bytes: int) -> Optional[Dict[str, object]]: if not url or not blob: diff --git a/readme.md b/readme.md index 059c3772f..9aff133b9 100644 --- a/readme.md +++ b/readme.md @@ -180,6 +180,7 @@ curl http://localhost:8000/v1/chat/completions \ - 工具调用为**提示词模拟 + 客户端执行回填**:模型通过 `{...}` 输出调用请求,服务端解析为 `tool_calls`;不执行工具。 - `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` 后缀)。 - `grok-imagine-1.0-edit` 必须提供图片,多图默认取**最后 3 张**与最后一个文本。 - `grok-imagine-1.0-video` 支持文生视频与图生视频(通过 `image_url` 传参考图,**仅取第 1 张**)。 - 除上述外的其他参数将自动丢弃并忽略。