@capybara-hq/plugin-model-fake 0.1.0-rc.2 → 0.1.0-rc.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.capybara-plugin/plugin.json +63 -3
- package/dist/index.mjs +130 -34
- package/package.json +1 -1
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"name": "model-fake",
|
|
4
|
-
"version": "0.1.0-rc.
|
|
4
|
+
"version": "0.1.0-rc.4",
|
|
5
5
|
"publisher": "Capybara",
|
|
6
|
-
"description":
|
|
6
|
+
"description": {
|
|
7
|
+
"en": "Deterministic scripted model provider (model-provider@2) for runtime tests, e2e tests and keyless demos",
|
|
8
|
+
"zh": "确定性的脚本化模型提供方(model-provider@2),用于运行时测试、端到端测试和无密钥演示"
|
|
9
|
+
},
|
|
7
10
|
"displayName": {
|
|
8
11
|
"zh": "模拟模型",
|
|
9
12
|
"en": "Fake model"
|
|
@@ -13,7 +16,7 @@
|
|
|
13
16
|
"entry": "./dist/index.mjs",
|
|
14
17
|
"manifest": {
|
|
15
18
|
"name": "model-fake",
|
|
16
|
-
"version": "0.1.0-rc.
|
|
19
|
+
"version": "0.1.0-rc.4",
|
|
17
20
|
"sdk": "^0.1.0-rc.0",
|
|
18
21
|
"provides": [
|
|
19
22
|
"model-provider@2"
|
|
@@ -95,12 +98,69 @@
|
|
|
95
98
|
}
|
|
96
99
|
},
|
|
97
100
|
"title": "Document input"
|
|
101
|
+
},
|
|
102
|
+
"kind": {
|
|
103
|
+
"type": "string",
|
|
104
|
+
"enum": [
|
|
105
|
+
"chat",
|
|
106
|
+
"embedding",
|
|
107
|
+
"rerank",
|
|
108
|
+
"image_generation",
|
|
109
|
+
"video_generation",
|
|
110
|
+
"speech_to_text",
|
|
111
|
+
"text_to_speech"
|
|
112
|
+
],
|
|
113
|
+
"x-ui": {
|
|
114
|
+
"label": {
|
|
115
|
+
"zh": "类型",
|
|
116
|
+
"en": "Kind"
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
"title": "Kind"
|
|
120
|
+
},
|
|
121
|
+
"publisher": {
|
|
122
|
+
"type": "string",
|
|
123
|
+
"x-ui": {
|
|
124
|
+
"label": {
|
|
125
|
+
"zh": "发布者",
|
|
126
|
+
"en": "Publisher"
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
"title": "Publisher"
|
|
130
|
+
},
|
|
131
|
+
"dimensions": {
|
|
132
|
+
"type": "array",
|
|
133
|
+
"items": {
|
|
134
|
+
"type": "integer",
|
|
135
|
+
"exclusiveMinimum": 0,
|
|
136
|
+
"maximum": 9007199254740991
|
|
137
|
+
},
|
|
138
|
+
"x-ui": {
|
|
139
|
+
"label": {
|
|
140
|
+
"zh": "向量维度",
|
|
141
|
+
"en": "Embedding dimensions"
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
"title": "Embedding dimensions"
|
|
145
|
+
},
|
|
146
|
+
"maxBatch": {
|
|
147
|
+
"type": "integer",
|
|
148
|
+
"exclusiveMinimum": 0,
|
|
149
|
+
"maximum": 9007199254740991,
|
|
150
|
+
"x-ui": {
|
|
151
|
+
"label": {
|
|
152
|
+
"zh": "单次最大输入数",
|
|
153
|
+
"en": "Max inputs per call"
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
"title": "Max inputs per call"
|
|
98
157
|
}
|
|
99
158
|
},
|
|
100
159
|
"required": [
|
|
101
160
|
"id"
|
|
102
161
|
]
|
|
103
162
|
},
|
|
163
|
+
"x-org": "override",
|
|
104
164
|
"x-ui": {
|
|
105
165
|
"label": {
|
|
106
166
|
"zh": "模型",
|
package/dist/index.mjs
CHANGED
|
@@ -8,7 +8,9 @@ const __capybara_dirname = __capybaraPathDirname(__capybara_filename);
|
|
|
8
8
|
// src/index.ts
|
|
9
9
|
import {
|
|
10
10
|
defineExtension,
|
|
11
|
-
MODEL_PROVIDER_EXTENSION_POINT_V2
|
|
11
|
+
MODEL_PROVIDER_EXTENSION_POINT_V2,
|
|
12
|
+
ModelError as ModelError2,
|
|
13
|
+
orgAccess
|
|
12
14
|
} from "@capybara-hq/plugin-sdk";
|
|
13
15
|
import { z } from "zod";
|
|
14
16
|
|
|
@@ -208,25 +210,68 @@ function scripted(req) {
|
|
|
208
210
|
if (first !== void 0) return { kind: "tool", name: first.name, input: {} };
|
|
209
211
|
return std(`You said: ${text}`);
|
|
210
212
|
}
|
|
213
|
+
function embedVector(text, dim) {
|
|
214
|
+
const out = [];
|
|
215
|
+
for (let block = 0; out.length < dim; block++) {
|
|
216
|
+
const h = createHash("sha256").update(`${block}:${text}`).digest();
|
|
217
|
+
for (let i = 0; i + 1 < h.length && out.length < dim; i += 2) {
|
|
218
|
+
out.push(h.readUInt16BE(i) / 65535 * 2 - 1);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return out;
|
|
222
|
+
}
|
|
211
223
|
function createFakeProvider(opts) {
|
|
212
224
|
const sleep = opts.sleep ?? defaultSleep;
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
225
|
+
const ERROR_KINDS = [
|
|
226
|
+
"rate_limited",
|
|
227
|
+
"overloaded",
|
|
228
|
+
"server",
|
|
229
|
+
"timeout",
|
|
230
|
+
"network",
|
|
231
|
+
"auth",
|
|
232
|
+
"invalid_request",
|
|
233
|
+
"context_overflow",
|
|
234
|
+
"aborted"
|
|
235
|
+
];
|
|
236
|
+
function infosOf(models) {
|
|
237
|
+
const configured = models !== void 0 && models.length > 0 ? models : [{ id: "fake-1" }];
|
|
238
|
+
return configured.map((m) => {
|
|
239
|
+
const embedding = m.kind === "embedding";
|
|
240
|
+
const dims = m.dimensions ?? [8, 16];
|
|
241
|
+
return {
|
|
242
|
+
id: m.id,
|
|
243
|
+
displayName: m.id,
|
|
244
|
+
...m.kind !== void 0 ? { kind: m.kind } : {},
|
|
245
|
+
...m.publisher !== void 0 ? { publisher: m.publisher } : {},
|
|
246
|
+
...embedding ? {
|
|
247
|
+
embedding: {
|
|
248
|
+
dimensions: dims,
|
|
249
|
+
defaultDimensions: m.dimensions?.[0] ?? 8,
|
|
250
|
+
maxInputTokens: 512,
|
|
251
|
+
maxBatch: m.maxBatch ?? 4
|
|
252
|
+
}
|
|
253
|
+
} : {},
|
|
254
|
+
contextWindow: m.contextWindow ?? 32e3,
|
|
255
|
+
maxOutputTokens: 4096,
|
|
256
|
+
capabilities: {
|
|
257
|
+
tools: true,
|
|
258
|
+
vision: m.vision ?? false,
|
|
259
|
+
documents: m.documents ?? false,
|
|
260
|
+
reasoning: false,
|
|
261
|
+
caching: true,
|
|
262
|
+
midConversationSystem: true
|
|
263
|
+
},
|
|
264
|
+
...m.streamIdleTimeoutMs !== void 0 ? { streamIdleTimeoutMs: m.streamIdleTimeoutMs } : {},
|
|
265
|
+
parameters: parameters()
|
|
266
|
+
};
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
const setupInfos = infosOf(opts.models);
|
|
270
|
+
function infosFor(call) {
|
|
271
|
+
if (call?.config === void 0 || opts.modelsOf === void 0) return setupInfos;
|
|
272
|
+
const models = opts.modelsOf(call.config);
|
|
273
|
+
return models === void 0 ? setupInfos : infosOf(models);
|
|
274
|
+
}
|
|
230
275
|
async function cacheUsage(req) {
|
|
231
276
|
const cached = req.system.filter((s) => s.cache).map((s) => s.text);
|
|
232
277
|
const prefix = cached.join("\n");
|
|
@@ -240,11 +285,34 @@ function createFakeProvider(opts) {
|
|
|
240
285
|
return { read: 0, write: tokens(cachedChars), cachedChars };
|
|
241
286
|
}
|
|
242
287
|
return {
|
|
243
|
-
async listModels() {
|
|
244
|
-
return
|
|
288
|
+
async listModels(call) {
|
|
289
|
+
return infosFor(call).map((i) => structuredClone(i));
|
|
290
|
+
},
|
|
291
|
+
async embed(req, call) {
|
|
292
|
+
if (call.signal.aborted) throw abortError();
|
|
293
|
+
const info = infosFor(call).find((i) => i.id === req.model && i.kind === "embedding");
|
|
294
|
+
if (info?.embedding === void 0) {
|
|
295
|
+
throw new ModelError("invalid_request", `Unknown embedding model "${req.model}"`);
|
|
296
|
+
}
|
|
297
|
+
const failure = /^fail:(\w+)/.exec(req.input[0] ?? "");
|
|
298
|
+
if (failure && ERROR_KINDS.includes(failure[1])) {
|
|
299
|
+
throw new ModelError(failure[1], `scripted failure: ${failure[1]}`);
|
|
300
|
+
}
|
|
301
|
+
const dim = req.dimensions ?? info.embedding.defaultDimensions;
|
|
302
|
+
if (!info.embedding.dimensions.includes(dim)) {
|
|
303
|
+
throw new ModelError("invalid_request", `Unsupported dimensions ${dim}`);
|
|
304
|
+
}
|
|
305
|
+
if (req.input.length > info.embedding.maxBatch) {
|
|
306
|
+
throw new ModelError("invalid_request", "Too many inputs in one embed call");
|
|
307
|
+
}
|
|
308
|
+
return {
|
|
309
|
+
vectors: req.input.map((t) => embedVector(t, dim)),
|
|
310
|
+
usage: { inputTokens: req.input.reduce((n, t) => n + Math.ceil(t.length / 4), 0) }
|
|
311
|
+
};
|
|
245
312
|
},
|
|
246
|
-
async *stream(req,
|
|
247
|
-
|
|
313
|
+
async *stream(req, call) {
|
|
314
|
+
const { signal } = call;
|
|
315
|
+
if (!infosFor(call).some((i) => i.id === req.model && (i.kind ?? "chat") === "chat")) {
|
|
248
316
|
throw new ModelError("invalid_request", `Unknown model "${req.model}"`);
|
|
249
317
|
}
|
|
250
318
|
if (signal.aborted) throw abortError();
|
|
@@ -307,20 +375,35 @@ function createFakeProvider(opts) {
|
|
|
307
375
|
// src/index.ts
|
|
308
376
|
var label = (zh, en) => ({ ui: { label: { zh, en } } });
|
|
309
377
|
var DeploymentConfig = z.object({
|
|
310
|
-
models:
|
|
311
|
-
z.
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
378
|
+
models: orgAccess(
|
|
379
|
+
z.array(
|
|
380
|
+
z.object({
|
|
381
|
+
id: z.string().min(1).meta(label("模型 ID", "Model ID")),
|
|
382
|
+
contextWindow: z.number().int().positive().optional().meta(label("上下文窗口", "Context window")),
|
|
383
|
+
streamIdleTimeoutMs: z.number().int().positive().optional().meta(label("流空闲超时(毫秒)", "Stream idle timeout (ms)")),
|
|
384
|
+
/** Declares image / document input support (default false). */
|
|
385
|
+
vision: z.boolean().optional().meta(label("图片输入", "Image input")),
|
|
386
|
+
documents: z.boolean().optional().meta(label("文档输入", "Document input")),
|
|
387
|
+
kind: z.enum([
|
|
388
|
+
"chat",
|
|
389
|
+
"embedding",
|
|
390
|
+
"rerank",
|
|
391
|
+
"image_generation",
|
|
392
|
+
"video_generation",
|
|
393
|
+
"speech_to_text",
|
|
394
|
+
"text_to_speech"
|
|
395
|
+
]).optional().meta(label("类型", "Kind")),
|
|
396
|
+
publisher: z.string().optional().meta(label("发布者", "Publisher")),
|
|
397
|
+
dimensions: z.array(z.number().int().positive()).optional().meta(label("向量维度", "Embedding dimensions")),
|
|
398
|
+
maxBatch: z.number().int().positive().optional().meta(label("单次最大输入数", "Max inputs per call"))
|
|
399
|
+
})
|
|
400
|
+
).optional().meta(label("模型", "Models")),
|
|
401
|
+
"override"
|
|
402
|
+
)
|
|
320
403
|
});
|
|
321
404
|
var index_default = defineExtension({
|
|
322
405
|
name: "model-fake",
|
|
323
|
-
version: "0.1.0-rc.
|
|
406
|
+
version: "0.1.0-rc.4",
|
|
324
407
|
sdk: "^0.1.0-rc.0",
|
|
325
408
|
provides: [MODEL_PROVIDER_EXTENSION_POINT_V2],
|
|
326
409
|
config: { deployment: DeploymentConfig },
|
|
@@ -328,7 +411,20 @@ var index_default = defineExtension({
|
|
|
328
411
|
const { models } = DeploymentConfig.parse(ctx.config ?? {});
|
|
329
412
|
ctx.provide(
|
|
330
413
|
MODEL_PROVIDER_EXTENSION_POINT_V2,
|
|
331
|
-
createFakeProvider({
|
|
414
|
+
createFakeProvider({
|
|
415
|
+
kv: ctx.kv,
|
|
416
|
+
...models !== void 0 ? { models } : {},
|
|
417
|
+
modelsOf: (config) => {
|
|
418
|
+
const parsed = DeploymentConfig.safeParse(config ?? {});
|
|
419
|
+
if (!parsed.success) {
|
|
420
|
+
throw new ModelError2(
|
|
421
|
+
"invalid_request",
|
|
422
|
+
`Invalid model-fake config: ${parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")}`
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
return parsed.data.models;
|
|
426
|
+
}
|
|
427
|
+
})
|
|
332
428
|
);
|
|
333
429
|
}
|
|
334
430
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capybara-hq/plugin-model-fake",
|
|
3
|
-
"version": "0.1.0-rc.
|
|
3
|
+
"version": "0.1.0-rc.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"description": "Deterministic scripted model provider (model-provider@2) for runtime tests, e2e tests and keyless demos",
|