Files
Praveen K Palaniswamy 65e81158ab fix(ollama): route models by advertised capability (#11088)
Landed with the design call resolved per the owner's pick — **option 1**: the synced store is now endpoint-agnostic (persistDiscoveredModels and managedModelImport no longer drop non-chat models at write time), and chat selectability moved to read time (auto-pool expansion in autoStrategy applies filterChatSelectableModels; the models-route projection already had its chatOnly filter). Your discovery test now passes end-to-end (3/3): /api/show capabilities persist per connection and image/embedding requests route through the advertising host.

Reconciliation notes: conflicted areas merged onto the current tip (adobe discovery import, requestedModel preflight signature, resolvedProvider fast-path coexists with the synced-route override — explicit resolution wins); carried base-red drains (#10055 memoization, #11071 test variants) dropped as already-landed; the managed-model-import exclusion test was propagated to the new contract (image/video models persist; the read filter still hides them from chat pickers — pinned by a new assertion). Full battery: 205/206 focused (the one red is a confirmed periodic-timer timing flake on the loaded devbox — 20/20 isolated), autoCombo vitest 30/30, combo suites 46/46, gates + typecheck clean.

Thank you @yourspraveen — the capability probe + routing design was right; it just needed the store contract opened up. Fixes #11087.
2026-08-23 11:45:01 -03:00

81 lines
1.9 KiB
TypeScript

import crypto from "node:crypto";
import {
encodeBytes,
encodeMessage,
encodeString,
encodeUInt32Field,
} from "./wire.ts";
const SI_UUID = 2;
const SI_PATH = 3;
const SI_DIMENSION = 4;
const SI_MIME_TYPE = 7;
const SI_BLOB_ID_WITH_DATA = 9;
const SIBD_BLOB_ID = 1;
const SIBD_DATA = 2;
const DIM_WIDTH = 1;
const DIM_HEIGHT = 2;
export type EncodedImage = {
data: Buffer;
mimeType?: string;
width?: number;
height?: number;
uuid: string;
};
export function cursorImageAttachmentPath(uuid: string, mimeType?: string): string {
const normalized = (mimeType || "").toLowerCase();
const ext =
normalized === "image/jpeg" || normalized === "image/jpg"
? "jpg"
: normalized === "image/gif"
? "gif"
: normalized === "image/webp"
? "webp"
: "png";
return `attachment-${uuid}.${ext}`;
}
export function encodeSelectedImageBody(
img: EncodedImage,
blobStore?: Map<string, Buffer>
): Buffer {
const blobId = crypto.createHash("sha256").update(img.data).digest();
if (blobStore) {
blobStore.set(blobId.toString("hex"), img.data);
}
const parts: Buffer[] = [
encodeString(SI_UUID, img.uuid),
encodeString(SI_PATH, cursorImageAttachmentPath(img.uuid, img.mimeType)),
];
if (
typeof img.width === "number" &&
typeof img.height === "number" &&
Number.isFinite(img.width) &&
Number.isFinite(img.height) &&
img.width > 0 &&
img.height > 0
) {
parts.push(
encodeMessage(SI_DIMENSION, [
encodeUInt32Field(DIM_WIDTH, Math.floor(img.width)),
encodeUInt32Field(DIM_HEIGHT, Math.floor(img.height)),
])
);
}
if (img.mimeType) {
parts.push(encodeString(SI_MIME_TYPE, img.mimeType));
}
parts.push(
encodeMessage(SI_BLOB_ID_WITH_DATA, [
encodeBytes(SIBD_BLOB_ID, blobId),
encodeBytes(SIBD_DATA, img.data),
])
);
return Buffer.concat(parts);
}