@juspay/neurolink 12.49.9 → 12.49.10
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/CHANGELOG.md +1 -5
- package/dist/browser/neurolink.min.js +366 -366
- package/dist/providers/decide/dialects/index.d.ts +2 -0
- package/dist/providers/decide/dialects/index.js +5 -0
- package/dist/providers/decide/dialects/systemOne.d.ts +2 -0
- package/dist/providers/decide/dialects/systemOne.js +104 -0
- package/dist/providers/decide/guards.d.ts +4 -0
- package/dist/providers/decide/guards.js +29 -0
- package/dist/providers/systemOneDecision.d.ts +7 -3
- package/dist/providers/systemOneDecision.js +20 -117
- package/dist/types/decision.d.ts +17 -0
- package/package.json +1 -1
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { asNumber, asNumberMap, asStringMap, isRecord } from "../guards.js";
|
|
2
|
+
/**
|
|
3
|
+
* Last-resort confidence, when no transport reported one.
|
|
4
|
+
*
|
|
5
|
+
* The peak probability is what a calibrated confidence approximates, but it is
|
|
6
|
+
* NOT the same number, so a threshold tuned against a vendor's calibrated
|
|
7
|
+
* figure does not transfer unexamined; an even distribution lands near 1/N
|
|
8
|
+
* rather than 0.
|
|
9
|
+
*/
|
|
10
|
+
function deriveConfidence(probabilities) {
|
|
11
|
+
const values = Object.values(probabilities);
|
|
12
|
+
return values.length > 0 ? Math.max(...values) : 0;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Validate one answer off the wire. Returns null rather than throwing so a
|
|
16
|
+
* single malformed answer degrades to "unanswered" instead of failing the
|
|
17
|
+
* whole batch — the batch may hold hundreds of usable answers.
|
|
18
|
+
*/
|
|
19
|
+
function parseDecisionAnswer(raw, reportedConfidence) {
|
|
20
|
+
if (!isRecord(raw)) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
switch (raw.type) {
|
|
24
|
+
// "noul" is the System One wire's own spelling; "boolean" is the neutral
|
|
25
|
+
// one (Vercel's gateway), which renames both the type and the field.
|
|
26
|
+
// Accepting both keeps one parser for every transport.
|
|
27
|
+
case "noul":
|
|
28
|
+
case "boolean": {
|
|
29
|
+
const probability = asNumber(raw.noul) ?? asNumber(raw.probability);
|
|
30
|
+
return probability === undefined
|
|
31
|
+
? null
|
|
32
|
+
: { type: "boolean", probability };
|
|
33
|
+
}
|
|
34
|
+
case "choice": {
|
|
35
|
+
const probabilities = asNumberMap(raw.probabilities);
|
|
36
|
+
if (typeof raw.choice !== "string" || !probabilities) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
type: "choice",
|
|
41
|
+
choice: raw.choice,
|
|
42
|
+
confidence: asNumber(raw.confidence) ??
|
|
43
|
+
reportedConfidence ??
|
|
44
|
+
deriveConfidence(probabilities),
|
|
45
|
+
probabilities,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
case "score": {
|
|
49
|
+
const score = asNumber(raw.score);
|
|
50
|
+
const legend = asStringMap(raw.legend);
|
|
51
|
+
const probabilities = asNumberMap(raw.probabilities);
|
|
52
|
+
if (score === undefined || !legend || !probabilities) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
type: "score",
|
|
57
|
+
score,
|
|
58
|
+
confidence: asNumber(raw.confidence) ??
|
|
59
|
+
reportedConfidence ??
|
|
60
|
+
deriveConfidence(probabilities),
|
|
61
|
+
legend,
|
|
62
|
+
probabilities,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
default:
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* The System One wire calls the yes/no primitive `noul`; this codebase and
|
|
71
|
+
* every SDK exposing it call it `boolean`.
|
|
72
|
+
*/
|
|
73
|
+
function encodeQuestion(question) {
|
|
74
|
+
if (question.type === "boolean") {
|
|
75
|
+
return {
|
|
76
|
+
type: "noul",
|
|
77
|
+
instructions: question.instructions,
|
|
78
|
+
...(question.criteria ? { criteria: question.criteria } : {}),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
return question;
|
|
82
|
+
}
|
|
83
|
+
export const systemOneDialect = {
|
|
84
|
+
name: "system-one",
|
|
85
|
+
encodeQuestion,
|
|
86
|
+
readAnswers: (decoded, reportedConfidence) => {
|
|
87
|
+
const raw = decoded.answers;
|
|
88
|
+
if (!isRecord(raw)) {
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
const answers = {};
|
|
92
|
+
const dropped = [];
|
|
93
|
+
for (const [id, value] of Object.entries(raw)) {
|
|
94
|
+
const parsed = parseDecisionAnswer(value, reportedConfidence[id]);
|
|
95
|
+
if (parsed) {
|
|
96
|
+
answers[id] = parsed;
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
dropped.push(id);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return { answers, dropped };
|
|
103
|
+
},
|
|
104
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const isRecord: (v: unknown) => v is Record<string, unknown>;
|
|
2
|
+
export declare const asNumber: (v: unknown) => number | undefined;
|
|
3
|
+
export declare const asNumberMap: (v: unknown) => Record<string, number> | undefined;
|
|
4
|
+
export declare const asStringMap: (v: unknown) => Record<string, string> | undefined;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export const isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
2
|
+
export const asNumber = (v) => typeof v === "number" && Number.isFinite(v) ? v : undefined;
|
|
3
|
+
export const asNumberMap = (v) => {
|
|
4
|
+
if (!isRecord(v)) {
|
|
5
|
+
return undefined;
|
|
6
|
+
}
|
|
7
|
+
const out = {};
|
|
8
|
+
for (const [k, raw] of Object.entries(v)) {
|
|
9
|
+
const n = asNumber(raw);
|
|
10
|
+
if (n === undefined) {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
out[k] = n;
|
|
14
|
+
}
|
|
15
|
+
return out;
|
|
16
|
+
};
|
|
17
|
+
export const asStringMap = (v) => {
|
|
18
|
+
if (!isRecord(v)) {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
const out = {};
|
|
22
|
+
for (const [k, raw] of Object.entries(v)) {
|
|
23
|
+
if (typeof raw !== "string") {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
out[k] = raw;
|
|
27
|
+
}
|
|
28
|
+
return out;
|
|
29
|
+
};
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import type { AIProviderName } from "../constants/enums.js";
|
|
2
2
|
import { BaseProvider } from "../core/baseProvider.js";
|
|
3
3
|
import { ProviderError } from "../types/index.js";
|
|
4
|
-
import type { DecisionError, DecisionQuestion, DecisionRequest, DecisionResult, DecisionPreparedMedia, DecisionState, LanguageModel, StreamOptions, StreamResult, ValidationSchema } from "../types/index.js";
|
|
4
|
+
import type { DecisionDialect, DecisionError, DecisionQuestion, DecisionRequest, DecisionResult, DecisionPreparedMedia, DecisionState, LanguageModel, StreamOptions, StreamResult, ValidationSchema } from "../types/index.js";
|
|
5
5
|
/**
|
|
6
6
|
* Generous enough for a cold start (measured at 2.0–2.7s after idle on Jev)
|
|
7
7
|
* while still bounded. Every internal caller is fail-open, so this only bites
|
|
8
8
|
* on a genuine stall.
|
|
9
9
|
*/
|
|
10
10
|
export declare const DEFAULT_DECISION_TIMEOUT_MS = 5000;
|
|
11
|
-
export
|
|
12
|
-
export declare const asNumber: (v: unknown) => number | undefined;
|
|
11
|
+
export { asNumber, isRecord } from "./decide/guards.js";
|
|
13
12
|
/**
|
|
14
13
|
* FastAPI's request-validation envelope, `{"detail":[{loc, msg, input}]}`,
|
|
15
14
|
* reduced to its field paths and messages. `input` echoes the caller's own
|
|
@@ -73,6 +72,11 @@ export declare abstract class SystemOneDecisionProvider extends BaseProvider {
|
|
|
73
72
|
* every SDK exposing it call it `boolean`.
|
|
74
73
|
*/
|
|
75
74
|
protected encodeQuestion(question: DecisionQuestion): Record<string, unknown>;
|
|
75
|
+
/**
|
|
76
|
+
* The wire family this provider speaks. A provider on another layout
|
|
77
|
+
* overrides it; every one so far speaks System One.
|
|
78
|
+
*/
|
|
79
|
+
protected dialect(): DecisionDialect;
|
|
76
80
|
/**
|
|
77
81
|
* How long a rate-limited or unavailable response asks the caller to wait.
|
|
78
82
|
* Undefined keeps the default backoff. A wait longer than what is left of the
|
|
@@ -7,6 +7,8 @@ import { prepareDecisionMedia } from "../utils/decisionMedia.js";
|
|
|
7
7
|
import { logger } from "../utils/logger.js";
|
|
8
8
|
import { redactUrlsInText } from "../utils/logSanitize.js";
|
|
9
9
|
import { estimateDecisionStateTokens, resolveDecisionLimitsReading, } from "../utils/decisionLimits.js";
|
|
10
|
+
import { systemOneDialect } from "./decide/dialects/systemOne.js";
|
|
11
|
+
import { asNumber, isRecord } from "./decide/guards.js";
|
|
10
12
|
/**
|
|
11
13
|
* Generous enough for a cold start (measured at 2.0–2.7s after idle on Jev)
|
|
12
14
|
* while still bounded. Every internal caller is fail-open, so this only bites
|
|
@@ -14,102 +16,7 @@ import { estimateDecisionStateTokens, resolveDecisionLimitsReading, } from "../u
|
|
|
14
16
|
*/
|
|
15
17
|
export const DEFAULT_DECISION_TIMEOUT_MS = 5000;
|
|
16
18
|
const DEFAULT_MAX_RETRIES = 1;
|
|
17
|
-
export
|
|
18
|
-
export const asNumber = (v) => typeof v === "number" && Number.isFinite(v) ? v : undefined;
|
|
19
|
-
const asNumberMap = (v) => {
|
|
20
|
-
if (!isRecord(v)) {
|
|
21
|
-
return undefined;
|
|
22
|
-
}
|
|
23
|
-
const out = {};
|
|
24
|
-
for (const [k, raw] of Object.entries(v)) {
|
|
25
|
-
const n = asNumber(raw);
|
|
26
|
-
if (n === undefined) {
|
|
27
|
-
return undefined;
|
|
28
|
-
}
|
|
29
|
-
out[k] = n;
|
|
30
|
-
}
|
|
31
|
-
return out;
|
|
32
|
-
};
|
|
33
|
-
const asStringMap = (v) => {
|
|
34
|
-
if (!isRecord(v)) {
|
|
35
|
-
return undefined;
|
|
36
|
-
}
|
|
37
|
-
const out = {};
|
|
38
|
-
for (const [k, raw] of Object.entries(v)) {
|
|
39
|
-
if (typeof raw !== "string") {
|
|
40
|
-
return undefined;
|
|
41
|
-
}
|
|
42
|
-
out[k] = raw;
|
|
43
|
-
}
|
|
44
|
-
return out;
|
|
45
|
-
};
|
|
46
|
-
/**
|
|
47
|
-
* Last-resort confidence, when no transport reported one.
|
|
48
|
-
*
|
|
49
|
-
* The peak probability is what a calibrated confidence approximates, but it is
|
|
50
|
-
* NOT the same number, so a threshold tuned against a vendor's calibrated
|
|
51
|
-
* figure does not transfer unexamined; an even distribution lands near 1/N
|
|
52
|
-
* rather than 0.
|
|
53
|
-
*/
|
|
54
|
-
function deriveConfidence(probabilities) {
|
|
55
|
-
const values = Object.values(probabilities);
|
|
56
|
-
return values.length > 0 ? Math.max(...values) : 0;
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Validate one answer off the wire. Returns null rather than throwing so a
|
|
60
|
-
* single malformed answer degrades to "unanswered" instead of failing the
|
|
61
|
-
* whole batch — the batch may hold hundreds of usable answers.
|
|
62
|
-
*/
|
|
63
|
-
function parseDecisionAnswer(raw, reportedConfidence) {
|
|
64
|
-
if (!isRecord(raw)) {
|
|
65
|
-
return null;
|
|
66
|
-
}
|
|
67
|
-
switch (raw.type) {
|
|
68
|
-
// "noul" is the System One wire's own spelling; "boolean" is the neutral
|
|
69
|
-
// one (Vercel's gateway), which renames both the type and the field.
|
|
70
|
-
// Accepting both keeps one parser for every transport.
|
|
71
|
-
case "noul":
|
|
72
|
-
case "boolean": {
|
|
73
|
-
const probability = asNumber(raw.noul) ?? asNumber(raw.probability);
|
|
74
|
-
return probability === undefined
|
|
75
|
-
? null
|
|
76
|
-
: { type: "boolean", probability };
|
|
77
|
-
}
|
|
78
|
-
case "choice": {
|
|
79
|
-
const probabilities = asNumberMap(raw.probabilities);
|
|
80
|
-
if (typeof raw.choice !== "string" || !probabilities) {
|
|
81
|
-
return null;
|
|
82
|
-
}
|
|
83
|
-
return {
|
|
84
|
-
type: "choice",
|
|
85
|
-
choice: raw.choice,
|
|
86
|
-
confidence: asNumber(raw.confidence) ??
|
|
87
|
-
reportedConfidence ??
|
|
88
|
-
deriveConfidence(probabilities),
|
|
89
|
-
probabilities,
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
case "score": {
|
|
93
|
-
const score = asNumber(raw.score);
|
|
94
|
-
const legend = asStringMap(raw.legend);
|
|
95
|
-
const probabilities = asNumberMap(raw.probabilities);
|
|
96
|
-
if (score === undefined || !legend || !probabilities) {
|
|
97
|
-
return null;
|
|
98
|
-
}
|
|
99
|
-
return {
|
|
100
|
-
type: "score",
|
|
101
|
-
score,
|
|
102
|
-
confidence: asNumber(raw.confidence) ??
|
|
103
|
-
reportedConfidence ??
|
|
104
|
-
deriveConfidence(probabilities),
|
|
105
|
-
legend,
|
|
106
|
-
probabilities,
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
default:
|
|
110
|
-
return null;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
19
|
+
export { asNumber, isRecord } from "./decide/guards.js";
|
|
113
20
|
/**
|
|
114
21
|
* FastAPI's request-validation envelope, `{"detail":[{loc, msg, input}]}`,
|
|
115
22
|
* reduced to its field paths and messages. `input` echoes the caller's own
|
|
@@ -210,14 +117,14 @@ export class SystemOneDecisionProvider extends BaseProvider {
|
|
|
210
117
|
* every SDK exposing it call it `boolean`.
|
|
211
118
|
*/
|
|
212
119
|
encodeQuestion(question) {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
return
|
|
120
|
+
return this.dialect().encodeQuestion(question);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* The wire family this provider speaks. A provider on another layout
|
|
124
|
+
* overrides it; every one so far speaks System One.
|
|
125
|
+
*/
|
|
126
|
+
dialect() {
|
|
127
|
+
return systemOneDialect;
|
|
221
128
|
}
|
|
222
129
|
/**
|
|
223
130
|
* How long a rate-limited or unavailable response asks the caller to wait.
|
|
@@ -389,7 +296,10 @@ export class SystemOneDecisionProvider extends BaseProvider {
|
|
|
389
296
|
continue;
|
|
390
297
|
}
|
|
391
298
|
const decoded = this.readDecisionPayload(payload);
|
|
392
|
-
|
|
299
|
+
const reading = isRecord(decoded)
|
|
300
|
+
? this.dialect().readAnswers(decoded, this.reportedConfidence(decoded))
|
|
301
|
+
: undefined;
|
|
302
|
+
if (!isRecord(decoded) || !reading) {
|
|
393
303
|
throw this.decisionError({
|
|
394
304
|
kind: "server",
|
|
395
305
|
message: `${label} returned a response without an answers map.`,
|
|
@@ -398,19 +308,12 @@ export class SystemOneDecisionProvider extends BaseProvider {
|
|
|
398
308
|
retryable: false,
|
|
399
309
|
});
|
|
400
310
|
}
|
|
401
|
-
const
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
if (parsed) {
|
|
406
|
-
answers[id] = parsed;
|
|
407
|
-
}
|
|
408
|
-
else {
|
|
409
|
-
logger.warn(`${label}: dropped unparseable answer "${id}"`, {
|
|
410
|
-
requestId,
|
|
411
|
-
});
|
|
412
|
-
}
|
|
311
|
+
for (const id of reading.dropped) {
|
|
312
|
+
logger.warn(`${label}: dropped unparseable answer "${id}"`, {
|
|
313
|
+
requestId,
|
|
314
|
+
});
|
|
413
315
|
}
|
|
316
|
+
const answers = { ...reading.answers };
|
|
414
317
|
const usage = isRecord(decoded.usage) ? decoded.usage : {};
|
|
415
318
|
return {
|
|
416
319
|
// `resolvedModel`, not `this.modelName`: when the caller pinned a
|
package/dist/types/decision.d.ts
CHANGED
|
@@ -522,3 +522,20 @@ export type DecisionBooleanGate = {
|
|
|
522
522
|
/** Minimum {@link decisionBooleanConfidence} to act at all. Default 0.4. */
|
|
523
523
|
minConfidence?: number;
|
|
524
524
|
};
|
|
525
|
+
/** The wire family a decide provider speaks. */
|
|
526
|
+
export type DecideDialectName = "system-one";
|
|
527
|
+
export type DecisionAnswerReading = {
|
|
528
|
+
readonly answers: Readonly<Record<string, DecisionAnswer>>;
|
|
529
|
+
/** Ids of answers the server returned that could not be parsed. */
|
|
530
|
+
readonly dropped: readonly string[];
|
|
531
|
+
};
|
|
532
|
+
/**
|
|
533
|
+
* How questions are written to, and answers read from, a decide wire. One
|
|
534
|
+
* dialect serves every vendor that speaks the same layout.
|
|
535
|
+
*/
|
|
536
|
+
export type DecisionDialect = {
|
|
537
|
+
readonly name: DecideDialectName;
|
|
538
|
+
encodeQuestion: (question: DecisionQuestion) => Record<string, unknown>;
|
|
539
|
+
/** `undefined` means the response has no usable answers container. */
|
|
540
|
+
readAnswers: (decoded: Readonly<Record<string, unknown>>, reportedConfidence: Readonly<Record<string, number>>) => DecisionAnswerReading | undefined;
|
|
541
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@juspay/neurolink",
|
|
3
|
-
"version": "12.49.
|
|
3
|
+
"version": "12.49.10",
|
|
4
4
|
"packageManager": "pnpm@10.15.1",
|
|
5
5
|
"description": "The pipe layer of an AI nervous system: one interface connecting provider neurons to your application, across three inference types — generate, stream and decide. `decide` returns typed, calibrated judgements from a non-generative model (~400ms, ~$0.00002/call) for routing, tool selection and context budgeting. MCP-native (4 transports), voice TTS/STT/realtime, RAG, agents, memory, compaction, 9 observability exporters. OpenAI · Anthropic · Gemini · Bedrock · Azure · Ollama · TypeSafe Jev and more.",
|
|
6
6
|
"author": {
|