mirror of
https://github.com/HeyPuter/puter.git
synced 2026-10-20 10:32:36 +00:00
Advertise supported image input for ZAI Flash models
This commit is contained in:
1 parent
8c64f54a87
commit
16d0ce8b3e
2 files changed
+87
-1
No files matched your search
@@ -51,6 +51,10 @@ import { AIChatStream } from '../../utils/Streaming.js';
|
||||
import { ZAI_MODELS } from './models.js';
|
||||
import { ZAIProvider } from './ZAIProvider.js';
|
||||
import { modelLookupNames } from '../../utils/modelRouting.js';
|
||||
import {
|
||||
modelSupportsModality,
|
||||
requiredInputModalities,
|
||||
} from '../../utils/mediaParts.js';
|
||||
|
||||
// ── OpenAI SDK mock ─────────────────────────────────────────────────
|
||||
//
|
||||
@@ -211,6 +215,47 @@ describe('ZAIProvider model catalog', () => {
|
||||
cached_tokens: 7.5,
|
||||
});
|
||||
});
|
||||
|
||||
it('advertises image input, and no video or file input, only on the GLM-5.3 flash models', async () => {
|
||||
const { provider } = makeProvider();
|
||||
const models = await provider.models();
|
||||
|
||||
const imageIds = models
|
||||
.filter((m) => modelSupportsModality(m, 'image'))
|
||||
.map((m) => m.id);
|
||||
expect(imageIds).toEqual(['glm-5.3-flash', 'glm-5.3-flashx']);
|
||||
for (const m of models) {
|
||||
expect(m.modalities?.output).toEqual(['text']);
|
||||
expect(modelSupportsModality(m, 'video')).toBe(false);
|
||||
expect(modelSupportsModality(m, 'file')).toBe(false);
|
||||
}
|
||||
});
|
||||
|
||||
it('passes the driver input gate for a puter.js image message on the flash models only', async () => {
|
||||
const { provider } = makeProvider();
|
||||
const models = await provider.models();
|
||||
// The shape `puter.ai.chat(prompt, imageURL)` sends.
|
||||
const required = requiredInputModalities([
|
||||
{
|
||||
content: [
|
||||
'what is this?',
|
||||
{ image_url: { url: 'https://cdn.test/cat.png' } },
|
||||
],
|
||||
},
|
||||
]);
|
||||
expect(required).toEqual(['image']);
|
||||
|
||||
const accepts = (id: string) =>
|
||||
required.every((modality) =>
|
||||
modelSupportsModality(
|
||||
models.find((m) => m.id === id)!,
|
||||
modality,
|
||||
),
|
||||
);
|
||||
expect(accepts('glm-5.3-flash')).toBe(true);
|
||||
expect(accepts('glm-5.3-flashx')).toBe(true);
|
||||
expect(accepts('glm-5.3')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
// ── Request shape (OpenAI-compat quirks specific to GLM) ────────────
|
||||
@@ -348,6 +393,40 @@ describe('ZAIProvider.complete request shape', () => {
|
||||
expect(args.response_format).toEqual({ type: 'json_object' });
|
||||
});
|
||||
|
||||
it.each(['glm-5.3-flash', 'zai/glm-5.3-flashx'])(
|
||||
'forwards image_url parts for %s unchanged, by URL and by data URL',
|
||||
async (requested) => {
|
||||
const { provider } = makeProvider();
|
||||
createMock.mockResolvedValueOnce(baseCompletion);
|
||||
const content = [
|
||||
{ type: 'text', text: 'compare these' },
|
||||
{
|
||||
type: 'image_url',
|
||||
image_url: { url: 'https://cdn.test/cat.png' },
|
||||
},
|
||||
{
|
||||
type: 'image_url',
|
||||
image_url: { url: 'data:image/png;base64,iVBORw0KGgo=' },
|
||||
},
|
||||
];
|
||||
|
||||
await withTestActor(() =>
|
||||
provider.complete({
|
||||
model: requested,
|
||||
messages: [
|
||||
{ role: 'user', content: structuredClone(content) },
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
const [args] = createMock.mock.calls[0]!;
|
||||
expect(args.model).toBe(requested.replace(/^zai\//, ''));
|
||||
// Z.AI reads http(s) and data URLs itself, so neither is fetched or
|
||||
// rewritten on the way through.
|
||||
expect(args.messages).toEqual([{ role: 'user', content }]);
|
||||
},
|
||||
);
|
||||
|
||||
it('strips Anthropic-style cache_control from messages before sending', async () => {
|
||||
const { provider } = makeProvider();
|
||||
createMock.mockResolvedValueOnce(baseCompletion);
|
||||
|
||||
@@ -22,18 +22,23 @@ import { usdPerMToken } from '../../utils/pricing.js';
|
||||
|
||||
const K = 1_000;
|
||||
|
||||
// The flash models also read video and files upstream; only images are
|
||||
// advertised, as the one input with a verified wire shape we forward as-is.
|
||||
const IMAGE_INPUT = ['text', 'image'];
|
||||
|
||||
const textModel = (
|
||||
id: string,
|
||||
name: string,
|
||||
context: number,
|
||||
maxTokens: number,
|
||||
costs: IChatModel['costs'],
|
||||
input: string[] = ['text'],
|
||||
): IChatModel => ({
|
||||
puterId: `z-ai:z-ai/${id}`,
|
||||
id,
|
||||
name,
|
||||
aliases: [`z-ai/${id}`, `zai/${id}`],
|
||||
modalities: { input: ['text'], output: ['text'] },
|
||||
modalities: { input, output: ['text'] },
|
||||
open_weights: false,
|
||||
tool_call: true,
|
||||
context,
|
||||
@@ -62,6 +67,7 @@ export const ZAI_MODELS: IChatModel[] = [
|
||||
1_000 * K,
|
||||
128 * K,
|
||||
usdPerMToken(0.15, 0.5, 0.03),
|
||||
IMAGE_INPUT,
|
||||
),
|
||||
textModel(
|
||||
'glm-5.3-flashx',
|
||||
@@ -69,6 +75,7 @@ export const ZAI_MODELS: IChatModel[] = [
|
||||
1_000 * K,
|
||||
128 * K,
|
||||
usdPerMToken(0.37, 1.25, 0.075),
|
||||
IMAGE_INPUT,
|
||||
),
|
||||
textModel(
|
||||
'glm-5.2',
|
||||
|
||||
Reference in new issue
Block a user