Image requests previously sent only the current prompt and attachments, so follow-up edits could lose the previous image and conversation requirements. Reuse the latest successful image from the existing artifact store together with text history, including after reopening the app. Explicit attachments take priority, and configured model IDs remain unchanged. When image editing is explicitly unsupported, continue with text-based image generation and retain a quiet message footer explaining the missing image context. Keep genuine provider errors and cancellation visible. Add focused regressions, persisted footer metadata, and aligned feature documentation. Validated with four real sub-gpt-image-2 image requests and a simulated unsupported-edit response. Image regressions, typecheck and lint passed; the full working-tree run had two unrelated concurrent Runtime-menu failures. Release note: 连续改图会自动带上上一张图片和文字上下文,重开对话后仍可继续修改。上游明确不支持编辑时仍可按文字生图,仅在回复底部轻提示未使用参考图片。
57 lines
1.7 KiB
TypeScript
57 lines
1.7 KiB
TypeScript
import { nativeImage } from 'electron'
|
|
import type { AssistantArtifact } from '../../shared/assistant-contracts'
|
|
import type { AgentExecutionRequest, AgentImage } from './runtime'
|
|
|
|
export function withImageConversationContext(
|
|
request: AgentExecutionRequest,
|
|
getArtifact: (id: string) => AssistantArtifact
|
|
): AgentExecutionRequest {
|
|
if (request.images?.length || !request.imageContextArtifactIds?.length) {
|
|
return request
|
|
}
|
|
const images: AgentImage[] = []
|
|
let missing = false
|
|
for (const id of request.imageContextArtifactIds) {
|
|
let artifact: AssistantArtifact
|
|
try {
|
|
artifact = getArtifact(id)
|
|
} catch (error) {
|
|
if (!(error instanceof Error) || error.message !== '成果不存在') {
|
|
throw error
|
|
}
|
|
missing = true
|
|
continue
|
|
}
|
|
const match = artifact.kind === 'image'
|
|
? artifact.content?.match(/^data:(image\/(?:png|jpeg|webp));base64,([\s\S]+)$/u)
|
|
: undefined
|
|
if (!match) {
|
|
missing = true
|
|
continue
|
|
}
|
|
if (match[1] === 'image/webp') {
|
|
const image = nativeImage.createFromBuffer(Buffer.from(match[2]!, 'base64'))
|
|
if (image.isEmpty()) {
|
|
missing = true
|
|
continue
|
|
}
|
|
images.push({
|
|
name: `${artifact.id}.png`,
|
|
mediaType: 'image/png',
|
|
data: image.toPNG().toString('base64')
|
|
})
|
|
} else {
|
|
images.push({
|
|
name: `${artifact.id}.${match[1] === 'image/png' ? 'png' : 'jpg'}`,
|
|
mediaType: match[1] as AgentImage['mediaType'],
|
|
data: match[2]!
|
|
})
|
|
}
|
|
}
|
|
return {
|
|
...request,
|
|
images: images.length ? images : undefined,
|
|
...(missing ? { imageContextNotice: 'reference-unavailable' as const } : {})
|
|
}
|
|
}
|