@juspay/neurolink 12.47.13 → 12.48.0
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 +3 -3
- package/dist/auth/middleware/AuthMiddleware.d.ts +1 -1
- package/dist/browser/neurolink.min.js +578 -565
- package/dist/cli/factories/commandFactory.d.ts +7 -0
- package/dist/cli/factories/commandFactory.js +26 -0
- package/dist/cli/loop/optionsSchema.js +9 -0
- package/dist/context/budgetChecker.js +54 -2
- package/dist/context/contextDecision.js +2 -0
- package/dist/context/toolPairRepair.d.ts +12 -0
- package/dist/context/toolPairRepair.js +29 -3
- package/dist/core/baseProvider.d.ts +92 -3
- package/dist/core/baseProvider.js +254 -10
- package/dist/core/conversationMemoryManager.js +17 -2
- package/dist/core/loopEngine.js +1 -1
- package/dist/core/modules/MessageBuilder.js +11 -3
- package/dist/core/modules/TelemetryHandler.d.ts +2 -2
- package/dist/core/modules/ToolsManager.d.ts +35 -0
- package/dist/core/modules/ToolsManager.js +159 -55
- package/dist/core/nativeGenerateLoop.d.ts +58 -1
- package/dist/core/nativeGenerateLoop.js +277 -3
- package/dist/core/redisConversationMemoryManager.js +10 -3
- package/dist/core/resolveRequestKind.d.ts +24 -4
- package/dist/core/resolveRequestKind.js +31 -4
- package/dist/core/toolExecutionRecorder.d.ts +6 -0
- package/dist/core/toolExecutionRecorder.js +24 -2
- package/dist/core/toolRoutingDecision.js +1 -0
- package/dist/factories/mediaHandlerCatalog.js +5 -0
- package/dist/factories/providerDescriptors.js +15 -0
- package/dist/files/fileTools.d.ts +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.js +9 -2
- package/dist/memory/condensationPrompt.d.ts +8 -0
- package/dist/memory/condensationPrompt.js +29 -0
- package/dist/memory/hippocampusInitializer.d.ts +14 -2
- package/dist/memory/hippocampusInitializer.js +64 -3
- package/dist/neurolink.d.ts +252 -3
- package/dist/neurolink.js +898 -43
- package/dist/providers/amazonBedrock/client.js +6 -0
- package/dist/providers/amazonSagemaker.js +14 -1
- package/dist/providers/anthropic/client.js +148 -19
- package/dist/providers/anthropic/loopAdapter.js +2 -2
- package/dist/providers/googleAiStudio/client.js +29 -14
- package/dist/providers/openaiChatCompletionsBase.js +103 -23
- package/dist/providers/systemOneDecision.d.ts +6 -0
- package/dist/providers/systemOneDecision.js +14 -63
- package/dist/providers/typesafe.d.ts +6 -0
- package/dist/providers/typesafe.js +6 -0
- package/dist/rag/retrieval/searchDecision.js +1 -0
- package/dist/routing/classifierRouter.js +6 -5
- package/dist/routing/classifierStrategies.js +8 -1
- package/dist/types/classifierRouter.d.ts +16 -3
- package/dist/types/common.d.ts +2 -0
- package/dist/types/config.d.ts +9 -0
- package/dist/types/context.d.ts +8 -1
- package/dist/types/conversation.d.ts +87 -0
- package/dist/types/decision.d.ts +210 -1
- package/dist/types/decision.js +9 -1
- package/dist/types/dispatch.d.ts +11 -4
- package/dist/types/generate.d.ts +118 -54
- package/dist/types/loopEngine.d.ts +7 -2
- package/dist/types/memory.d.ts +177 -9
- package/dist/types/openaiCompatible.d.ts +11 -3
- package/dist/types/stream.d.ts +13 -16
- package/dist/types/stt.d.ts +58 -1
- package/dist/types/tools.d.ts +14 -0
- package/dist/types/tools.js +8 -0
- package/dist/types/tts.d.ts +108 -0
- package/dist/types/voice.d.ts +41 -4
- package/dist/utils/decisionAnswers.d.ts +31 -1
- package/dist/utils/decisionAnswers.js +114 -0
- package/dist/utils/decisionLimits.d.ts +39 -0
- package/dist/utils/decisionLimits.js +116 -0
- package/dist/utils/logger.d.ts +7 -0
- package/dist/utils/logger.js +9 -0
- package/dist/utils/messageBuilder.d.ts +45 -1
- package/dist/utils/messageBuilder.js +393 -16
- package/dist/utils/multimodalOptionsBuilder.d.ts +1 -0
- package/dist/utils/multimodalOptionsBuilder.js +1 -0
- package/dist/utils/speechText.d.ts +41 -0
- package/dist/utils/speechText.js +364 -0
- package/dist/utils/toolResultStatus.d.ts +26 -0
- package/dist/utils/toolResultStatus.js +73 -0
- package/dist/utils/ttsProcessor.d.ts +29 -0
- package/dist/utils/ttsProcessor.js +84 -2
- package/dist/voice/index.d.ts +1 -0
- package/dist/voice/index.js +3 -0
- package/dist/voice/providers/ElevenLabsSTT.d.ts +42 -0
- package/dist/voice/providers/ElevenLabsSTT.js +351 -0
- package/dist/voice/providers/ElevenLabsTTS.d.ts +81 -1
- package/dist/voice/providers/ElevenLabsTTS.js +415 -74
- package/docs-site/static/search-index.json +48 -33
- package/package.json +5 -2
- package/dist/utils/toolEndEmitter.d.ts +0 -25
- package/dist/utils/toolEndEmitter.js +0 -64
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* @module utils/decisionAnswers
|
|
9
9
|
*/
|
|
10
|
-
import type { DecisionAnswerMap, DecisionBooleanGate, DecisionChoiceReading, DecisionScoreReading, ProviderDescriptor } from "../types/index.js";
|
|
10
|
+
import type { DecisionAnswerMap, DecisionBooleanGate, DecisionChoiceReading, DecisionQuestion, DecisionResult, DecisionScoreReading, ProviderDescriptor } from "../types/index.js";
|
|
11
11
|
/**
|
|
12
12
|
* Read a yes/no answer as a probability.
|
|
13
13
|
*
|
|
@@ -54,3 +54,33 @@ export declare function gateDecisionBoolean(answers: DecisionAnswerMap, id: stri
|
|
|
54
54
|
* after any normalisation the wire might apply.
|
|
55
55
|
*/
|
|
56
56
|
export declare function decisionKey(namespace: string, index: number): string;
|
|
57
|
+
/** Namespace for questions a host adds through `decisionHooks.extendQuestions`. */
|
|
58
|
+
export declare const HOST_DECISION_NAMESPACE = "host";
|
|
59
|
+
/** Whether a wire id is one the funnel gave a host's question (`host__N`). */
|
|
60
|
+
export declare function isHostDecisionKey(id: string): boolean;
|
|
61
|
+
/**
|
|
62
|
+
* A validated copy of a question a host handed back from `extendQuestions`,
|
|
63
|
+
* or undefined when it is malformed or cannot be copied. The wire carries
|
|
64
|
+
* the copy, never the host's object: a getter, a proxy or a later edit on
|
|
65
|
+
* the host's side can then reach neither the request nor the answers keyed
|
|
66
|
+
* on it. A value `structuredClone` refuses (a proxy, a function-valued
|
|
67
|
+
* field, a throwing getter) is treated as malformed.
|
|
68
|
+
*/
|
|
69
|
+
export declare function snapshotDecisionQuestion(value: unknown): DecisionQuestion | undefined;
|
|
70
|
+
/**
|
|
71
|
+
* `result` without the answers to a host's namespaced questions — what a
|
|
72
|
+
* consumer is handed when the funnel's host-facing steps failed after the
|
|
73
|
+
* wire call had already returned. A null-prototype map, so an own id such
|
|
74
|
+
* as `__proto__` survives the copy.
|
|
75
|
+
*/
|
|
76
|
+
export declare function stripHostAnswers(result: DecisionResult, hostIdByWireId: ReadonlyMap<string, string>): DecisionResult;
|
|
77
|
+
/**
|
|
78
|
+
* Runtime check that a value from outside the type system — a host's
|
|
79
|
+
* `extendQuestions` hook — is a well-formed question, so a malformed one
|
|
80
|
+
* is dropped on its own rather than failing the whole request that
|
|
81
|
+
* NeuroLink's own routing depends on.
|
|
82
|
+
*
|
|
83
|
+
* A `choice` needs at least two options and a `score` at least two levels;
|
|
84
|
+
* fewer carries no information, and the servers refuse it anyway.
|
|
85
|
+
*/
|
|
86
|
+
export declare function isDecisionQuestion(value: unknown): value is DecisionQuestion;
|
|
@@ -93,3 +93,117 @@ export function gateDecisionBoolean(answers, id, gate) {
|
|
|
93
93
|
export function decisionKey(namespace, index) {
|
|
94
94
|
return `${namespace}__${index}`;
|
|
95
95
|
}
|
|
96
|
+
/** Namespace for questions a host adds through `decisionHooks.extendQuestions`. */
|
|
97
|
+
export const HOST_DECISION_NAMESPACE = "host";
|
|
98
|
+
/** Whether a wire id is one the funnel gave a host's question (`host__N`). */
|
|
99
|
+
export function isHostDecisionKey(id) {
|
|
100
|
+
return id.startsWith(`${HOST_DECISION_NAMESPACE}__`);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* A validated copy of a question a host handed back from `extendQuestions`,
|
|
104
|
+
* or undefined when it is malformed or cannot be copied. The wire carries
|
|
105
|
+
* the copy, never the host's object: a getter, a proxy or a later edit on
|
|
106
|
+
* the host's side can then reach neither the request nor the answers keyed
|
|
107
|
+
* on it. A value `structuredClone` refuses (a proxy, a function-valued
|
|
108
|
+
* field, a throwing getter) is treated as malformed.
|
|
109
|
+
*/
|
|
110
|
+
export function snapshotDecisionQuestion(value) {
|
|
111
|
+
let copy;
|
|
112
|
+
try {
|
|
113
|
+
copy = structuredClone(value);
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
return isDecisionQuestion(copy) ? copy : undefined;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* `result` without the answers to a host's namespaced questions — what a
|
|
122
|
+
* consumer is handed when the funnel's host-facing steps failed after the
|
|
123
|
+
* wire call had already returned. A null-prototype map, so an own id such
|
|
124
|
+
* as `__proto__` survives the copy.
|
|
125
|
+
*/
|
|
126
|
+
export function stripHostAnswers(result, hostIdByWireId) {
|
|
127
|
+
if (hostIdByWireId.size === 0) {
|
|
128
|
+
return result;
|
|
129
|
+
}
|
|
130
|
+
const answers = Object.create(null);
|
|
131
|
+
for (const [id, answer] of Object.entries(result.answers)) {
|
|
132
|
+
if (!hostIdByWireId.has(id)) {
|
|
133
|
+
answers[id] = answer;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return { ...result, answers };
|
|
137
|
+
}
|
|
138
|
+
const isRecordValue = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
139
|
+
/**
|
|
140
|
+
* Whether `value` is something a decision model accepts as content: a
|
|
141
|
+
* string, number or boolean, or an array or plain object of the same.
|
|
142
|
+
* `null` and `undefined` are not — a question with no instructions is
|
|
143
|
+
* refused by every provider.
|
|
144
|
+
*/
|
|
145
|
+
function isDecisionInput(value) {
|
|
146
|
+
switch (typeof value) {
|
|
147
|
+
case "string":
|
|
148
|
+
case "boolean":
|
|
149
|
+
return true;
|
|
150
|
+
case "number":
|
|
151
|
+
// NaN and ±Infinity serialise to null and would make the provider
|
|
152
|
+
// reject the whole request, not just this question.
|
|
153
|
+
return Number.isFinite(value);
|
|
154
|
+
case "object":
|
|
155
|
+
if (value === null) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
return Array.isArray(value)
|
|
159
|
+
? value.every(isDecisionInput)
|
|
160
|
+
: Object.values(value).every(isDecisionInput);
|
|
161
|
+
default:
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Runtime check that a value from outside the type system — a host's
|
|
167
|
+
* `extendQuestions` hook — is a well-formed question, so a malformed one
|
|
168
|
+
* is dropped on its own rather than failing the whole request that
|
|
169
|
+
* NeuroLink's own routing depends on.
|
|
170
|
+
*
|
|
171
|
+
* A `choice` needs at least two options and a `score` at least two levels;
|
|
172
|
+
* fewer carries no information, and the servers refuse it anyway.
|
|
173
|
+
*/
|
|
174
|
+
export function isDecisionQuestion(value) {
|
|
175
|
+
try {
|
|
176
|
+
return isWellFormedDecisionQuestion(value);
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
// Cyclic or otherwise exotic host input: the walk below can overflow the
|
|
180
|
+
// stack. That drops this question alone rather than failing the request.
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
function isWellFormedDecisionQuestion(value) {
|
|
185
|
+
if (!isRecordValue(value) || !isDecisionInput(value.instructions)) {
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
switch (value.type) {
|
|
189
|
+
case "boolean": {
|
|
190
|
+
if (value.criteria === undefined) {
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
if (!isRecordValue(value.criteria)) {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
return Object.values(value.criteria).every((side) => side === null || side === undefined || isDecisionInput(side));
|
|
197
|
+
}
|
|
198
|
+
case "choice":
|
|
199
|
+
return (isRecordValue(value.criteria) &&
|
|
200
|
+
Object.keys(value.criteria).length >= 2 &&
|
|
201
|
+
Object.values(value.criteria).every((rubric) => rubric === null || isDecisionInput(rubric)));
|
|
202
|
+
case "score":
|
|
203
|
+
return (Array.isArray(value.criteria) &&
|
|
204
|
+
value.criteria.length >= 2 &&
|
|
205
|
+
value.criteria.every((level) => level === null || isDecisionInput(level)));
|
|
206
|
+
default:
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decision limits — the one place a provider's `decisionLimits` are read and
|
|
3
|
+
* a state's size is estimated against them.
|
|
4
|
+
*
|
|
5
|
+
* `SystemOneDecisionProvider` refuses an over-limit request through these
|
|
6
|
+
* functions, and `NeuroLink.decisionLimits()` / `estimateDecisionStateTokens`
|
|
7
|
+
* expose the same functions to hosts, so what a host measures before the call
|
|
8
|
+
* is exactly what the pre-flight check measures inside it. Two estimators that
|
|
9
|
+
* drift apart would let a host size a state as fitting and then watch it be
|
|
10
|
+
* refused.
|
|
11
|
+
*
|
|
12
|
+
* @module utils/decisionLimits
|
|
13
|
+
*/
|
|
14
|
+
import type { DecisionLimits, DecisionLimitsReading, DecisionState, ProviderDescriptor } from "../types/index.js";
|
|
15
|
+
/**
|
|
16
|
+
* Estimate how many tokens a decision model will read from `state`.
|
|
17
|
+
*
|
|
18
|
+
* `estimateTokens` assumes ~4 characters per token. That holds for English
|
|
19
|
+
* and is several times too generous for other scripts on an English
|
|
20
|
+
* tokenizer, so when the limits declare a `nonAsciiTokensPerChar` rate,
|
|
21
|
+
* non-ASCII characters are counted at it instead. Digits, ASCII punctuation
|
|
22
|
+
* and astral characters (emoji) are each counted separately only when the
|
|
23
|
+
* limits declare a rate for that class — a tokenizer that reads each digit,
|
|
24
|
+
* and most punctuation, on its own makes a number-heavy or JSON-heavy state
|
|
25
|
+
* several times longer than the ~4-characters-per-token estimate suggests. A
|
|
26
|
+
* non-string state is serialized first, as it is on the wire.
|
|
27
|
+
*
|
|
28
|
+
* This is the estimator the pre-flight refusal uses: a state this reports at
|
|
29
|
+
* or under `maxStateTokens` is sent, one over it is refused before any
|
|
30
|
+
* network call.
|
|
31
|
+
*/
|
|
32
|
+
export declare function estimateDecisionStateTokens(state: DecisionState, limits?: Pick<DecisionLimits, "nonAsciiTokensPerChar" | "digitTokensPerChar" | "symbolTokensPerChar" | "astralTokensPerChar">): number;
|
|
33
|
+
/**
|
|
34
|
+
* Flatten a descriptor's `decisionLimits` for one model: the per-model entry
|
|
35
|
+
* overrides the base, field by field, and `advisory` becomes
|
|
36
|
+
* `enforcedLocally: false`. Returns null for a descriptor that declares no
|
|
37
|
+
* limits.
|
|
38
|
+
*/
|
|
39
|
+
export declare function resolveDecisionLimitsReading(descriptor: Pick<ProviderDescriptor, "name" | "decisionLimits">, model: string): DecisionLimitsReading | null;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decision limits — the one place a provider's `decisionLimits` are read and
|
|
3
|
+
* a state's size is estimated against them.
|
|
4
|
+
*
|
|
5
|
+
* `SystemOneDecisionProvider` refuses an over-limit request through these
|
|
6
|
+
* functions, and `NeuroLink.decisionLimits()` / `estimateDecisionStateTokens`
|
|
7
|
+
* expose the same functions to hosts, so what a host measures before the call
|
|
8
|
+
* is exactly what the pre-flight check measures inside it. Two estimators that
|
|
9
|
+
* drift apart would let a host size a state as fitting and then watch it be
|
|
10
|
+
* refused.
|
|
11
|
+
*
|
|
12
|
+
* @module utils/decisionLimits
|
|
13
|
+
*/
|
|
14
|
+
import { CHARS_PER_TOKEN, estimateTokens, serializeForEstimate, } from "./tokenEstimation.js";
|
|
15
|
+
/**
|
|
16
|
+
* Estimate how many tokens a decision model will read from `state`.
|
|
17
|
+
*
|
|
18
|
+
* `estimateTokens` assumes ~4 characters per token. That holds for English
|
|
19
|
+
* and is several times too generous for other scripts on an English
|
|
20
|
+
* tokenizer, so when the limits declare a `nonAsciiTokensPerChar` rate,
|
|
21
|
+
* non-ASCII characters are counted at it instead. Digits, ASCII punctuation
|
|
22
|
+
* and astral characters (emoji) are each counted separately only when the
|
|
23
|
+
* limits declare a rate for that class — a tokenizer that reads each digit,
|
|
24
|
+
* and most punctuation, on its own makes a number-heavy or JSON-heavy state
|
|
25
|
+
* several times longer than the ~4-characters-per-token estimate suggests. A
|
|
26
|
+
* non-string state is serialized first, as it is on the wire.
|
|
27
|
+
*
|
|
28
|
+
* This is the estimator the pre-flight refusal uses: a state this reports at
|
|
29
|
+
* or under `maxStateTokens` is sent, one over it is refused before any
|
|
30
|
+
* network call.
|
|
31
|
+
*/
|
|
32
|
+
export function estimateDecisionStateTokens(state, limits) {
|
|
33
|
+
const text = serializeForEstimate(state);
|
|
34
|
+
const nonAsciiTokensPerChar = limits?.nonAsciiTokensPerChar;
|
|
35
|
+
const digit = limits?.digitTokensPerChar;
|
|
36
|
+
const symbol = limits?.symbolTokensPerChar;
|
|
37
|
+
const astral = limits?.astralTokensPerChar;
|
|
38
|
+
if (nonAsciiTokensPerChar === undefined &&
|
|
39
|
+
digit === undefined &&
|
|
40
|
+
symbol === undefined &&
|
|
41
|
+
astral === undefined) {
|
|
42
|
+
return estimateTokens(text);
|
|
43
|
+
}
|
|
44
|
+
// Each class of character is counted once, at its own rate. A class with no
|
|
45
|
+
// declared rate stays in the ordinary four-characters-a-token estimate (or,
|
|
46
|
+
// for non-ASCII, at that estimate's per-character rate), so a provider that
|
|
47
|
+
// declares none of the extra rates is estimated exactly as before.
|
|
48
|
+
const isDigit = (c) => c >= "0" && c <= "9";
|
|
49
|
+
const isSymbol = (c) => /[!-/:-@[-`{-~]/.test(c);
|
|
50
|
+
let digits = 0;
|
|
51
|
+
let symbols = 0;
|
|
52
|
+
let astrals = 0;
|
|
53
|
+
let nonAscii = 0;
|
|
54
|
+
const rest = [];
|
|
55
|
+
for (const c of text) {
|
|
56
|
+
const code = c.codePointAt(0) ?? 0;
|
|
57
|
+
if (code > 0xffff && astral !== undefined) {
|
|
58
|
+
astrals += 1;
|
|
59
|
+
}
|
|
60
|
+
else if (code > 0x7f) {
|
|
61
|
+
nonAscii += 1;
|
|
62
|
+
}
|
|
63
|
+
else if (digit !== undefined && isDigit(c)) {
|
|
64
|
+
digits += 1;
|
|
65
|
+
}
|
|
66
|
+
else if (symbol !== undefined && isSymbol(c)) {
|
|
67
|
+
symbols += 1;
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
rest.push(c);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return (estimateTokens(rest.join("")) +
|
|
74
|
+
Math.ceil(digits * (digit ?? 0)) +
|
|
75
|
+
Math.ceil(symbols * (symbol ?? 0)) +
|
|
76
|
+
Math.ceil(astrals * (astral ?? 0)) +
|
|
77
|
+
Math.ceil(nonAscii * (nonAsciiTokensPerChar ?? 1 / CHARS_PER_TOKEN)));
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Flatten a descriptor's `decisionLimits` for one model: the per-model entry
|
|
81
|
+
* overrides the base, field by field, and `advisory` becomes
|
|
82
|
+
* `enforcedLocally: false`. Returns null for a descriptor that declares no
|
|
83
|
+
* limits.
|
|
84
|
+
*/
|
|
85
|
+
export function resolveDecisionLimitsReading(descriptor, model) {
|
|
86
|
+
const limits = descriptor.decisionLimits;
|
|
87
|
+
if (!limits) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
const modelLimits = limits.models?.[model];
|
|
91
|
+
const nonAsciiTokensPerChar = modelLimits?.nonAsciiTokensPerChar ?? limits.nonAsciiTokensPerChar;
|
|
92
|
+
return {
|
|
93
|
+
provider: descriptor.name,
|
|
94
|
+
model,
|
|
95
|
+
maxStateTokens: modelLimits?.maxStateTokens ?? limits.maxStateTokens,
|
|
96
|
+
// Absent for a provider that caps by tokens or bytes rather than by
|
|
97
|
+
// question count (TypeSafe, XOR).
|
|
98
|
+
...(limits.maxQuestions !== undefined
|
|
99
|
+
? { maxQuestions: limits.maxQuestions }
|
|
100
|
+
: {}),
|
|
101
|
+
...(nonAsciiTokensPerChar !== undefined ? { nonAsciiTokensPerChar } : {}),
|
|
102
|
+
// Not model-overridable — `models` only carries `maxStateTokens` and
|
|
103
|
+
// `nonAsciiTokensPerChar` — so these three come from the base limits only.
|
|
104
|
+
...(limits.digitTokensPerChar !== undefined
|
|
105
|
+
? { digitTokensPerChar: limits.digitTokensPerChar }
|
|
106
|
+
: {}),
|
|
107
|
+
...(limits.symbolTokensPerChar !== undefined
|
|
108
|
+
? { symbolTokensPerChar: limits.symbolTokensPerChar }
|
|
109
|
+
: {}),
|
|
110
|
+
...(limits.astralTokensPerChar !== undefined
|
|
111
|
+
? { astralTokensPerChar: limits.astralTokensPerChar }
|
|
112
|
+
: {}),
|
|
113
|
+
...(limits.media !== undefined ? { media: limits.media } : {}),
|
|
114
|
+
enforcedLocally: limits.advisory !== true,
|
|
115
|
+
};
|
|
116
|
+
}
|
package/dist/utils/logger.d.ts
CHANGED
|
@@ -30,6 +30,12 @@ declare class NeuroLinkLogger {
|
|
|
30
30
|
* @param emitter - The event emitter instance
|
|
31
31
|
*/
|
|
32
32
|
setEventEmitter(emitter: LogEventEmitter): void;
|
|
33
|
+
/**
|
|
34
|
+
* The process-wide sink currently receiving log events, or undefined when
|
|
35
|
+
* none is set. Lets code that must construct an instance without changing
|
|
36
|
+
* the sink capture it beforehand and put it back afterwards.
|
|
37
|
+
*/
|
|
38
|
+
getEventEmitter(): LogEventEmitter | undefined;
|
|
33
39
|
/**
|
|
34
40
|
* Clears the event emitter reference.
|
|
35
41
|
* Should be called when a NeuroLink instance is disposed to prevent memory
|
|
@@ -279,6 +285,7 @@ export declare const logger: {
|
|
|
279
285
|
getLogs: (level?: LogLevel) => LogEntry[];
|
|
280
286
|
clearLogs: () => void;
|
|
281
287
|
setEventEmitter: (emitter: LogEventEmitter) => void;
|
|
288
|
+
getEventEmitter: () => LogEventEmitter | undefined;
|
|
282
289
|
clearEventEmitter: (ifEmitter?: LogEventEmitter) => void;
|
|
283
290
|
runInInstanceScope: <T>(instanceId: string, fn: () => T) => T;
|
|
284
291
|
getInstanceScope: () => string | undefined;
|
package/dist/utils/logger.js
CHANGED
|
@@ -113,6 +113,14 @@ class NeuroLinkLogger {
|
|
|
113
113
|
setEventEmitter(emitter) {
|
|
114
114
|
this.eventEmitter = emitter;
|
|
115
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* The process-wide sink currently receiving log events, or undefined when
|
|
118
|
+
* none is set. Lets code that must construct an instance without changing
|
|
119
|
+
* the sink capture it beforehand and put it back afterwards.
|
|
120
|
+
*/
|
|
121
|
+
getEventEmitter() {
|
|
122
|
+
return this.eventEmitter;
|
|
123
|
+
}
|
|
116
124
|
/**
|
|
117
125
|
* Clears the event emitter reference.
|
|
118
126
|
* Should be called when a NeuroLink instance is disposed to prevent memory
|
|
@@ -634,6 +642,7 @@ export const logger = {
|
|
|
634
642
|
getLogs: (level) => neuroLinkLogger.getLogs(level),
|
|
635
643
|
clearLogs: () => neuroLinkLogger.clearLogs(),
|
|
636
644
|
setEventEmitter: (emitter) => neuroLinkLogger.setEventEmitter(emitter),
|
|
645
|
+
getEventEmitter: () => neuroLinkLogger.getEventEmitter(),
|
|
637
646
|
clearEventEmitter: (ifEmitter) => neuroLinkLogger.clearEventEmitter(ifEmitter),
|
|
638
647
|
// Per-instance routing (see NeuroLinkLogger.runInInstanceScope)
|
|
639
648
|
runInInstanceScope: (instanceId, fn) => neuroLinkLogger.runInInstanceScope(instanceId, fn),
|
|
@@ -1,10 +1,47 @@
|
|
|
1
1
|
import type { GenerateOptions, MultimodalChatMessage, StreamOptions, TextGenerationOptions, VisionImageOutputFormat } from "../types/index.js";
|
|
2
|
-
import type { ModelMessage } from "../types/index.js";
|
|
2
|
+
import type { ModelMessage, ToolHistoryRow, ToolReplayMode } from "../types/index.js";
|
|
3
3
|
/**
|
|
4
4
|
* Type-safe conversion from MultimodalChatMessage[] to ModelMessage[]
|
|
5
5
|
* Filters out invalid content and ensures strict ModelMessage contract compliance
|
|
6
6
|
*/
|
|
7
7
|
export declare function convertToModelMessages(messages: MultimodalChatMessage[]): ModelMessage[];
|
|
8
|
+
/**
|
|
9
|
+
* Resolve the replay mode for a request: the per-request option, else the
|
|
10
|
+
* default. The instance-level `conversationMemory.replayToolSteps` is folded
|
|
11
|
+
* into the option by BaseProvider before the builder runs.
|
|
12
|
+
*/
|
|
13
|
+
export declare const resolveToolReplayMode: (options: {
|
|
14
|
+
replayToolSteps?: ToolReplayMode;
|
|
15
|
+
} | undefined) => ToolReplayMode;
|
|
16
|
+
/**
|
|
17
|
+
* Replay a session's tool rows into the prompt while the caller converts the
|
|
18
|
+
* regular rows its own way.
|
|
19
|
+
*
|
|
20
|
+
* Usage: normalise the history with `rows()`, then for each row call
|
|
21
|
+
* `consume(row)`; a `true` return means the row was a tool row and is now
|
|
22
|
+
* held. Before converting a non-tool row (and once after the last row) call
|
|
23
|
+
* `flush(messages, nextRole)`, which appends whatever the held tool rows
|
|
24
|
+
* amount to under `mode`:
|
|
25
|
+
*
|
|
26
|
+
* - `"full"`: paired tool-call / tool-result turns, one pair per step.
|
|
27
|
+
* - `"marker"`: markers are folded into the assistant text — returned for
|
|
28
|
+
* the caller to prepend when the next row is the assistant that answered
|
|
29
|
+
* after those tools; otherwise appended to the assistant turn just
|
|
30
|
+
* emitted, or emitted as a marker-only assistant turn when there is none.
|
|
31
|
+
* - `"off"`: dropped.
|
|
32
|
+
*
|
|
33
|
+
* The caller owns the conversion of regular rows so that the text and the
|
|
34
|
+
* multimodal builders keep their own (different) handling of them.
|
|
35
|
+
*/
|
|
36
|
+
export declare const createToolStepReplay: (mode: ToolReplayMode) => {
|
|
37
|
+
rows: <TRow extends ToolHistoryRow>(rows: ReadonlyArray<TRow>) => TRow[];
|
|
38
|
+
consume: (row: ToolHistoryRow) => boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Append the held steps to `messages`. Returns marker text the caller must
|
|
41
|
+
* prepend to the assistant row it is about to convert, or undefined.
|
|
42
|
+
*/
|
|
43
|
+
flush: (messages: Array<ModelMessage | MultimodalChatMessage>, nextRole: string | undefined) => string | undefined;
|
|
44
|
+
};
|
|
8
45
|
/**
|
|
9
46
|
* Build a properly formatted message array for AI providers
|
|
10
47
|
* Combines system prompt, conversation history, and current user prompt
|
|
@@ -43,6 +80,13 @@ export declare function processUnifiedFilesArray(options: GenerateOptions, maxSi
|
|
|
43
80
|
* Detects when images are present and routes through provider adapter
|
|
44
81
|
*/
|
|
45
82
|
export declare function buildMultimodalMessagesArray(options: GenerateOptions, provider: string, model: string): Promise<MultimodalChatMessage[]>;
|
|
83
|
+
/**
|
|
84
|
+
* The multimodal builder proper. Returns model messages so a session's tool
|
|
85
|
+
* steps can be replayed as real tool turns (`replayToolSteps: "full"`) on a
|
|
86
|
+
* turn that also carries files or images; `buildMultimodalMessagesArray`
|
|
87
|
+
* wraps this for callers on the older `MultimodalChatMessage[]` shape.
|
|
88
|
+
*/
|
|
89
|
+
export declare function buildMultimodalModelMessages(options: GenerateOptions, provider: string, model: string, replayMode?: ToolReplayMode): Promise<Array<MultimodalChatMessage | ModelMessage>>;
|
|
46
90
|
/**
|
|
47
91
|
* Transcode any image in `input.images` that no vision provider accepts,
|
|
48
92
|
* rewriting the entry in place as a PNG data URI.
|