feat: add OpenAI image generation controls
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-05-02
|
||||
@@ -0,0 +1,46 @@
|
||||
## Context
|
||||
|
||||
现有普通 Responses 流式已经具备较完整的断连续写能力:客户端写失败后可继续 drain 上游,并且具备数据间隔超时和 keepalive。图片流式路径目前仍然采用更直接的读写方式,客户端写失败会立即返回,上游读取也更容易跟随客户端取消而结束。
|
||||
|
||||
本次变更只针对图片流式路径,不改变普通文本流式路径的配置和行为。系统已经存在普通流式的后端超时配置,因此这里不引入页面级超时设置;图片流式只需要独立的后端默认值,让图片生成有更长的容忍窗口。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- 图片流式在客户端断开后继续读取上游,尽量保留最终图片结果与计费结果。
|
||||
- 图片流式使用独立于普通流式的超时与 keepalive 默认值。
|
||||
- 不修改现有普通流式配置项的含义,不要求管理员新增页面配置。
|
||||
- 维持图片计费与图片结果计数的一致性。
|
||||
|
||||
**Non-Goals:**
|
||||
- 不设计新的前端配置页面。
|
||||
- 不修改普通文本流式的超时策略。
|
||||
- 不改变图片计费公式或分组倍率语义。
|
||||
|
||||
## Decisions
|
||||
|
||||
1. **使用独立的图片流式配置键**
|
||||
- 选择:在后端配置中增加图片流式专用 `image_stream_data_interval_timeout` / `image_stream_keepalive_interval`。
|
||||
- 原因:图片流式耗时显著更长,复用普通流式默认值会过早触发超时;独立键能避免影响现有文本流式。
|
||||
- 备选方案:直接复用普通流式配置并在代码里按路径放大倍数。这个方案会让普通流式和图片流式共享语义,后续难以维护。
|
||||
|
||||
2. **继续使用上下文 detach,而不是依赖客户端上下文**
|
||||
- 选择:图片流式请求向上游发起时使用 `context.WithoutCancel` 派生的上下文。
|
||||
- 原因:客户端断开时不应自动取消上游请求,否则无法收集最终图片结果,也无法完成图片计费。
|
||||
- 备选方案:仍使用 `c.Request.Context()` 并只在写失败后继续 drain。这个方案在客户端取消场景下无法保证上游读取继续进行。
|
||||
|
||||
3. **只改图片流式路径,不改普通流式路径**
|
||||
- 选择:`/v1/images/*` 与 `Responses + image_generation` 两条图片流式链路单独处理。
|
||||
- 原因:风险最小,避免回归普通文本流式和现有超时配置。
|
||||
- 备选方案:统一重构所有流式处理。这个方案范围更大,验证成本更高,不符合本次“尽量少改现有行为”的目标。
|
||||
|
||||
4. **不新增页面配置**
|
||||
- 选择:图片流式独立超时默认值写入后端配置,沿用当前配置加载方式。
|
||||
- 原因:用户明确要求和当前设置行为统一,不需要额外页面输入项。
|
||||
- 备选方案:前端增加图片超时配置项。这个方案会改变现有运维方式,也容易引入误配。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [Risk] 图片流式继续 drain 上游后,客户端已经断开但服务端仍占用连接与协程资源。→ [Mitigation] 只对图片流式启用更长但仍有限的专用超时,并保持与普通流式同样的 keepalive/超时退出机制。
|
||||
- [Risk] 图片流式与普通流式的默认超时不同,运维如果只关注通用配置可能忽略图片专用值。→ [Mitigation] 在配置示例中明确标注图片流式专用默认值和用途。
|
||||
- [Risk] 断连后继续读取可能导致日志中出现“客户端断开但最终成功”的状态。→ [Mitigation] 保留现有图片计费结果返回语义,同时让调用方在结果与错误并存时优先使用结果对象。
|
||||
@@ -0,0 +1,25 @@
|
||||
## Why
|
||||
|
||||
图片流式路径目前没有和普通 Responses 流式一致的断连续写策略,也没有独立于普通流式的超时控制。由于图片生成耗时更长,如果继续沿用普通流式处理方式,客户端断开时容易中断上游读取,影响图片产物收集与按图计费的准确性。
|
||||
|
||||
## What Changes
|
||||
|
||||
- 为 OpenAI Images API 和 `Responses + image_generation` 流式路径补充独立的上游续读策略,客户端断开后继续 drain 上游,尽量保留最终图片结果和计费结果。
|
||||
- 为图片流式路径使用独立的流数据间隔超时与 keepalive 策略,默认比普通流式更长,不新增页面配置项。
|
||||
- 保持现有普通流式配置与行为不变,避免影响已经配置好的普通文本分组。
|
||||
- 让图片流式路径在超时、断连、写入失败等场景下保持图片计费语义一致。
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `image-stream-resilience`: 图片流式路径的断连续读、独立超时和计费保留能力。
|
||||
|
||||
### Modified Capabilities
|
||||
- `image-generation-billing-accounting`: 图片流式结果计数和计费结果的稳定性行为发生改变,但计费契约不变。
|
||||
|
||||
## Impact
|
||||
|
||||
- 影响 `backend/internal/service/openai_images.go` 和 `backend/internal/service/openai_images_responses.go` 的流式实现。
|
||||
- 影响 `backend/internal/config/config.go` 与 `deploy/config.example.yaml` 中图片流式默认值和校验逻辑。
|
||||
- 影响 `backend/internal/service/openai_images_test.go`、`backend/internal/config/config_test.go` 以及新增的图片流式稳定性测试。
|
||||
- 不新增前端页面设置,不改变普通流式配置项名称和语义。
|
||||
@@ -0,0 +1,53 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Image stream resilience
|
||||
The system SHALL keep image generation stream processing active after downstream client disconnects so long as upstream reading can continue, in order to preserve final image outputs and billing results.
|
||||
|
||||
#### Scenario: Images API stream survives downstream disconnect
|
||||
- **WHEN** `/v1/images/generations` is streamed to a client
|
||||
- **AND** the downstream writer returns an error before the upstream stream completes
|
||||
- **THEN** the service continues draining the upstream stream
|
||||
- **AND** it still counts final image outputs if the upstream later emits them
|
||||
- **AND** the request can still complete with image billing metadata
|
||||
|
||||
#### Scenario: Responses image tool stream survives downstream disconnect
|
||||
- **WHEN** a `/v1/responses` request uses `image_generation` and is streamed to a client
|
||||
- **AND** the downstream writer returns an error before the upstream stream completes
|
||||
- **THEN** the service continues draining the upstream stream
|
||||
- **AND** it still counts final image outputs if the upstream later emits them
|
||||
- **AND** the request can still complete with image billing metadata
|
||||
|
||||
#### Scenario: Client disconnect does not force image stream to downgrade to text billing
|
||||
- **WHEN** a successful image stream request has already produced final image outputs
|
||||
- **AND** the downstream client disconnects before the final flush
|
||||
- **THEN** the request remains billed as an image request
|
||||
- **AND** the image count is preserved in the forward result
|
||||
|
||||
### Requirement: Image stream timeout isolation
|
||||
The system SHALL use image-specific streaming timeout settings for image generation stream paths, and these settings SHALL be independent from the ordinary text streaming timeout values.
|
||||
|
||||
#### Scenario: Image stream uses dedicated timeout defaults
|
||||
- **WHEN** an image generation stream path is executed
|
||||
- **THEN** it uses the image-specific data interval timeout and keepalive interval defaults
|
||||
- **AND** it does not rely on the ordinary text stream timeout defaults
|
||||
|
||||
#### Scenario: Ordinary stream settings remain unchanged
|
||||
- **WHEN** a normal non-image streaming request is executed
|
||||
- **THEN** the existing ordinary stream timeout configuration and behavior remain unchanged
|
||||
|
||||
#### Scenario: Image stream timeout is longer than ordinary stream timeout
|
||||
- **WHEN** the image streaming timeout defaults are compared with the ordinary streaming defaults
|
||||
- **THEN** the image streaming timeout is configured to allow a longer wait window than ordinary text streaming
|
||||
|
||||
### Requirement: Image stream billing consistency
|
||||
The system SHALL keep the image billing result consistent even when image stream handling uses retries, keepalive writes, or downstream disconnect recovery.
|
||||
|
||||
#### Scenario: Final image count is preserved after reconnect-unsafe downstream failure
|
||||
- **WHEN** the downstream client disconnects after at least one final image output has been observed upstream
|
||||
- **THEN** the forward result retains the final image count
|
||||
- **AND** usage recording can still proceed with image billing metadata
|
||||
|
||||
#### Scenario: Image stream timeout does not silently switch billing mode
|
||||
- **WHEN** an image stream times out before any final image output is observed
|
||||
- **THEN** the request is handled as a failed image stream
|
||||
- **AND** it does not fall back to ordinary text billing semantics
|
||||
@@ -0,0 +1,20 @@
|
||||
## 1. Config and defaults
|
||||
|
||||
- [x] 1.1 Add image-specific stream timeout fields to gateway config.
|
||||
- [x] 1.2 Register image stream timeout defaults in the config loader.
|
||||
- [x] 1.3 Add config validation for image stream timeout ranges.
|
||||
- [x] 1.4 Expose image stream timeout defaults in `deploy/config.example.yaml`.
|
||||
|
||||
## 2. Image stream runtime behavior
|
||||
|
||||
- [x] 2.1 Detach image stream upstream contexts from client cancellation.
|
||||
- [x] 2.2 Add image-specific data interval timeout handling to `/v1/images/*` streaming.
|
||||
- [x] 2.3 Add image-specific data interval timeout handling to `Responses + image_generation` streaming.
|
||||
- [x] 2.4 Preserve upstream draining after downstream write failures in both image stream paths.
|
||||
|
||||
## 3. Tests and verification
|
||||
|
||||
- [x] 3.1 Add config tests for image stream timeout defaults and validation.
|
||||
- [x] 3.2 Add image streaming disconnect tests for the Images API path.
|
||||
- [x] 3.3 Add image streaming disconnect tests for the Responses image tool path.
|
||||
- [x] 3.4 Run focused Go tests for the touched config and image service paths.
|
||||
Reference in New Issue
Block a user