Skip to content

Commit aae4938

Browse files
authored
Merge pull request TQZHR#43 from lijirou12/codex/migrate-imagine-to-grok-superimage-1.0-hr1q8r
feat: expose grok-superimage-1.0 via /v1/chat/completions and fix image streaming
2 parents 5bab7ae + a6a298a commit aae4938

6 files changed

Lines changed: 80 additions & 3 deletions

File tree

app/api/v1/chat.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ class ChatCompletionRequest(BaseModel):
7878
"1024x1792",
7979
"1024x1024",
8080
}
81+
SUPERIMAGE_MODEL_ID = "grok-superimage-1.0"
8182

8283

8384
def _validate_media_input(value: str, field_name: str, param: str):
@@ -165,6 +166,17 @@ def _image_field(response_format: str) -> str:
165166
return "url"
166167
return "b64_json"
167168

169+
170+
def _superimage_server_image_config() -> ImageConfig:
171+
"""Load server-side image generation parameters for grok-superimage-1.0."""
172+
n = int(get_config("superimage.n", 1) or 1)
173+
size = str(get_config("superimage.size", "1024x1024") or "1024x1024")
174+
response_format = str(
175+
get_config("superimage.response_format", get_config("app.image_format") or "url")
176+
or "url"
177+
)
178+
return ImageConfig(n=n, size=size, response_format=response_format)
179+
168180
def _validate_image_config(image_conf: ImageConfig, *, stream: bool):
169181
n = image_conf.n or 1
170182
if n < 1 or n > 10:
@@ -514,7 +526,7 @@ def validate_request(request: ChatCompletionRequest):
514526
param="messages",
515527
code="empty_prompt",
516528
)
517-
image_conf = request.image_config or ImageConfig()
529+
image_conf = _superimage_server_image_config() if request.model == SUPERIMAGE_MODEL_ID else (request.image_config or ImageConfig())
518530
n = image_conf.n or 1
519531
if not (1 <= n <= 10):
520532
raise ValidationException(
@@ -685,7 +697,7 @@ async def chat_completions(request: ChatCompletionRequest):
685697
is_stream = (
686698
request.stream if request.stream is not None else get_config("app.stream")
687699
)
688-
image_conf = request.image_config or ImageConfig()
700+
image_conf = _superimage_server_image_config() if request.model == SUPERIMAGE_MODEL_ID else (request.image_config or ImageConfig())
689701
_validate_image_config(image_conf, stream=bool(is_stream))
690702
response_format = _resolve_image_format(image_conf.response_format)
691703
response_field = _image_field(response_format)

app/services/grok/services/image.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,7 @@ def _sse(self, event: str, data: dict) -> str:
436436

437437
async def process(self, response: AsyncIterable[dict]) -> AsyncGenerator[str, None]:
438438
images: Dict[str, Dict] = {}
439+
emitted_chat_chunk = False
439440

440441
async for item in response:
441442
if item.get("type") == "error":
@@ -515,6 +516,7 @@ async def process(self, response: AsyncIterable[dict]) -> AsyncGenerator[str, No
515516
if not self._id_generated:
516517
self._response_id = make_response_id()
517518
self._id_generated = True
519+
emitted_chat_chunk = True
518520
yield self._sse(
519521
"chat.completion.chunk",
520522
make_chat_chunk(
@@ -593,6 +595,7 @@ async def process(self, response: AsyncIterable[dict]) -> AsyncGenerator[str, No
593595

594596
if self.chat_format:
595597
# OpenAI ChatCompletion chunk format
598+
emitted_chat_chunk = True
596599
yield self._sse(
597600
"chat.completion.chunk",
598601
make_chat_chunk(
@@ -624,6 +627,23 @@ async def process(self, response: AsyncIterable[dict]) -> AsyncGenerator[str, No
624627
},
625628
)
626629

630+
if self.chat_format:
631+
if not self._id_generated:
632+
self._response_id = make_response_id()
633+
self._id_generated = True
634+
if not emitted_chat_chunk:
635+
yield self._sse(
636+
"chat.completion.chunk",
637+
make_chat_chunk(
638+
self._response_id,
639+
self.model,
640+
"",
641+
index=0,
642+
is_final=True,
643+
),
644+
)
645+
yield "data: [DONE]\n\n"
646+
627647

628648
class ImageWSCollectProcessor(ImageWSBaseProcessor):
629649
"""WebSocket image non-stream processor."""

app/services/grok/services/image_edit.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,7 @@ async def process(
323323
) -> AsyncGenerator[str, None]:
324324
"""Process stream response."""
325325
final_images = []
326+
emitted_chat_chunk = False
326327
idle_timeout = get_config("image.stream_timeout")
327328

328329
try:
@@ -352,6 +353,7 @@ async def process(
352353
if not self._id_generated:
353354
self._response_id = make_response_id()
354355
self._id_generated = True
356+
emitted_chat_chunk = True
355357
yield self._sse(
356358
"chat.completion.chunk",
357359
make_chat_chunk(
@@ -421,6 +423,7 @@ async def process(
421423

422424
if self.chat_format:
423425
# OpenAI ChatCompletion chunk format
426+
emitted_chat_chunk = True
424427
yield self._sse(
425428
"chat.completion.chunk",
426429
make_chat_chunk(
@@ -450,6 +453,23 @@ async def process(
450453
},
451454
},
452455
)
456+
457+
if self.chat_format:
458+
if not self._id_generated:
459+
self._response_id = make_response_id()
460+
self._id_generated = True
461+
if not emitted_chat_chunk:
462+
yield self._sse(
463+
"chat.completion.chunk",
464+
make_chat_chunk(
465+
self._response_id,
466+
self.model,
467+
"",
468+
index=0,
469+
is_final=True,
470+
),
471+
)
472+
yield "data: [DONE]\n\n"
453473
except asyncio.CancelledError:
454474
logger.debug("Image stream cancelled by client")
455475
except StreamIdleTimeoutError as e:

app/services/grok/services/model.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,18 @@ class ModelService:
174174
is_image_edit=False,
175175
is_video=False,
176176
),
177+
ModelInfo(
178+
model_id="grok-superimage-1.0",
179+
grok_model="grok-3",
180+
model_mode="MODEL_MODE_FAST",
181+
tier=Tier.BASIC,
182+
cost=Cost.HIGH,
183+
display_name="Grok SuperImage",
184+
description="Imagine waterfall image generation model for chat completions",
185+
is_image=True,
186+
is_image_edit=False,
187+
is_video=False,
188+
),
177189
ModelInfo(
178190
model_id="grok-imagine-1.0",
179191
grok_model="grok-3",

config.defaults.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,17 @@ medium_min_bytes = 30000
110110
# 判定为最终图的最小字节数
111111
final_min_bytes = 100000
112112

113+
114+
# ==================== SuperImage 配置 ====================
115+
[superimage]
116+
# 仅对 grok-superimage-1.0 生效,由服务端统一控制,不使用客户端 image_config
117+
n = 1
118+
# 图片尺寸:1280x720 / 720x1280 / 1792x1024 / 1024x1792 / 1024x1024
119+
size = "1024x1024"
120+
# 响应格式:url / b64_json / base64
121+
response_format = "url"
122+
123+
113124
# ==================== 视频配置 ====================
114125
[video]
115126
# Reverse 接口并发上限

readme.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ docker compose up -d
109109
| `grok-4.1-thinking` | 4 | Basic/Super | 支持 | 支持 | - |
110110
| `grok-4.20-beta` | 1 | Basic/Super | 支持 | 支持 | - |
111111
| `grok-imagine-1.0` | - | Basic/Super | - | 支持 | - |
112+
| `grok-superimage-1.0` | - | Basic/Super | - | 支持 | - |
112113
| `grok-imagine-1.0-edit` | - | Basic/Super | - | 支持 | - |
113114
| `grok-imagine-1.0-video` | - | Basic/Super | - | - | 支持 |
114115

@@ -151,7 +152,7 @@ curl http://localhost:8000/v1/chat/completions \
151152
| └─`video_length` | integer | 视频时长 (秒) | `6`, `10`, `15` |
152153
| └─`resolution_name` | string | 分辨率 | `480p`, `720p` |
153154
| └─`preset` | string | 风格预设 | `fun`, `normal`, `spicy`, `custom` |
154-
| `image_config` | object | **图片模型专用配置对象** | 支持:`grok-imagine-1.0` / `grok-imagine-1.0-edit` |
155+
| `image_config` | object | **图片模型专用配置对象** | 支持:`grok-imagine-1.0` / `grok-superimage-1.0` / `grok-imagine-1.0-edit` |
155156
| └─`n` | integer | 生成数量 | `1` ~ `10` |
156157
| └─`size` | string | 图片尺寸 | `1280x720`, `720x1280`, `1792x1024`, `1024x1792`, `1024x1024` |
157158
| └─`response_format` | string | 响应格式 | `url`, `b64_json`, `base64` |
@@ -177,6 +178,7 @@ curl http://localhost:8000/v1/chat/completions \
177178
- `image_url/input_audio/file` 仅支持 URL 或 Data URI(`data:<mime>;base64,...`),裸 base64 会报错。
178179
- `reasoning_effort``none` 表示不输出思考,其他值都会输出思考内容。
179180
- 工具调用为**提示词模拟 + 客户端执行回填**:模型通过 `<tool_call>{...}</tool_call>` 输出调用请求,服务端解析为 `tool_calls`;不执行工具。
181+
- `grok-superimage-1.0` 与瀑布流 imagine 生成链路一致,可直接通过 `/v1/chat/completions` 调用;其 `n/size/response_format` 由服务端 `[superimage]` 统一控制。
180182
- `grok-imagine-1.0-edit` 必须提供图片,多图默认取**最后 3 张**与最后一个文本。
181183
- `grok-imagine-1.0-video` 支持文生视频与图生视频(通过 `image_url` 传参考图,**仅取第 1 张**)。
182184
- 除上述外的其他参数将自动丢弃并忽略。

0 commit comments

Comments
 (0)