Update image tests for split adapters and durable offload failures

This commit is contained in:
creatixchu committed 2026-09-14 10:21:54 +08:00
1 parent a7070008e7
commit 27d52a4edb
4 files changed
+23 -16

No files matched your search

@@ -10,7 +10,7 @@ import type { Agent } from '@deepseek-ai/dsh-agent'
import BasicCompactionEngine from '@deepseek-ai/dsh-compaction-basic'
import TokenMeter from '@deepseek-ai/dsh-token-meter'
import { ImageVariantId } from '@deepseek-ai/dsh-attachment'
import { serializeRequestWithImages } from '@deepseek-ai/dsh-llm-deepseek/src/serialize.ts'
import { serializeRequestWithImages } from '@deepseek-ai/dsh-llm-deepseek/src/protocols/chat-completions/serialize.ts'
import { mountAgentLoopTestDependencies } from '@deepseek-ai/dsh-agent-loop-testkit'
import { createAssistantMessage, createToolResultMessage, createUserMessage, IMAGE_OFFLOAD_REQUIRED_CODE, LlmAdapter, LlmError, ToolCallId } from '@deepseek-ai/dsh-llm'
import type { ContentBlock, GenerateOptions, StreamChunk } from '@deepseek-ai/dsh-llm'
@@ -173,10 +173,10 @@ describe('Cordis provider composition', () => {
const model = 'deepseek-v4-flash-vision-exp'
const price = () => ctx.llm.imageRequestPricing('deepseek-official', model)!
const dummy = { attachmentId: AttachmentId(`sha256:${'a'.repeat(64)}`), width: 1, height: 1, bytes: 3, mediaType: 'image/png' as const }
expect(price().priceImages([dummy])[0]?.text).toBeDefined()
expect(price().priceImages([{ type: 'image', attachment: dummy }])[0]?.text).toBeDefined()
await ctx.plugin(LocalAttachments, { dshHome: home })
const attachment = await ctx.attachments.saveImage({ data: await readFile(new URL('fixtures/red.png', import.meta.url)), mediaType: 'image/png' })
expect(price().priceImages([attachment])[0]?.text).not.toContain('/mounted/image.png')
expect(price().priceImages([{ type: 'image', attachment }])[0]?.text).not.toContain('/mounted/image.png')
class MappedFiles extends Service {
constructor(context: Context) { super(context, 'fs') }
processPathFromHostPath(_path: string) { return '/mounted/image.png' }
@@ -185,7 +185,7 @@ describe('Cordis provider composition', () => {
const message = user()
await chunks(ctx.llm.stream(options({ model, messages: [{ ...message, content: [...message.content, { type: 'image', attachment }] }] })))
expect(JSON.stringify(http.requests[0]?.body)).toContain('/mounted/image.png')
expect(price().priceImages([attachment])[0]?.text).toContain('/mounted/image.png')
expect(price().priceImages([{ type: 'image', attachment }])[0]?.text).toContain('/mounted/image.png')
})
async function boot(...args: Parameters<typeof server>) {
@@ -123,7 +123,7 @@ describe('Messages Files requests', () => {
await chunks(h.adapter.stream(original))
expect(body(fetchImpl.mock.calls[0]?.[1]).match(/"file_id"/gu)).toHaveLength(2)
h.ensureUploaded.mockRejectedValueOnce(new Error('offline'))
await expect(chunks(h.adapter.stream(original))).rejects.toMatchObject({ code: 'IMAGE_OFFLOAD_REQUIRED', offloadImages: 1 })
await expect(chunks(h.adapter.stream(original))).rejects.toMatchObject({ failure: { code: 'IMAGE_OFFLOAD_REQUIRED', offloadImages: 1 } })
expect(fetchImpl).toHaveBeenCalledTimes(1)
h.ensureUploaded.mockRejectedValueOnce(new Error('offline'))
await chunks(h.adapter.stream(options({ model, messages: [{ ...user(), content: [
@@ -138,7 +138,7 @@ describe('Messages Files requests', () => {
it('reads and prices only the retained occurrences selected by the logged offload', async () => {
vi.stubGlobal('fetch', async () => success())
const h = harness({ maxRequestFilesBytes: 3, imageOffloadByteQuantum: 1, maxImagesPerRequest: 2, imageOffloadCountQuantum: 1 })
const images = [{ type: 'image' as const, attachment: ref, offloaded: true }, { type: 'image' as const, attachment: second }]
const images = [{ type: 'image' as const, attachment: ref, offloaded: true as const }, { type: 'image' as const, attachment: second }]
await chunks(h.adapter.stream(options({ model, messages: [{ ...user(), content: images }] })))
expect(h.readImageRequest).toHaveBeenCalledExactlyOnceWith(second, expect.anything(), expect.any(AbortSignal))
expect(h.adapter.imageRequestPricing('deepseek-official', model).priceImages(images).map(image => image.visualTokens)).toEqual([0, expect.any(Number)])
@@ -153,7 +153,7 @@ describe('Messages Files requests', () => {
const h = harness(config)
const original = request([ref, ref])
const saved = JSON.stringify(original.messages)
await expect(chunks(h.adapter.stream(original))).rejects.toMatchObject({ code: 'IMAGE_OFFLOAD_REQUIRED', offloadImages: 1 })
await expect(chunks(h.adapter.stream(original))).rejects.toMatchObject({ failure: { code: 'IMAGE_OFFLOAD_REQUIRED', offloadImages: 1 } })
expect(h.readImageRequest).toHaveBeenCalledTimes(1)
expect(h.ensureUploaded).not.toHaveBeenCalled()
expect(fetchImpl).not.toHaveBeenCalled()
@@ -163,7 +163,7 @@ describe('Messages Files requests', () => {
it('uses exact prepared bytes when the durable reference fits the Files budget', async () => {
const h = harness({ maxRequestFilesBytes: 3, imageOffloadByteQuantum: 1, imageOffloadCountQuantum: 1 })
h.readImageRequest.mockResolvedValue({ ...version(ref), bytes: 4, data: Uint8Array.of(1, 2, 3, 4) })
await expect(chunks(h.adapter.stream(request()))).rejects.toMatchObject({ code: 'IMAGE_OFFLOAD_REQUIRED', offloadImages: 1 })
await expect(chunks(h.adapter.stream(request()))).rejects.toMatchObject({ failure: { code: 'IMAGE_OFFLOAD_REQUIRED', offloadImages: 1 } })
expect(h.ensureUploaded).not.toHaveBeenCalled()
})
@@ -1,7 +1,7 @@
/** Request conversion and durable replay validation. */
import { describe, expect, it, vi } from 'vitest'
import { createAssistantMessage, createMessage, createSystemMessage, createToolResultMessage, ReasoningEffortId, ToolCallId } from '@deepseek-ai/dsh-llm'
import type { ContentBlock, GenerateOptions, Message } from '@deepseek-ai/dsh-llm'
import type { ContentBlock, GenerateOptions, ImageBlock, Message } from '@deepseek-ai/dsh-llm'
import { AttachmentId, ImageVariantId } from '@deepseek-ai/dsh-attachment'
import type { AttachmentStore, ImageAttachmentRef, RequestImageAttachment } from '@deepseek-ai/dsh-attachment'
import { resolveAdapterOptions } from '../../src/config.ts'
@@ -248,7 +248,7 @@ describe('validated configuration', () => {
describe('Messages images', () => {
const ref: ImageAttachmentRef = { attachmentId: AttachmentId(`sha256:${'a'.repeat(64)}`), mediaType: 'image/png', width: 1, height: 1, bytes: 3 }
const image: ContentBlock = { type: 'image', attachment: ref }
const image: ImageBlock = { type: 'image', attachment: ref }
const version: RequestImageAttachment = { attachment: ref, variantId: ImageVariantId(`sha256:${'b'.repeat(64)}`), mediaType: 'image/png', bytes: 3, data: Uint8Array.of(1, 2, 3), width: 1, height: 1, depth: 'uchar', space: 'srgb', hasAlpha: false }
const access = () => ({ readonlyPath: '/workspace/image.png' })
const model = 'deepseek-v4-flash-vision-exp'
@@ -264,23 +264,30 @@ describe('Messages images', () => {
{ type: 'text', text: expect.stringContaining('/workspace/image.png') as string }, { type: 'image', source: { type: 'base64', media_type: 'image/png', data: 'AQID' } },
{ type: 'text' }, { type: 'image' },
] })
expect(imagePricing(connection, model, access).priceImages([ref])[0]?.visualTokens).toBeGreaterThan(0)
expect(imagePricing(connection, MODEL, access).priceImages([ref])[0]?.visualTokens).toBe(0)
expect(imagePricing(connection, model, access).priceImages([image])[0]?.visualTokens).toBeGreaterThan(0)
expect(imagePricing(connection, MODEL, access).priceImages([image])[0]?.visualTokens).toBe(0)
})
it('offloads an oldest prefix using exact encoded bytes and preserves durable references', async () => {
it('requires logged offload at exact encoded bytes and preserves durable references', async () => {
const config = resolveAdapterOptions({
maxInlineRequestImageBytes: 4, inlineImageOffloadByteQuantum: 1, maxImagesPerRequest: 2, imageOffloadCountQuantum: 1,
})
const history = [result('a', [image, image])]
const prepared = await prepareImages(history, config, model, attachments, access, signal)
expect(prepared.messages[0]?.content[0]).toMatchObject({ content: [image, image] })
expect(inlineImages(prepared.messages, prepared.versions, config, access)[0]?.content[0]).toMatchObject({ content: [{ type: 'text' }, { type: 'image' }] })
expect(() => inlineImages(prepared.messages, prepared.versions, config)).toThrow(expect.objectContaining({
failure: expect.objectContaining({ code: 'IMAGE_OFFLOAD_REQUIRED', offloadImages: 1 }) as unknown,
}))
const offloaded: ImageBlock = { ...image, offloaded: true }
const retry = await prepareImages([result('a', [offloaded, image])], config, model, attachments, access, signal)
expect(inlineImages(retry.messages, retry.versions, config)[0]?.content[0]).toMatchObject({ content: [{ type: 'text' }, { type: 'image' }] })
expect(history[0]?.content[0]).toMatchObject({ content: [image, image] })
expect(imagePricing(config, model, access).priceImages([ref, ref]).map(entry => entry.visualTokens))
expect(imagePricing(config, model, access).priceImages([image, image]).map(entry => entry.visualTokens))
.toEqual([expect.any(Number), expect.any(Number)])
const large = { readImageRequest: async () => ({ ...version, bytes: 30, data: new Uint8Array(30) }) } as unknown as AttachmentStore
const exact = await prepareImages([result('a', [image])], config, model, large, access, signal)
expect(inlineImages(exact.messages, exact.versions, config, access)[0]?.content[0]).toMatchObject({ content: [{ type: 'text' }] })
expect(() => inlineImages(exact.messages, exact.versions, config)).toThrow(expect.objectContaining({
failure: expect.objectContaining({ code: 'IMAGE_OFFLOAD_REQUIRED', offloadImages: 1 }) as unknown,
}))
})
it('rejects unsupported roles and unavailable image capabilities before HTTP', async () => {
const history = [result('a', [image])]