@konneal/engine 0.2.80 → 0.2.82
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/dist/{ask-7V4ECBLN.js → ask-QLY2CODU.js} +1 -1
- package/dist/{chunk-LTGOJVDF.js → chunk-DH3WDTZX.js} +14 -12
- package/dist/pipeline.d.ts +1 -1
- package/dist/worker_public/src/index.js +2 -2
- package/package.json +1 -1
- package/workers/worker_public/src/ask.ts +23 -11
- package/workers/worker_public/src/pipeline.ts +4 -1
|
@@ -2877,12 +2877,17 @@ Answer account questions from these records ONLY: name the record when you use i
|
|
|
2877
2877
|
const pendingTools = [];
|
|
2878
2878
|
if (/certificate|certified|certification/i.test(q.query) && env.DB) {
|
|
2879
2879
|
try {
|
|
2880
|
-
const
|
|
2881
|
-
|
|
2880
|
+
const imageTurn = typeof withImage === "string";
|
|
2881
|
+
const probeMessages = [...messages.slice(0, -1), { role: "user", content: `${TOOL_DECLARATION}
|
|
2882
|
+
|
|
2883
|
+
${imageTurn ? "A photograph of the instrument is attached after this message. Transcribe the maker, model and any printed certificate number you can read, then emit the TOOL line with those tokens as the query \u2014 never invented." : ""}
|
|
2882
2884
|
|
|
2883
|
-
${q.query}
|
|
2884
|
-
|
|
2885
|
-
|
|
2885
|
+
${q.query}`.replace("\n\n\n\n", "\n\n") }];
|
|
2886
|
+
if (imageTurn) probeMessages.push({ role: "user", content: [{ type: "image_url", image_url: { url: withImage } }] });
|
|
2887
|
+
const probe = await env.AI.run(model, {
|
|
2888
|
+
messages: probeMessages,
|
|
2889
|
+
max_tokens: 600,
|
|
2890
|
+
reasoning_effort: imageTurn ? "high" : "low"
|
|
2886
2891
|
});
|
|
2887
2892
|
const probeText = typeof probe?.response === "string" ? probe.response : probe?.choices?.[0]?.message?.content ?? "";
|
|
2888
2893
|
const call = parseToolCall(probeText);
|
|
@@ -2901,16 +2906,13 @@ ${q.query}` }],
|
|
|
2901
2906
|
const wantsMarkings = /\b(mark(ing|ings)?|nameplate|label|inscription|engrav|sticker|plate)\b|certificat/i.test(q.query);
|
|
2902
2907
|
const last = messages[messages.length - 1];
|
|
2903
2908
|
const note = wantsMarkings ? "\n\n(The user attached a photo with this question. First transcribe every inscription you can actually read in the image \u2014 names, model references, accuracy classes, numeric values with their units, certificate or approval numbers \u2014 quoting them verbatim. Then answer the question from the numbered passages, citing the requirement each interpretation rests on. If an inscription is unreadable, say so; never invent a marking.)" : "\n\n(The user attached an image with this question; interpret it directly when answering.)";
|
|
2904
|
-
if (
|
|
2909
|
+
if (typeof last.content === "string") {
|
|
2910
|
+
last.content = last.content + note;
|
|
2911
|
+
} else {
|
|
2905
2912
|
const textPart = last.content.find((p) => p.type === "text");
|
|
2906
2913
|
if (textPart) textPart.text += note;
|
|
2907
|
-
last.content = [...last.content, { type: "image_url", image_url: { url: withImage } }];
|
|
2908
|
-
} else {
|
|
2909
|
-
last.content = [
|
|
2910
|
-
{ type: "text", text: last.content + note },
|
|
2911
|
-
{ type: "image_url", image_url: { url: withImage } }
|
|
2912
|
-
];
|
|
2913
2914
|
}
|
|
2915
|
+
messages.push({ role: "user", content: [{ type: "image_url", image_url: { url: withImage } }] });
|
|
2914
2916
|
console.log("user image attached to generation");
|
|
2915
2917
|
}
|
|
2916
2918
|
const queryHash = await sha256Hex(q.query);
|
package/dist/pipeline.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
modelGroundingBlock,
|
|
7
7
|
scoreFaithfulness,
|
|
8
8
|
scoreJudge
|
|
9
|
-
} from "../../chunk-
|
|
9
|
+
} from "../../chunk-DH3WDTZX.js";
|
|
10
10
|
import {
|
|
11
11
|
handleSearch
|
|
12
12
|
} from "../../chunk-KO6DRI7C.js";
|
|
@@ -959,7 +959,7 @@ async function handleMcp(env, ctx, req, tier, key) {
|
|
|
959
959
|
// stream:false forces the JSON lane (anon defaults to SSE)
|
|
960
960
|
body: JSON.stringify({ ...args, stream: false })
|
|
961
961
|
});
|
|
962
|
-
const res = name === "ask" ? await (await import("../../ask-
|
|
962
|
+
const res = name === "ask" ? await (await import("../../ask-QLY2CODU.js")).handleAsk(env, ctx, inner, tier, key) : await (await import("../../search-LVUQLA3C.js")).handleSearch(env, ctx, inner, tier, key);
|
|
963
963
|
return res.json().catch(() => ({ error: { message: "tool transport failed", status: res.status } }));
|
|
964
964
|
});
|
|
965
965
|
if (out.ok && "accepted" in out) return new Response(null, { status: 202 });
|
package/package.json
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"playwright": "^1.62.1",
|
|
28
28
|
"yaml": "^2.9.1"
|
|
29
29
|
},
|
|
30
|
-
"version": "0.2.
|
|
30
|
+
"version": "0.2.82",
|
|
31
31
|
"description": "The Konneal engine: the publisher-agnostic build pipeline and API plane for standards intelligence (retrieval, answer contract, verdicts, evaluation).",
|
|
32
32
|
"license": "BSD-3-Clause",
|
|
33
33
|
"type": "module",
|
|
@@ -85,7 +85,7 @@ function embedWarm(env: Env, text: string): Promise<number[] | null> {
|
|
|
85
85
|
* AI 8005s that scale with payload size), and only the ONE pinned
|
|
86
86
|
* figure attaches (the type-intent pin already chose the answering
|
|
87
87
|
* object; a second base64 blob doubles the flake surface for nothing). */
|
|
88
|
-
async function attachFigureImages(env: Env, messages: { role: string; content:
|
|
88
|
+
async function attachFigureImages(env: Env, messages: { role: string; content: any }[], usedHits: Hit[], query: string): Promise<void> {
|
|
89
89
|
// Attach only when the question WANTS the drawing (names a figure-ish
|
|
90
90
|
// artifact) or the pinned figure sits in the top prose passage's own
|
|
91
91
|
// clause (it IS the answering object) — a plain definition question
|
|
@@ -1222,12 +1222,20 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
|
|
|
1222
1222
|
// invocations record into pendingTools; the stream emits them as its
|
|
1223
1223
|
// first events (the UI shows what the agent consulted).
|
|
1224
1224
|
const pendingTools: { name: string; query: string; output: string }[] = [];
|
|
1225
|
+
// On an IMAGE turn the probe CARRIES THE IMAGE (2026-10-03, the owner's
|
|
1226
|
+
// architecture): the model that reads the plate is the model that issues
|
|
1227
|
+
// the tool call — one reader, one tool, no parallel extraction racing a
|
|
1228
|
+
// second vision pass that drifts hour to hour. The old probe dropped the
|
|
1229
|
+
// image-bearing message, which is why the private bridge had to exist.
|
|
1225
1230
|
if (/certificate|certified|certification/i.test(q.query) && env.DB) {
|
|
1226
1231
|
try {
|
|
1232
|
+
const imageTurn = typeof withImage === "string";
|
|
1233
|
+
const probeMessages: any[] = [...messages.slice(0, -1), { role: "user", content: `${TOOL_DECLARATION}\n\n${imageTurn ? "A photograph of the instrument is attached after this message. Transcribe the maker, model and any printed certificate number you can read, then emit the TOOL line with those tokens as the query — never invented." : ""}\n\n${q.query}`.replace("\n\n\n\n", "\n\n") }];
|
|
1234
|
+
if (imageTurn) probeMessages.push({ role: "user", content: [{ type: "image_url", image_url: { url: withImage } }] });
|
|
1227
1235
|
const probe = await (env as any).AI.run(model, {
|
|
1228
|
-
messages:
|
|
1229
|
-
max_tokens:
|
|
1230
|
-
reasoning_effort: "low",
|
|
1236
|
+
messages: probeMessages,
|
|
1237
|
+
max_tokens: 600,
|
|
1238
|
+
reasoning_effort: imageTurn ? "high" : "low",
|
|
1231
1239
|
});
|
|
1232
1240
|
const probeText = typeof probe?.response === "string" ? probe.response : probe?.choices?.[0]?.message?.content ?? "";
|
|
1233
1241
|
const call = parseToolCall(probeText);
|
|
@@ -1255,16 +1263,20 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
|
|
|
1255
1263
|
const note = wantsMarkings
|
|
1256
1264
|
? "\n\n(The user attached a photo with this question. First transcribe every inscription you can actually read in the image — names, model references, accuracy classes, numeric values with their units, certificate or approval numbers — quoting them verbatim. Then answer the question from the numbered passages, citing the requirement each interpretation rests on. If an inscription is unreadable, say so; never invent a marking.)"
|
|
1257
1265
|
: "\n\n(The user attached an image with this question; interpret it directly when answering.)";
|
|
1258
|
-
|
|
1266
|
+
|
|
1267
|
+
// the image rides its OWN SHORT TRAILING MESSAGE — never inside the
|
|
1268
|
+
// passage-bearing user message. Long text + image parts in ONE message
|
|
1269
|
+
// is the documented instability shape (2026-09-09); it was reintroduced
|
|
1270
|
+
// here, and the live "blindness" on photo answers (2026-10-03) was this
|
|
1271
|
+
// violation, not the model: the probe and extraction calls, which keep
|
|
1272
|
+
// the image on its own message, read the same plate every time.
|
|
1273
|
+
if (typeof last.content === "string") {
|
|
1274
|
+
last.content = last.content + note;
|
|
1275
|
+
} else {
|
|
1259
1276
|
const textPart = last.content.find((p: any) => p.type === "text");
|
|
1260
1277
|
if (textPart) textPart.text += note;
|
|
1261
|
-
last.content = [...last.content, { type: "image_url", image_url: { url: withImage } }] as unknown as string;
|
|
1262
|
-
} else {
|
|
1263
|
-
last.content = [
|
|
1264
|
-
{ type: "text", text: last.content + note },
|
|
1265
|
-
{ type: "image_url", image_url: { url: withImage } },
|
|
1266
|
-
] as unknown as string;
|
|
1267
1278
|
}
|
|
1279
|
+
messages.push({ role: "user", content: [{ type: "image_url", image_url: { url: withImage } }] });
|
|
1268
1280
|
console.log("user image attached to generation");
|
|
1269
1281
|
}
|
|
1270
1282
|
const queryHash = await sha256Hex(q.query);
|
|
@@ -180,7 +180,10 @@ function clipToTokens(s: string, maxTok: number): string {
|
|
|
180
180
|
}
|
|
181
181
|
|
|
182
182
|
export interface BuiltMessages {
|
|
183
|
-
|
|
183
|
+
// content is string OR an OpenAI-style parts array — the image parts
|
|
184
|
+
// ride their OWN trailing user message (never inside a passage-bearing
|
|
185
|
+
// one), so the type admits both shapes
|
|
186
|
+
messages: { role: string; content: string | any[] }[];
|
|
184
187
|
usedHits: Hit[]; // passages actually included (citations must match these)
|
|
185
188
|
}
|
|
186
189
|
|