@juspay/neurolink 12.47.14 → 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 +2 -1
- 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
|
@@ -15,6 +15,7 @@ import { withTimeout } from "../utils/errorHandling.js";
|
|
|
15
15
|
import { buildContextFromPointer, getEffectiveTokenThreshold, } from "../utils/conversationMemory.js";
|
|
16
16
|
import { runWithCurrentLangfuseContext } from "../services/server/ai/observability/instrumentation.js";
|
|
17
17
|
import { logger } from "../utils/logger.js";
|
|
18
|
+
import { extractIsErrorText } from "../utils/toolResultStatus.js";
|
|
18
19
|
import { deserializeConversation, encodeStoredMessages, getSessionMessagesKey, MESSAGES_KEY_SUFFIX, isSessionMessagesKey, parseStoredMessages, serializeConversationMetadata, usesSplitMessageStorage, getNormalizedConfig, getPooledRedisClient, getSessionKey, getUserSessionsKey, releasePooledRedisClient, scanKeys, serializeConversation, } from "../utils/redis.js";
|
|
19
20
|
const redisTracer = tracers.redis;
|
|
20
21
|
const REDIS_TIMEOUT_MS = 5000;
|
|
@@ -1602,11 +1603,17 @@ User message: "${userMessage}"`;
|
|
|
1602
1603
|
? { stepIndex: Number(toolResult.stepIndex) }
|
|
1603
1604
|
: {}),
|
|
1604
1605
|
};
|
|
1605
|
-
// Build result — success/error metadata only, NOT the output data
|
|
1606
|
+
// Build result — success/error metadata only, NOT the output data.
|
|
1607
|
+
// An `isError: true` payload the tool RETURNED (converted throw,
|
|
1608
|
+
// breaker refusal, MCP failure) is a failure as much as a thrown one,
|
|
1609
|
+
// and is flagged here so replay never reports it as "→ ok".
|
|
1610
|
+
const resultError = toolResult.error !== undefined && toolResult.error !== null
|
|
1611
|
+
? String(toolResult.error)
|
|
1612
|
+
: extractIsErrorText(toolResultValue);
|
|
1606
1613
|
const result = {
|
|
1607
|
-
success:
|
|
1614
|
+
success: resultError === undefined,
|
|
1608
1615
|
// result.result intentionally NOT stored — inferred from content at read time
|
|
1609
|
-
error:
|
|
1616
|
+
error: resultError,
|
|
1610
1617
|
};
|
|
1611
1618
|
const toolResultMessage = {
|
|
1612
1619
|
id: randomUUID(),
|
|
@@ -1,4 +1,23 @@
|
|
|
1
|
-
import type { RequestKind, RequestKindInput } from "../types/index.js";
|
|
1
|
+
import type { RequestKind, RequestKindInput, RequestKindTTSInput, TTSSynthesisMode } from "../types/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Which text a `generate({ tts })` request synthesizes. An explicit
|
|
4
|
+
* `tts.mode` wins; otherwise the legacy `useAiResponse` flag decides, and the
|
|
5
|
+
* absence of both means `"direct"` — the documented default that every
|
|
6
|
+
* "speak this text" caller relies on.
|
|
7
|
+
*
|
|
8
|
+
* Every precedence site (this module, `BaseProvider.synthesizeAIResponseIfNeeded`,
|
|
9
|
+
* `NeuroLink.attemptMCPGeneration`, the AI Studio generate() override) reads
|
|
10
|
+
* the answer from here so the rule cannot drift between them.
|
|
11
|
+
*/
|
|
12
|
+
export declare function resolveTTSMode(tts: RequestKindTTSInput | undefined): TTSSynthesisMode;
|
|
13
|
+
/**
|
|
14
|
+
* True when TTS is enabled and the request wants the input text synthesized,
|
|
15
|
+
* bypassing the LLM turn. `enabled` is tested for truthiness, as the dispatch
|
|
16
|
+
* it replaced did and as `synthesizeAIResponseIfNeeded` still does: a caller
|
|
17
|
+
* off the declared type (`enabled: 1`, `"true"`) reaches the same branch at
|
|
18
|
+
* every site rather than being routed to a model call by one of them.
|
|
19
|
+
*/
|
|
20
|
+
export declare function isDirectTTSRequest(tts: RequestKindTTSInput | undefined): boolean;
|
|
2
21
|
/**
|
|
3
22
|
* The dispatch decision for "what kind of request is this" — text, image,
|
|
4
23
|
* video, music, avatar, direct TTS synthesis, or PPT generation — at the
|
|
@@ -18,9 +37,10 @@ import type { RequestKind, RequestKindInput } from "../types/index.js";
|
|
|
18
37
|
* non-image output.format (json/structured/text) — this lets dual-mode
|
|
19
38
|
* models like gemini-3.1-flash-image-preview still perform text or
|
|
20
39
|
* structured generation when requested.
|
|
21
|
-
* 3. tts.enabled
|
|
22
|
-
*
|
|
23
|
-
*
|
|
40
|
+
* 3. tts.enabled with `resolveTTSMode()` answering "direct" — i.e. no
|
|
41
|
+
* `mode: "response"` and no legacy `useAiResponse: true` — direct
|
|
42
|
+
* synthesis, bypassing the LLM turn entirely (response mode means the
|
|
43
|
+
* LLM's own text gets synthesized afterward, which is NOT this branch).
|
|
24
44
|
* 4. otherwise, "text".
|
|
25
45
|
*/
|
|
26
46
|
export declare function resolveRequestKind(options: RequestKindInput, modelName?: string): RequestKind;
|
|
@@ -1,4 +1,30 @@
|
|
|
1
1
|
import { isImageGenerationModel } from "./constants.js";
|
|
2
|
+
/**
|
|
3
|
+
* Which text a `generate({ tts })` request synthesizes. An explicit
|
|
4
|
+
* `tts.mode` wins; otherwise the legacy `useAiResponse` flag decides, and the
|
|
5
|
+
* absence of both means `"direct"` — the documented default that every
|
|
6
|
+
* "speak this text" caller relies on.
|
|
7
|
+
*
|
|
8
|
+
* Every precedence site (this module, `BaseProvider.synthesizeAIResponseIfNeeded`,
|
|
9
|
+
* `NeuroLink.attemptMCPGeneration`, the AI Studio generate() override) reads
|
|
10
|
+
* the answer from here so the rule cannot drift between them.
|
|
11
|
+
*/
|
|
12
|
+
export function resolveTTSMode(tts) {
|
|
13
|
+
if (tts?.mode === "direct" || tts?.mode === "response") {
|
|
14
|
+
return tts.mode;
|
|
15
|
+
}
|
|
16
|
+
return tts?.useAiResponse ? "response" : "direct";
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* True when TTS is enabled and the request wants the input text synthesized,
|
|
20
|
+
* bypassing the LLM turn. `enabled` is tested for truthiness, as the dispatch
|
|
21
|
+
* it replaced did and as `synthesizeAIResponseIfNeeded` still does: a caller
|
|
22
|
+
* off the declared type (`enabled: 1`, `"true"`) reaches the same branch at
|
|
23
|
+
* every site rather than being routed to a model call by one of them.
|
|
24
|
+
*/
|
|
25
|
+
export function isDirectTTSRequest(tts) {
|
|
26
|
+
return Boolean(tts?.enabled) && resolveTTSMode(tts) === "direct";
|
|
27
|
+
}
|
|
2
28
|
/**
|
|
3
29
|
* The dispatch decision for "what kind of request is this" — text, image,
|
|
4
30
|
* video, music, avatar, direct TTS synthesis, or PPT generation — at the
|
|
@@ -18,9 +44,10 @@ import { isImageGenerationModel } from "./constants.js";
|
|
|
18
44
|
* non-image output.format (json/structured/text) — this lets dual-mode
|
|
19
45
|
* models like gemini-3.1-flash-image-preview still perform text or
|
|
20
46
|
* structured generation when requested.
|
|
21
|
-
* 3. tts.enabled
|
|
22
|
-
*
|
|
23
|
-
*
|
|
47
|
+
* 3. tts.enabled with `resolveTTSMode()` answering "direct" — i.e. no
|
|
48
|
+
* `mode: "response"` and no legacy `useAiResponse: true` — direct
|
|
49
|
+
* synthesis, bypassing the LLM turn entirely (response mode means the
|
|
50
|
+
* LLM's own text gets synthesized afterward, which is NOT this branch).
|
|
24
51
|
* 4. otherwise, "text".
|
|
25
52
|
*/
|
|
26
53
|
export function resolveRequestKind(options, modelName) {
|
|
@@ -42,7 +69,7 @@ export function resolveRequestKind(options, modelName) {
|
|
|
42
69
|
if (isImageGenerationModel(modelName) && !requestsNonImageOutput) {
|
|
43
70
|
return "image";
|
|
44
71
|
}
|
|
45
|
-
if (options.tts
|
|
72
|
+
if (isDirectTTSRequest(options.tts)) {
|
|
46
73
|
return "tts-direct";
|
|
47
74
|
}
|
|
48
75
|
return "text";
|
|
@@ -23,6 +23,12 @@ export declare function serializeToolResult(result: unknown, maxChars: number):
|
|
|
23
23
|
* Error-shaped tool results (MCP `isError`, `{error}` payloads) count as
|
|
24
24
|
* failures even though the loop received them as normal returns.
|
|
25
25
|
*/
|
|
26
|
+
/**
|
|
27
|
+
* True for an `error` value that actually carries something — `true`, a
|
|
28
|
+
* non-empty string, or a non-empty object/array — never a falsy placeholder
|
|
29
|
+
* a tool sets on every call (`error: false`, `error: 0`, `error: {}`).
|
|
30
|
+
*/
|
|
31
|
+
export declare function isMeaningfulError(error: unknown): boolean;
|
|
26
32
|
export declare function isErrorShapedToolResult(result: unknown): boolean;
|
|
27
33
|
/**
|
|
28
34
|
* Per-call recorder for tool executions. See module doc.
|
|
@@ -45,6 +45,26 @@ export function serializeToolResult(result, maxChars) {
|
|
|
45
45
|
* Error-shaped tool results (MCP `isError`, `{error}` payloads) count as
|
|
46
46
|
* failures even though the loop received them as normal returns.
|
|
47
47
|
*/
|
|
48
|
+
/**
|
|
49
|
+
* True for an `error` value that actually carries something — `true`, a
|
|
50
|
+
* non-empty string, or a non-empty object/array — never a falsy placeholder
|
|
51
|
+
* a tool sets on every call (`error: false`, `error: 0`, `error: {}`).
|
|
52
|
+
*/
|
|
53
|
+
export function isMeaningfulError(error) {
|
|
54
|
+
if (error === true) {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
if (typeof error === "string") {
|
|
58
|
+
return error.length > 0;
|
|
59
|
+
}
|
|
60
|
+
if (Array.isArray(error)) {
|
|
61
|
+
return error.length > 0;
|
|
62
|
+
}
|
|
63
|
+
if (typeof error === "object" && error !== null) {
|
|
64
|
+
return Object.keys(error).length > 0;
|
|
65
|
+
}
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
48
68
|
export function isErrorShapedToolResult(result) {
|
|
49
69
|
if (!result || typeof result !== "object") {
|
|
50
70
|
return false;
|
|
@@ -53,10 +73,12 @@ export function isErrorShapedToolResult(result) {
|
|
|
53
73
|
if (record.isError === true) {
|
|
54
74
|
return true;
|
|
55
75
|
}
|
|
56
|
-
if (record.error
|
|
76
|
+
if (isMeaningfulError(record.error)) {
|
|
57
77
|
return true;
|
|
58
78
|
}
|
|
59
|
-
if (typeof record.status === "string"
|
|
79
|
+
if (typeof record.status === "string" &&
|
|
80
|
+
record.success !== true &&
|
|
81
|
+
record.ok !== true) {
|
|
60
82
|
return ["error", "failed", "failure", "fail"].includes(record.status.toLowerCase().trim());
|
|
61
83
|
}
|
|
62
84
|
return false;
|
|
@@ -101,6 +101,7 @@ export async function selectServersByDecision(userQuery, routableServers, decide
|
|
|
101
101
|
questions[decisionKey(SERVER_NAMESPACE, index)] = serverQuestion(server);
|
|
102
102
|
});
|
|
103
103
|
const result = await decide({
|
|
104
|
+
site: "toolRouting",
|
|
104
105
|
// Structured state rather than a prompt string: the vendor accepts JSON,
|
|
105
106
|
// and naming the fields is what stops the model reading the catalogue as
|
|
106
107
|
// part of the user's request.
|
|
@@ -24,6 +24,11 @@ export const MEDIA_HANDLER_CATALOG = [
|
|
|
24
24
|
{ kind: "stt", name: "deepgram" },
|
|
25
25
|
{ kind: "stt", name: "google-stt" },
|
|
26
26
|
{ kind: "stt", name: "azure-stt" },
|
|
27
|
+
// "elevenlabs" is reused across kinds on purpose — TTS primary, STT alias —
|
|
28
|
+
// the same way "vertex" is a TTS alias and the Video primary. Each kind has
|
|
29
|
+
// its own registry, so `stt: { provider: "elevenlabs" }` and
|
|
30
|
+
// `tts: { provider: "elevenlabs" }` resolve to different handlers.
|
|
31
|
+
{ kind: "stt", name: "elevenlabs-stt", aliases: ["scribe", "elevenlabs"] },
|
|
27
32
|
// --- Realtime ---
|
|
28
33
|
{ kind: "realtime", name: "openai-realtime" },
|
|
29
34
|
{ kind: "realtime", name: "gemini-live" },
|
|
@@ -449,6 +449,21 @@ const HAND_DESCRIPTORS = [
|
|
|
449
449
|
// defaultHealthSweepPriority: all three feed generation fallback chains,
|
|
450
450
|
// and a text-less provider must never be reachable from them.
|
|
451
451
|
timeouts: { decideMs: 5000 },
|
|
452
|
+
// ADVISORY, not enforced: Jev's server refuses an over-limit request
|
|
453
|
+
// itself (measured by bisection — a 33,002-token state accepted, 33,003
|
|
454
|
+
// rejected), so this row exists for `decisionLimits()` to report, and the
|
|
455
|
+
// pre-flight check skips it. Refusing locally would turn a measured server
|
|
456
|
+
// ceiling into a client one that drifts from it silently. The number
|
|
457
|
+
// mirrors TYPESAFE_MAX_STATE_TOKENS in providers/typesafe.ts (which
|
|
458
|
+
// cannot be imported here without a provider → descriptor cycle); the
|
|
459
|
+
// decide suite pins the two equal. No question COUNT cap has been
|
|
460
|
+
// measured — 400 questions beside a near-ceiling state were accepted —
|
|
461
|
+
// because TypeSafe bounds a request by tokens (TYPESAFE_MAX_REQUEST_TOKENS,
|
|
462
|
+
// 64K on state plus all questions), not by count.
|
|
463
|
+
decisionLimits: {
|
|
464
|
+
maxStateTokens: 33_000,
|
|
465
|
+
advisory: true,
|
|
466
|
+
},
|
|
452
467
|
setupUrl: "https://console.typesafe.ai/keys",
|
|
453
468
|
},
|
|
454
469
|
// Laya MUST stay after TypeSafe, XOR after Laya, Perplexity after XOR, and
|
|
@@ -157,7 +157,7 @@ export declare function createFileTools(registry: FileReferenceRegistry): {
|
|
|
157
157
|
} | undefined;
|
|
158
158
|
columns?: string[] | undefined;
|
|
159
159
|
entry_path?: string | undefined;
|
|
160
|
-
format?: "text" | "
|
|
160
|
+
format?: "text" | "detailed" | "summary" | undefined;
|
|
161
161
|
}, {
|
|
162
162
|
success: false;
|
|
163
163
|
error: string | undefined;
|
package/dist/index.d.ts
CHANGED
|
@@ -55,12 +55,14 @@ export { createSkillStore, FileSystemSkillStore, InMemorySkillStore, } from "./s
|
|
|
55
55
|
export { S3SkillStore } from "./skills/skillStoreS3.js";
|
|
56
56
|
export { RedisSkillStore } from "./skills/skillStoreRedis.js";
|
|
57
57
|
export { createSkillTools } from "./skills/skillTools.js";
|
|
58
|
+
export { validateCondensationPrompt } from "./memory/condensationPrompt.js";
|
|
58
59
|
export * from "./types/index.js";
|
|
59
60
|
export { isAbortError } from "./utils/errorHandling.js";
|
|
60
61
|
export { calculateCost, hasPricing } from "./utils/pricing.js";
|
|
61
62
|
export { getAvailableProviders, getBestProvider, isValidProvider, } from "./utils/providerUtils.js";
|
|
62
63
|
export { TTSProcessor } from "./utils/ttsProcessor.js";
|
|
63
64
|
export { TTS_ERROR_CODES, TTSError } from "./utils/ttsProcessor.js";
|
|
65
|
+
export { prepareTextForSpeech, resolveSpeechSanitizeOptions, } from "./utils/speechText.js";
|
|
64
66
|
export { VideoProcessor } from "./utils/videoProcessor.js";
|
|
65
67
|
export { VIDEO_ERROR_CODES, VideoError } from "./utils/videoProcessor.js";
|
|
66
68
|
export { AvatarProcessor } from "./utils/avatarProcessor.js";
|
|
@@ -68,7 +70,7 @@ export { AVATAR_ERROR_CODES, AvatarError } from "./utils/avatarProcessor.js";
|
|
|
68
70
|
export { MusicProcessor } from "./utils/musicProcessor.js";
|
|
69
71
|
export { MUSIC_ERROR_CODES, MusicError } from "./utils/musicProcessor.js";
|
|
70
72
|
export { STTProcessor } from "./utils/sttProcessor.js";
|
|
71
|
-
export { AzureTTS, AzureTTSHandler, CartesiaTTS, CartesiaTTSHandler, ElevenLabsTTS, ElevenLabsTTSHandler, FishAudioTTS, FishAudioTTSHandler, SixtyDBTTS, SixtyDBTTSHandler, GoogleTTSHandler, OpenAITTS, OpenAITTSHandler, AzureSTT, AzureSTTHandler, DeepgramSTT, DeepgramSTTHandler, GoogleSTT, GoogleSTTHandler, OpenAISTT, OpenAISTTHandler, WhisperSTT, WhisperSTTHandler, BaseRealtimeHandler, GeminiLive, GeminiLiveHandler, OpenAIRealtime, OpenAIRealtimeHandler, RealtimeProcessor, RealtimeError, STTError, VoiceError, registerDefaultRealtimeHandlers, registerDefaultSTTHandlers, registerDefaultTTSHandlers, } from "./voice/index.js";
|
|
73
|
+
export { AzureTTS, AzureTTSHandler, CartesiaTTS, CartesiaTTSHandler, ElevenLabsTTS, ElevenLabsTTSHandler, FishAudioTTS, FishAudioTTSHandler, SixtyDBTTS, SixtyDBTTSHandler, GoogleTTSHandler, OpenAITTS, OpenAITTSHandler, AzureSTT, AzureSTTHandler, DeepgramSTT, DeepgramSTTHandler, ElevenLabsSTT, ElevenLabsSTTHandler, GoogleSTT, GoogleSTTHandler, OpenAISTT, OpenAISTTHandler, WhisperSTT, WhisperSTTHandler, BaseRealtimeHandler, GeminiLive, GeminiLiveHandler, OpenAIRealtime, OpenAIRealtimeHandler, RealtimeProcessor, RealtimeError, STTError, VoiceError, registerDefaultRealtimeHandlers, registerDefaultSTTHandlers, registerDefaultTTSHandlers, } from "./voice/index.js";
|
|
72
74
|
export { BeatovenMusic, BeatovenMusicHandler, ElevenLabsMusic, ElevenLabsMusicHandler, LyriaMusic, LyriaMusicHandler, registerDefaultMusicHandlers, ReplicateMusic, ReplicateMusicHandler, } from "./music/index.js";
|
|
73
75
|
export { DIDAvatar, DIDAvatarHandler, HeyGenAvatar, HeyGenAvatarHandler, registerDefaultAvatarHandlers, ReplicateAvatar, ReplicateAvatarHandler, } from "./avatar/index.js";
|
|
74
76
|
export { isVertexVideoConfigured, KlingVideoHandler, registerDefaultVideoHandlers, ReplicateVideoHandler, RunwayVideoHandler, VertexVideoHandler, } from "./adapters/video/index.js";
|
|
@@ -417,7 +419,8 @@ export { classifyProviderError, ModelPool, createDefaultRequestRouter, Classifie
|
|
|
417
419
|
* if (team && team.confidence > 0.7) { route(team.choice); }
|
|
418
420
|
* ```
|
|
419
421
|
*/
|
|
420
|
-
export { decisionBooleanConfidence, decisionKey, gateDecisionBoolean, readDecisionBoolean, readDecisionChoice, readDecisionScore, servesInferenceKind, } from "./utils/decisionAnswers.js";
|
|
422
|
+
export { decisionBooleanConfidence, decisionKey, gateDecisionBoolean, HOST_DECISION_NAMESPACE, isDecisionQuestion, readDecisionBoolean, readDecisionChoice, readDecisionScore, servesInferenceKind, } from "./utils/decisionAnswers.js";
|
|
423
|
+
export { estimateDecisionStateTokens, resolveDecisionLimitsReading, } from "./utils/decisionLimits.js";
|
|
421
424
|
export { DECISION_PROVIDERS, resolveDefaultDecisionProvider, } from "./factories/providerDescriptors.js";
|
|
422
425
|
export { TYPESAFE_MAX_REQUEST_TOKENS, TYPESAFE_MAX_STATE_TOKENS, } from "./providers/typesafe.js";
|
|
423
426
|
/**
|
package/dist/index.js
CHANGED
|
@@ -95,6 +95,8 @@ export { createSkillStore, FileSystemSkillStore, InMemorySkillStore, } from "./s
|
|
|
95
95
|
export { S3SkillStore } from "./skills/skillStoreS3.js";
|
|
96
96
|
export { RedisSkillStore } from "./skills/skillStoreRedis.js";
|
|
97
97
|
export { createSkillTools } from "./skills/skillTools.js";
|
|
98
|
+
// Memory (Hippocampus) — condensation prompt template validation
|
|
99
|
+
export { validateCondensationPrompt } from "./memory/condensationPrompt.js";
|
|
98
100
|
// Export ALL types from the centralized type barrel
|
|
99
101
|
export * from "./types/index.js";
|
|
100
102
|
// Error utilities
|
|
@@ -106,6 +108,7 @@ export { getAvailableProviders, getBestProvider, isValidProvider, } from "./util
|
|
|
106
108
|
// TTS utilities
|
|
107
109
|
export { TTSProcessor } from "./utils/ttsProcessor.js";
|
|
108
110
|
export { TTS_ERROR_CODES, TTSError } from "./utils/ttsProcessor.js";
|
|
111
|
+
export { prepareTextForSpeech, resolveSpeechSanitizeOptions, } from "./utils/speechText.js";
|
|
109
112
|
// Video utilities
|
|
110
113
|
export { VideoProcessor } from "./utils/videoProcessor.js";
|
|
111
114
|
export { VIDEO_ERROR_CODES, VideoError } from "./utils/videoProcessor.js";
|
|
@@ -131,7 +134,7 @@ export {
|
|
|
131
134
|
// TTS
|
|
132
135
|
AzureTTS, AzureTTSHandler, CartesiaTTS, CartesiaTTSHandler, ElevenLabsTTS, ElevenLabsTTSHandler, FishAudioTTS, FishAudioTTSHandler, SixtyDBTTS, SixtyDBTTSHandler, GoogleTTSHandler, OpenAITTS, OpenAITTSHandler,
|
|
133
136
|
// STT
|
|
134
|
-
AzureSTT, AzureSTTHandler, DeepgramSTT, DeepgramSTTHandler, GoogleSTT, GoogleSTTHandler, OpenAISTT, OpenAISTTHandler, WhisperSTT, WhisperSTTHandler,
|
|
137
|
+
AzureSTT, AzureSTTHandler, DeepgramSTT, DeepgramSTTHandler, ElevenLabsSTT, ElevenLabsSTTHandler, GoogleSTT, GoogleSTTHandler, OpenAISTT, OpenAISTTHandler, WhisperSTT, WhisperSTTHandler,
|
|
135
138
|
// Realtime
|
|
136
139
|
BaseRealtimeHandler, GeminiLive, GeminiLiveHandler, OpenAIRealtime, OpenAIRealtimeHandler, RealtimeProcessor,
|
|
137
140
|
// Voice error classes (RealtimeError + STTError + VoiceError)
|
|
@@ -655,7 +658,11 @@ export { classifyProviderError, ModelPool, createDefaultRequestRouter, Classifie
|
|
|
655
658
|
* if (team && team.confidence > 0.7) { route(team.choice); }
|
|
656
659
|
* ```
|
|
657
660
|
*/
|
|
658
|
-
export { decisionBooleanConfidence, decisionKey, gateDecisionBoolean, readDecisionBoolean, readDecisionChoice, readDecisionScore, servesInferenceKind, } from "./utils/decisionAnswers.js";
|
|
661
|
+
export { decisionBooleanConfidence, decisionKey, gateDecisionBoolean, HOST_DECISION_NAMESPACE, isDecisionQuestion, readDecisionBoolean, readDecisionChoice, readDecisionScore, servesInferenceKind, } from "./utils/decisionAnswers.js";
|
|
662
|
+
// The estimator the pre-flight refusal uses, and the flattening behind
|
|
663
|
+
// `neurolink.decisionLimits()` — exported so a host measures a state with the
|
|
664
|
+
// very function that will judge it.
|
|
665
|
+
export { estimateDecisionStateTokens, resolveDecisionLimitsReading, } from "./utils/decisionLimits.js";
|
|
659
666
|
export { DECISION_PROVIDERS, resolveDefaultDecisionProvider, } from "./factories/providerDescriptors.js";
|
|
660
667
|
export { TYPESAFE_MAX_REQUEST_TOKENS, TYPESAFE_MAX_STATE_TOKENS, } from "./providers/typesafe.js";
|
|
661
668
|
/**
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { CondensationPromptValidation } from "../types/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Check a condensation prompt template for the placeholders Hippocampus
|
|
4
|
+
* expects. Pure — never logs or throws. `valid` is false only when a
|
|
5
|
+
* structural placeholder (`OLD_MEMORY` / `NEW_CONTENT`) is missing;
|
|
6
|
+
* a missing `MAX_WORDS` is reported in `missing` but leaves `valid` true.
|
|
7
|
+
*/
|
|
8
|
+
export declare function validateCondensationPrompt(prompt: string): CondensationPromptValidation;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Placeholders `@juspay/hippocampus` substitutes into a condensation prompt
|
|
3
|
+
* (`replaceAll` in `Hippocampus.add()`). None is required by Hippocampus
|
|
4
|
+
* itself — a missing one simply stays literal — which is exactly why the
|
|
5
|
+
* two structural ones must be checked here: a prompt without
|
|
6
|
+
* `{{NEW_CONTENT}}` produces a memory that never grows, and one without
|
|
7
|
+
* `{{OLD_MEMORY}}` overwrites the whole summary on every turn. Both fail
|
|
8
|
+
* silently at the LLM boundary.
|
|
9
|
+
*/
|
|
10
|
+
const CONDENSATION_PLACEHOLDERS = [
|
|
11
|
+
"OLD_MEMORY",
|
|
12
|
+
"NEW_CONTENT",
|
|
13
|
+
"MAX_WORDS",
|
|
14
|
+
];
|
|
15
|
+
const FATAL_PLACEHOLDERS = new Set([
|
|
16
|
+
"OLD_MEMORY",
|
|
17
|
+
"NEW_CONTENT",
|
|
18
|
+
]);
|
|
19
|
+
/**
|
|
20
|
+
* Check a condensation prompt template for the placeholders Hippocampus
|
|
21
|
+
* expects. Pure — never logs or throws. `valid` is false only when a
|
|
22
|
+
* structural placeholder (`OLD_MEMORY` / `NEW_CONTENT`) is missing;
|
|
23
|
+
* a missing `MAX_WORDS` is reported in `missing` but leaves `valid` true.
|
|
24
|
+
*/
|
|
25
|
+
export function validateCondensationPrompt(prompt) {
|
|
26
|
+
const missing = CONDENSATION_PLACEHOLDERS.filter((name) => !prompt.includes(`{{${name}}}`));
|
|
27
|
+
const fatal = missing.filter((name) => FATAL_PLACEHOLDERS.has(name));
|
|
28
|
+
return { valid: fatal.length === 0, missing, fatal };
|
|
29
|
+
}
|
|
@@ -1,2 +1,14 @@
|
|
|
1
|
-
import type { HippocampusConfig, HippocampusLike } from "../types/index.js";
|
|
2
|
-
|
|
1
|
+
import type { HippocampusClientFactory, HippocampusConfig, HippocampusLike } from "../types/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Construct (or adopt) the Hippocampus client. `config.neurolink.instance`
|
|
4
|
+
* and `config.neurolink.credentials` are read by `@juspay/hippocampus`
|
|
5
|
+
* ≥0.2.0 itself (the peer floor is 0.2.1, which also fixes literal
|
|
6
|
+
* placeholder substitution), so condensation runs through the
|
|
7
|
+
* instance NeuroLink supplies (the one carrying this host's credentials)
|
|
8
|
+
* with no help from this side.
|
|
9
|
+
*
|
|
10
|
+
* Every path that disables memory logs at `error`: warnings are hidden at
|
|
11
|
+
* the default log level, and a host whose memory is silently off has no
|
|
12
|
+
* other way to learn it.
|
|
13
|
+
*/
|
|
14
|
+
export declare function initializeHippocampus(config: HippocampusConfig, injectedClient?: HippocampusLike | HippocampusClientFactory): HippocampusLike | null;
|
|
@@ -25,10 +25,70 @@ function loadHippocampusModule() {
|
|
|
25
25
|
return null;
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
|
-
|
|
28
|
+
/**
|
|
29
|
+
* The client methods NeuroLink core calls: `add()` on the write path and
|
|
30
|
+
* `get()` on the read path. `delete()` and `close()` are on the type for a
|
|
31
|
+
* host's own use, but core never calls them, so a client without them is
|
|
32
|
+
* still usable.
|
|
33
|
+
*/
|
|
34
|
+
const REQUIRED_CLIENT_METHODS = ["add", "get"];
|
|
35
|
+
/**
|
|
36
|
+
* Construct (or adopt) the Hippocampus client. `config.neurolink.instance`
|
|
37
|
+
* and `config.neurolink.credentials` are read by `@juspay/hippocampus`
|
|
38
|
+
* ≥0.2.0 itself (the peer floor is 0.2.1, which also fixes literal
|
|
39
|
+
* placeholder substitution), so condensation runs through the
|
|
40
|
+
* instance NeuroLink supplies (the one carrying this host's credentials)
|
|
41
|
+
* with no help from this side.
|
|
42
|
+
*
|
|
43
|
+
* Every path that disables memory logs at `error`: warnings are hidden at
|
|
44
|
+
* the default log level, and a host whose memory is silently off has no
|
|
45
|
+
* other way to learn it.
|
|
46
|
+
*/
|
|
47
|
+
export function initializeHippocampus(config, injectedClient) {
|
|
48
|
+
if (injectedClient) {
|
|
49
|
+
try {
|
|
50
|
+
const client = typeof injectedClient === "function"
|
|
51
|
+
? injectedClient(config)
|
|
52
|
+
: injectedClient;
|
|
53
|
+
// A loosely typed or async factory can hand back undefined or a
|
|
54
|
+
// Promise; cache neither. A partial client — a write-only double with
|
|
55
|
+
// no get() — would make every read fail silently, so it is refused
|
|
56
|
+
// here rather than on each call. Memory is disabled with a clear
|
|
57
|
+
// message instead of failing every call with a generic error.
|
|
58
|
+
//
|
|
59
|
+
// The Promise case falls through to the same "missing" check below
|
|
60
|
+
// (a thenable has none of the required methods), but a rejecting
|
|
61
|
+
// factory's Promise needs a handler attached NOW — nothing else ever
|
|
62
|
+
// awaits it, so an unhandled rejection would otherwise end the
|
|
63
|
+
// process under the default Node rejection policy.
|
|
64
|
+
const maybeThenable = client;
|
|
65
|
+
if (client &&
|
|
66
|
+
typeof client === "object" &&
|
|
67
|
+
typeof maybeThenable.then === "function") {
|
|
68
|
+
maybeThenable.then(undefined, () => {
|
|
69
|
+
logger.error("[memoryInitializer] Host-supplied async client factory rejected");
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
const missing = !client || typeof client !== "object"
|
|
73
|
+
? [...REQUIRED_CLIENT_METHODS]
|
|
74
|
+
: REQUIRED_CLIENT_METHODS.filter((name) => typeof client[name] !== "function");
|
|
75
|
+
if (missing.length > 0) {
|
|
76
|
+
logger.error(`[memoryInitializer] Host-supplied client has no ${missing
|
|
77
|
+
.map((name) => `${name}()`)
|
|
78
|
+
.join(" / ")}; disabling memory`);
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
logger.info("[memoryInitializer] Memory initialized with host-supplied client", { fromFactory: typeof injectedClient === "function" });
|
|
82
|
+
return client;
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
logger.error("[memoryInitializer] Host-supplied client factory threw; disabling memory", { error: error instanceof Error ? error.message : String(error) });
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
29
89
|
const mod = loadHippocampusModule();
|
|
30
90
|
if (!mod) {
|
|
31
|
-
logger.
|
|
91
|
+
logger.error("[memoryInitializer] Memory configuration provided but @juspay/hippocampus is not installed. Run `pnpm add @juspay/hippocampus` (or your package manager equivalent) to enable memory.");
|
|
32
92
|
return null;
|
|
33
93
|
}
|
|
34
94
|
try {
|
|
@@ -37,11 +97,12 @@ export function initializeHippocampus(config) {
|
|
|
37
97
|
storageType: config.storage?.type || "sqlite",
|
|
38
98
|
maxWords: config.maxWords || 50,
|
|
39
99
|
hasCustomPrompt: !!config.prompt,
|
|
100
|
+
condenserSupplied: config.neurolink?.instance !== undefined,
|
|
40
101
|
});
|
|
41
102
|
return instance;
|
|
42
103
|
}
|
|
43
104
|
catch (error) {
|
|
44
|
-
logger.
|
|
105
|
+
logger.error("[memoryInitializer] Failed to initialize memory; disabling", {
|
|
45
106
|
error: error instanceof Error ? error.message : String(error),
|
|
46
107
|
});
|
|
47
108
|
return null;
|