diff options
| author | Adam Malczewski <[email protected]> | 2026-06-27 04:18:59 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-27 04:18:59 +0900 |
| commit | 3566a20ebbded754070fce66af48690d1a904879 (patch) | |
| tree | ad4941abdbe685e2d4ae85d9ad3d0b6e9cf82c2d /src/features/chat/model-select.test.ts | |
| parent | 2fa03f8d7410c2b8d6be8e10ad088863e83d7177 (diff) | |
| download | dispatch-web-3566a20ebbded754070fce66af48690d1a904879.tar.gz dispatch-web-3566a20ebbded754070fce66af48690d1a904879.zip | |
feat(vision): image paste + transcript image rendering + vision badge
Vision & vision-handoff frontend (consumes the backend's additive
[email protected] / [email protected] image types — no version bump).
Contracts mirrored:
- .dispatch/wire.reference.md: ImageChunk added to the Chunk union +
ImageChunk/ImageInput interfaces.
- .dispatch/transport-contract.reference.md: ChatRequest.images,
ModelMetadata.vision, + ImageChunk/ImageInput re-exports.
Core (core/chunks):
- conformance: assertChunkExhaustive handles the new 'image' variant
(the guard caught it — its purpose).
- appendUserMessage(state, text, images?) echoes a [text, image, ...]
user run; the user-message event dedup scans the trailing user run
(not just the last chunk) so an image-bearing echo doesn't duplicate
the text; applyHistory's during-gen dedup matches a multi-chunk echo
by content equality (chunkContentEquals + trailingRun helpers).
UI:
- ChatView renders user 'image' chunks as lazy <img> bubbles; a
non-vision model's persisted [image, analysis-text] both render.
read_image tool renders generically (no special-casing).
- Composer: clipboard paste / file picker / drag-drop of images ->
base64 data URLs, thumbnail previews with remove, forwarded on
chat.send (omitted when none). Image-only sends allowed; steering
(chat.queue) never forwards images.
- ModelSelector: vision badge (isVisionModel) marks vision-capable
models; indicator shows native-vision vs vision-handoff hint.
Store wiring: ChatStore.send + AppStore.send + App.svelte handleSend
thread images through; chat.send still omits cwd (only images added).
Verification: svelte-check 0/0; vitest 901/901 (run twice, +34 new);
biome clean; vite build OK. See backend-handoff.md §2j.
Not merged or pushed.
Diffstat (limited to 'src/features/chat/model-select.test.ts')
| -rw-r--r-- | src/features/chat/model-select.test.ts | 33 |
1 files changed, 32 insertions, 1 deletions
diff --git a/src/features/chat/model-select.test.ts b/src/features/chat/model-select.test.ts index deb673d..6d3081d 100644 --- a/src/features/chat/model-select.test.ts +++ b/src/features/chat/model-select.test.ts @@ -1,5 +1,11 @@ import { describe, expect, it } from "vitest"; -import { joinModelName, modelKeys, modelsForKey, splitModelName } from "./model-select"; +import { + isVisionModel, + joinModelName, + modelKeys, + modelsForKey, + splitModelName, +} from "./model-select"; describe("splitModelName", () => { it("splits on the first slash", () => { @@ -56,3 +62,28 @@ describe("modelsForKey", () => { expect(modelsForKey(["openai/gpt-4"], "anthropic")).toEqual([]); }); }); + +describe("isVisionModel", () => { + it("returns true when modelInfo[name].vision is true", () => { + const info = { "kimi/k2": { vision: true } }; + expect(isVisionModel(info, "kimi/k2")).toBe(true); + }); + + it("returns false when vision is false", () => { + const info = { "umans/glm-5.2": { vision: false } }; + expect(isVisionModel(info, "umans/glm-5.2")).toBe(false); + }); + + it("returns false when vision is absent (unknown)", () => { + const info = { "umans/glm-5.2": { contextWindow: 128000 } }; + expect(isVisionModel(info, "umans/glm-5.2")).toBe(false); + }); + + it("returns false for a model with no metadata entry at all", () => { + expect(isVisionModel({}, "unknown/model")).toBe(false); + }); + + it("returns false for an empty modelInfo map", () => { + expect(isVisionModel({}, "kimi/k2")).toBe(false); + }); +}); |
