@orangekame3/pi-qcaleval 0.0.1 → 0.0.2-beta.3
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/README.md +9 -4
- package/extensions/qcaleval.ts +75 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -26,10 +26,15 @@ for example 2 when an expected reference figure precedes the measured result.
|
|
|
26
26
|
|
|
27
27
|
## Configuration
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
No setting is required. The evaluation model is resolved in this order:
|
|
30
|
+
|
|
31
|
+
1. `QCALEVAL_MODEL` (`provider/modelId`), an explicit override for pi CLI use;
|
|
32
|
+
2. a configured calibration VLM, recognised by its id (`Ising-Calibration`);
|
|
33
|
+
3. the conversation's own model, when it accepts images. The result is then
|
|
34
|
+
marked `fallback: true` in `details`, so a host without an Ising deployment
|
|
35
|
+
still gets a diagnosis from the vision model it already runs.
|
|
36
|
+
|
|
37
|
+
The output budget follows the model's `maxTokens` (at most 4096).
|
|
33
38
|
|
|
34
39
|
The provider must be declared in pi's `models.json` with its base URL and
|
|
35
40
|
credential, for example:
|
package/extensions/qcaleval.ts
CHANGED
|
@@ -16,20 +16,25 @@ import type {
|
|
|
16
16
|
} from "@earendil-works/pi-coding-agent";
|
|
17
17
|
import { Type, type Static } from "typebox";
|
|
18
18
|
|
|
19
|
-
/** `provider/modelId`; the model id itself may contain slashes (nvidia/...). */
|
|
20
|
-
export const DEFAULT_MODEL = "vllm/nvidia/Ising-Calibration-1.5-31B-NVFP4";
|
|
21
|
-
|
|
22
19
|
/** Figures per evaluation; a 16k-token deployment holds a few plots comfortably. */
|
|
23
20
|
export const MAX_IMAGES = 4;
|
|
24
21
|
|
|
22
|
+
/** Model ids of models fine-tuned for calibration plots; preferred when configured. */
|
|
23
|
+
export const CALIBRATION_MODEL_PATTERN = /ising-calibration/i;
|
|
24
|
+
|
|
25
|
+
/** Output budget when the model declares none; a diagnosis is a few hundred tokens. */
|
|
26
|
+
const DEFAULT_MAX_TOKENS = 2048;
|
|
27
|
+
const MAX_OUTPUT_TOKENS = 4096;
|
|
28
|
+
|
|
25
29
|
export interface ModelSpec {
|
|
26
30
|
provider: string;
|
|
27
31
|
modelId: string;
|
|
28
32
|
}
|
|
29
33
|
|
|
30
|
-
/** Parse `provider/modelId
|
|
31
|
-
export function parseModelSpec(value: string | undefined): ModelSpec {
|
|
32
|
-
const spec = (value ?? "").trim()
|
|
34
|
+
/** Parse an explicit `provider/modelId` override; the model id may contain slashes (nvidia/...). */
|
|
35
|
+
export function parseModelSpec(value: string | undefined): ModelSpec | undefined {
|
|
36
|
+
const spec = (value ?? "").trim();
|
|
37
|
+
if (!spec) return undefined;
|
|
33
38
|
const slash = spec.indexOf("/");
|
|
34
39
|
if (slash <= 0 || slash === spec.length - 1) {
|
|
35
40
|
throw new Error(`QCALEVAL_MODEL must be provider/modelId, got "${spec}"`);
|
|
@@ -37,6 +42,54 @@ export function parseModelSpec(value: string | undefined): ModelSpec {
|
|
|
37
42
|
return { provider: spec.slice(0, slash), modelId: spec.slice(slash + 1) };
|
|
38
43
|
}
|
|
39
44
|
|
|
45
|
+
/** The slice of pi's model registry the resolution needs. */
|
|
46
|
+
export interface ModelLookup {
|
|
47
|
+
find(provider: string, modelId: string): Model<Api> | undefined;
|
|
48
|
+
getAvailable(): Model<Api>[];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export type EvaluationModel =
|
|
52
|
+
| { model: Model<Api>; fallback: false }
|
|
53
|
+
| { model: Model<Api>; fallback: true }
|
|
54
|
+
| { model?: undefined; error: string };
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Pick the model that judges the plot, without any host-specific setting:
|
|
58
|
+
*
|
|
59
|
+
* 1. `QCALEVAL_MODEL` (`provider/modelId`), an explicit override for the pi CLI;
|
|
60
|
+
* 2. a configured calibration VLM, recognised by its id (Ising-Calibration);
|
|
61
|
+
* 3. the conversation's own model, when it accepts images.
|
|
62
|
+
*
|
|
63
|
+
* A host that declares the calibration model in its models.json gets it
|
|
64
|
+
* everywhere; one that does not (no Ising deployment) still gets a diagnosis
|
|
65
|
+
* from the vision model it already runs, marked as a fallback.
|
|
66
|
+
*/
|
|
67
|
+
export function resolveEvaluationModel(
|
|
68
|
+
registry: ModelLookup,
|
|
69
|
+
current: Model<Api> | undefined,
|
|
70
|
+
override: string | undefined,
|
|
71
|
+
): EvaluationModel {
|
|
72
|
+
const spec = parseModelSpec(override);
|
|
73
|
+
if (spec) {
|
|
74
|
+
const model = registry.find(spec.provider, spec.modelId);
|
|
75
|
+
return model
|
|
76
|
+
? { model, fallback: false }
|
|
77
|
+
: { error: `model ${spec.provider}/${spec.modelId} is not configured (QCALEVAL_MODEL)` };
|
|
78
|
+
}
|
|
79
|
+
const calibration = registry.getAvailable().find((model) => CALIBRATION_MODEL_PATTERN.test(model.id));
|
|
80
|
+
if (calibration) return { model: calibration, fallback: false };
|
|
81
|
+
if (current?.input.includes("image")) return { model: current, fallback: true };
|
|
82
|
+
return {
|
|
83
|
+
error:
|
|
84
|
+
"no evaluation model: declare a calibration VLM (Ising-Calibration) in models.json, or run with a vision model",
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Output budget for one evaluation, within what the model allows. */
|
|
89
|
+
export function outputBudget(model: Pick<Model<Api>, "maxTokens">): number {
|
|
90
|
+
return model.maxTokens > 0 ? Math.min(model.maxTokens, MAX_OUTPUT_TOKENS) : DEFAULT_MAX_TOKENS;
|
|
91
|
+
}
|
|
92
|
+
|
|
40
93
|
/** Categories the Ising-Calibration model card describes; asked for in this order. */
|
|
41
94
|
export const DIAGNOSIS_SECTIONS = [
|
|
42
95
|
"technical_description",
|
|
@@ -164,14 +217,21 @@ const parameters = Type.Object({
|
|
|
164
217
|
|
|
165
218
|
/** Stored on the tool result; the host reads `diagnosis` without reparsing text. */
|
|
166
219
|
export interface EvaluationDetails {
|
|
167
|
-
model
|
|
220
|
+
/** The model that judged the plot; absent when none could be resolved. */
|
|
221
|
+
model?: ModelSpec;
|
|
222
|
+
/** True when no calibration VLM was configured and the conversation's model judged instead. */
|
|
223
|
+
fallback?: boolean;
|
|
168
224
|
diagnosis?: Record<string, unknown> | null;
|
|
169
225
|
/** The model's answer when it was not the requested JSON. */
|
|
170
226
|
raw?: string;
|
|
171
227
|
}
|
|
172
228
|
|
|
173
|
-
function failure(spec: ModelSpec, text: string): AgentToolResult<EvaluationDetails> {
|
|
174
|
-
return {
|
|
229
|
+
function failure(spec: ModelSpec | undefined, text: string): AgentToolResult<EvaluationDetails> {
|
|
230
|
+
return {
|
|
231
|
+
content: [{ type: "text", text: `qcal_evaluate: ${text}` }],
|
|
232
|
+
details: spec ? { model: spec } : {},
|
|
233
|
+
isError: true,
|
|
234
|
+
};
|
|
175
235
|
}
|
|
176
236
|
|
|
177
237
|
export default function qcalevalExtension(pi: ExtensionAPI) {
|
|
@@ -194,11 +254,10 @@ export default function qcalevalExtension(pi: ExtensionAPI) {
|
|
|
194
254
|
_onUpdate: unknown,
|
|
195
255
|
ctx: ExtensionToolContext,
|
|
196
256
|
): Promise<AgentToolResult<EvaluationDetails>> {
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
}
|
|
257
|
+
const resolved = resolveEvaluationModel(ctx.modelRegistry, ctx.model, process.env.QCALEVAL_MODEL);
|
|
258
|
+
if (!resolved.model) return failure(undefined, resolved.error);
|
|
259
|
+
const { model, fallback } = resolved;
|
|
260
|
+
const spec: ModelSpec = { provider: model.provider, modelId: model.id };
|
|
202
261
|
const images: ImageContent[] = (params.images ?? []).map((image) => ({
|
|
203
262
|
type: "image",
|
|
204
263
|
data: image.data,
|
|
@@ -226,7 +285,7 @@ export default function qcalevalExtension(pi: ExtensionAPI) {
|
|
|
226
285
|
},
|
|
227
286
|
],
|
|
228
287
|
},
|
|
229
|
-
{ signal, maxTokens:
|
|
288
|
+
{ signal, maxTokens: outputBudget(model) },
|
|
230
289
|
);
|
|
231
290
|
if (message.stopReason === "error") {
|
|
232
291
|
return failure(spec, message.errorMessage ?? "model error");
|
|
@@ -237,6 +296,7 @@ export default function qcalevalExtension(pi: ExtensionAPI) {
|
|
|
237
296
|
content: [{ type: "text", text: diagnosis ? JSON.stringify(diagnosis, null, 2) : text }],
|
|
238
297
|
details: {
|
|
239
298
|
model: spec,
|
|
299
|
+
...(fallback ? { fallback: true } : {}),
|
|
240
300
|
diagnosis: diagnosis ?? null,
|
|
241
301
|
...(diagnosis ? {} : { raw: text }),
|
|
242
302
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orangekame3/pi-qcaleval",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2-beta.3",
|
|
4
4
|
"description": "pi extension that sends calibration plots to a calibration-evaluation VLM such as NVIDIA Ising-Calibration and returns a structured diagnosis",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|