@juspay/neurolink 12.49.20 → 12.51.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/browser/neurolink.min.js +671 -671
- package/dist/cli/commands/setup.d.ts +7 -5
- package/dist/cli/commands/setup.js +10 -8
- package/dist/constants/enums.d.ts +7 -15
- package/dist/constants/enums.js +8 -16
- package/dist/core/conversationMemoryManager.js +10 -4
- package/dist/core/redisConversationMemoryManager.js +6 -5
- package/dist/factories/providerDescriptors.d.ts +17 -3
- package/dist/factories/providerDescriptors.js +81 -56
- package/dist/factories/providerRegistry.d.ts +9 -1
- package/dist/factories/providerRegistry.js +19 -9
- package/dist/models/manifestRegistry.js +40 -3
- package/dist/neurolink.d.ts +52 -1
- package/dist/neurolink.js +195 -0
- package/dist/providers/catalog/index.generated.d.ts +3 -1
- package/dist/providers/catalog/index.generated.js +11 -0
- package/dist/providers/catalog/laya.json +107 -0
- package/dist/providers/catalog/loader.d.ts +7 -4
- package/dist/providers/catalog/loader.js +10 -3
- package/dist/providers/catalog/provider-catalog.schema.json +218 -11
- package/dist/providers/catalog/schema.d.ts +11 -9
- package/dist/providers/catalog/schema.js +333 -59
- package/dist/providers/configuredDecision.d.ts +23 -0
- package/dist/providers/configuredDecision.js +111 -0
- package/dist/providers/decide/baseUrl.d.ts +2 -0
- package/dist/providers/decide/baseUrl.js +14 -0
- package/dist/providers/decide/errors.d.ts +8 -0
- package/dist/providers/decide/errors.js +57 -0
- package/dist/providers/decide/formatters.d.ts +2 -0
- package/dist/providers/decide/formatters.js +4 -0
- package/dist/providers/decide/paths.d.ts +1 -0
- package/dist/providers/decide/paths.js +21 -0
- package/dist/providers/decide/template.d.ts +3 -0
- package/dist/providers/decide/template.js +13 -0
- package/dist/types/conversation.d.ts +109 -0
- package/dist/types/livekit.d.ts +246 -3
- package/dist/types/providerCatalog.d.ts +74 -1
- package/dist/types/providerCatalog.generated.d.ts +2 -2
- package/dist/types/providers.d.ts +12 -9
- package/dist/types/sdk.d.ts +1 -1
- package/dist/types/stream.d.ts +8 -1
- package/dist/utils/conversationTurnIdentity.d.ts +24 -0
- package/dist/utils/conversationTurnIdentity.js +81 -0
- package/dist/utils/modelChoices.js +11 -25
- package/dist/utils/providerConfig.d.ts +0 -5
- package/dist/utils/providerConfig.js +0 -17
- package/dist/voice/livekit/brain.js +10 -7
- package/dist/voice/livekit/index.d.ts +2 -0
- package/dist/voice/livekit/index.js +2 -0
- package/dist/voice/livekit/memoryBinding.d.ts +47 -0
- package/dist/voice/livekit/memoryBinding.js +152 -0
- package/dist/voice/livekit/realtimeMcpTools.js +20 -1
- package/dist/voice/livekit/realtimeMemory.d.ts +22 -0
- package/dist/voice/livekit/realtimeMemory.js +111 -0
- package/dist/voice/livekit/realtimeVoiceAgent.js +40 -1
- package/dist/voice/livekit/turnRecorder.d.ts +38 -0
- package/dist/voice/livekit/turnRecorder.js +362 -0
- package/dist/voice/livekit/voiceAgent.js +43 -5
- package/docs-site/static/search-index.json +8 -7
- package/package.json +7 -2
- package/dist/models/manifests/laya.d.ts +0 -12
- package/dist/models/manifests/laya.js +0 -54
- package/dist/providers/laya.d.ts +0 -40
- package/dist/providers/laya.js +0 -152
package/dist/neurolink.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* Uses real MCP infrastructure for tool discovery and execution.
|
|
7
7
|
*/
|
|
8
8
|
import type { DecisionCallerOptions, DecisionLimitsQuery, DecisionLimitsReading, DecisionOptions, DecisionResult, AgentDefinition, AgentNetworkConfig, AgentRunOptions, AgentRunOutcome, AgentToolRegistrationOptions, IsolatedAgentDefinition, NetworkExecutionInput, NetworkExecutionOptions, NetworkExecutionResult, NetworkStreamChunk, WorkerInstanceOptions } from "./types/index.js";
|
|
9
|
-
import type { CompactionConfig, CompactionResult, ArtifactStore, SpanData, ObservabilityConfig, MetricsSummary, MCPToolAnnotations, TraceView, AuthenticatedContext, AuthProvider, JsonObject, NeuroLinkEvents, TypedEventEmitter, MCPEnhancementsConfig, NeuroLinkAuthConfig, NeurolinkConstructorConfig, ChatMessage, ExternalMCPOperationResult, ExternalMCPServerInstance, ExternalMCPServerHealth, ExternalMCPToolInfo, GenerateOptions, GenerateResult, ProviderStatus, TextGenerationOptions, TextGenerationResult, MCPExecutableTool, MCPServerInfo, MCPStatus, StreamOptions, StreamResult, ToolExecutionContext, ToolExecutionSummary, ToolInfo, ToolRegistrationOptions, BatchOperationResult, StreamGenerationEndContext, ToolRoutingServerDescriptor, ToolDedupConfig, ToolConfig, KnowledgeEngineStatus, ChecklistState, ArtifactPageRequest, BankArtifactOptions, BankedArtifactRef, DelegateCollectRequest, DelegateCollectResult, DelegateHandle, DelegateRegistrationOptions, DelegateSpawnOptions, BackgroundCommandHandle, BackgroundCommandOptions, BackgroundCommandOutputPage, BackgroundCommandPageRequest, BackgroundCommandPolicy, BackgroundCommandStatus, GitToolResult, GitToolsetOptions } from "./types/index.js";
|
|
9
|
+
import type { CompactionConfig, CompactionResult, ArtifactStore, SpanData, ObservabilityConfig, MetricsSummary, MCPToolAnnotations, TraceView, AuthenticatedContext, AuthProvider, JsonObject, NeuroLinkEvents, TypedEventEmitter, MCPEnhancementsConfig, NeuroLinkAuthConfig, NeurolinkConstructorConfig, ChatMessage, ConversationSeedMessage, ConversationSeedOptions, RecordConversationTurnOptions, RecordConversationTurnResult, ExternalMCPOperationResult, ExternalMCPServerInstance, ExternalMCPServerHealth, ExternalMCPToolInfo, GenerateOptions, GenerateResult, ProviderStatus, TextGenerationOptions, TextGenerationResult, MCPExecutableTool, MCPServerInfo, MCPStatus, StreamOptions, StreamResult, ToolExecutionContext, ToolExecutionSummary, ToolInfo, ToolRegistrationOptions, BatchOperationResult, StreamGenerationEndContext, ToolRoutingServerDescriptor, ToolDedupConfig, ToolConfig, KnowledgeEngineStatus, ChecklistState, ArtifactPageRequest, BankArtifactOptions, BankedArtifactRef, DelegateCollectRequest, DelegateCollectResult, DelegateHandle, DelegateRegistrationOptions, DelegateSpawnOptions, BackgroundCommandHandle, BackgroundCommandOptions, BackgroundCommandOutputPage, BackgroundCommandPageRequest, BackgroundCommandPolicy, BackgroundCommandStatus, GitToolResult, GitToolsetOptions } from "./types/index.js";
|
|
10
10
|
import { ConversationMemoryManager } from "./core/conversationMemoryManager.js";
|
|
11
11
|
import type { RedisConversationMemoryManager } from "./core/redisConversationMemoryManager.js";
|
|
12
12
|
import { ExternalServerManager } from "./mcp/externalServerManager.js";
|
|
@@ -2120,6 +2120,57 @@ export declare class NeuroLink {
|
|
|
2120
2120
|
* @returns Array of ChatMessage objects, or empty array if session doesn't exist
|
|
2121
2121
|
*/
|
|
2122
2122
|
getSessionMessages(sessionId: string, userId?: string): Promise<ChatMessage[]>;
|
|
2123
|
+
/**
|
|
2124
|
+
* Record one finished turn from a transport that does not go through
|
|
2125
|
+
* generate()/stream() — a voice call — into the same conversation memory
|
|
2126
|
+
* typed chat uses: same key, same manager, same summarization and title
|
|
2127
|
+
* handling, same optional long-term memory write.
|
|
2128
|
+
*
|
|
2129
|
+
* Identity is never defaulted: `userId` is required, because typed chat falls
|
|
2130
|
+
* back to a shared placeholder user when it is missing and a spoken turn must
|
|
2131
|
+
* never be filed there. Recording the same `turnId` twice stores it once, so
|
|
2132
|
+
* a caller can retry after an ambiguous failure.
|
|
2133
|
+
*
|
|
2134
|
+
* A turn with a blank user message, or a blank response and no tool events,
|
|
2135
|
+
* is not stored (an empty assistant message makes some providers reject the
|
|
2136
|
+
* next request); the result says which. Storage failures throw — the caller
|
|
2137
|
+
* decides whether to retry or to carry on without the turn.
|
|
2138
|
+
*
|
|
2139
|
+
* @throws NeuroLinkError (INVALID_PARAMETERS) for a bad sessionId or userId
|
|
2140
|
+
* @throws NeuroLinkError (MISSING_CONFIGURATION) when conversation memory is not enabled
|
|
2141
|
+
* @throws ConversationMemoryError (STORAGE_ERROR) when the store rejects the write
|
|
2142
|
+
*/
|
|
2143
|
+
recordConversationTurn(options: RecordConversationTurnOptions): Promise<RecordConversationTurnResult>;
|
|
2144
|
+
/**
|
|
2145
|
+
* The most recent user/assistant messages of a conversation, bounded, for
|
|
2146
|
+
* seeding a model that has no history of its own — a voice model at call
|
|
2147
|
+
* start. Summaries, pinned skills, tool traffic and blank messages are left
|
|
2148
|
+
* out; the oldest messages are dropped first when `maxChars` would be
|
|
2149
|
+
* exceeded. Best effort: a missing conversation, disabled memory or a read
|
|
2150
|
+
* failure yields an empty seed and never throws, because a call must start
|
|
2151
|
+
* either way.
|
|
2152
|
+
*/
|
|
2153
|
+
getConversationSeed(options: ConversationSeedOptions): Promise<ConversationSeedMessage[]>;
|
|
2154
|
+
/**
|
|
2155
|
+
* Whether a conversation already holds messages under this user. A client
|
|
2156
|
+
* may name the conversation a call appends to; the server accepts the name
|
|
2157
|
+
* only when this is true for the user the session is verified as.
|
|
2158
|
+
*/
|
|
2159
|
+
conversationExists(options: {
|
|
2160
|
+
sessionId: string;
|
|
2161
|
+
userId: string;
|
|
2162
|
+
}): Promise<boolean>;
|
|
2163
|
+
/**
|
|
2164
|
+
* Whether a stored conversation is owned by someone other than `userId`.
|
|
2165
|
+
*
|
|
2166
|
+
* The Redis manager includes the user in the key, so a different user simply
|
|
2167
|
+
* never finds the conversation. The in-process manager keys sessions by id
|
|
2168
|
+
* alone and only records the owner, so ownership is checked here: a session
|
|
2169
|
+
* that exists with no recorded owner, or another one, is not `userId`'s.
|
|
2170
|
+
*/
|
|
2171
|
+
private isOwnedByAnotherUser;
|
|
2172
|
+
/** Shared identity check for the voice-memory methods. */
|
|
2173
|
+
private static assertConversationIdentity;
|
|
2123
2174
|
/**
|
|
2124
2175
|
* Replace the entire messages array for a session.
|
|
2125
2176
|
* @param sessionId - The session ID to update
|
package/dist/neurolink.js
CHANGED
|
@@ -9342,6 +9342,9 @@ Current user's request: ${currentInput}`;
|
|
|
9342
9342
|
...(pendingSkillMessages.length > 0
|
|
9343
9343
|
? { skillMessages: pendingSkillMessages }
|
|
9344
9344
|
: {}),
|
|
9345
|
+
...(enhancedOptions.modality
|
|
9346
|
+
? { modality: enhancedOptions.modality }
|
|
9347
|
+
: {}),
|
|
9345
9348
|
});
|
|
9346
9349
|
this.recordMemorySpan("memory.store", { "memory.operation": "store", "memory.path": "stream" }, Date.now() - memStoreStart, SpanStatus.OK);
|
|
9347
9350
|
logger.debug("[NeuroLink.stream] Stored conversation turn with events", {
|
|
@@ -12757,6 +12760,198 @@ Current user's request: ${currentInput}`;
|
|
|
12757
12760
|
}
|
|
12758
12761
|
return await this.conversationMemory.getSessionMessages(sessionId, userId);
|
|
12759
12762
|
}
|
|
12763
|
+
/**
|
|
12764
|
+
* Record one finished turn from a transport that does not go through
|
|
12765
|
+
* generate()/stream() — a voice call — into the same conversation memory
|
|
12766
|
+
* typed chat uses: same key, same manager, same summarization and title
|
|
12767
|
+
* handling, same optional long-term memory write.
|
|
12768
|
+
*
|
|
12769
|
+
* Identity is never defaulted: `userId` is required, because typed chat falls
|
|
12770
|
+
* back to a shared placeholder user when it is missing and a spoken turn must
|
|
12771
|
+
* never be filed there. Recording the same `turnId` twice stores it once, so
|
|
12772
|
+
* a caller can retry after an ambiguous failure.
|
|
12773
|
+
*
|
|
12774
|
+
* A turn with a blank user message, or a blank response and no tool events,
|
|
12775
|
+
* is not stored (an empty assistant message makes some providers reject the
|
|
12776
|
+
* next request); the result says which. Storage failures throw — the caller
|
|
12777
|
+
* decides whether to retry or to carry on without the turn.
|
|
12778
|
+
*
|
|
12779
|
+
* @throws NeuroLinkError (INVALID_PARAMETERS) for a bad sessionId or userId
|
|
12780
|
+
* @throws NeuroLinkError (MISSING_CONFIGURATION) when conversation memory is not enabled
|
|
12781
|
+
* @throws ConversationMemoryError (STORAGE_ERROR) when the store rejects the write
|
|
12782
|
+
*/
|
|
12783
|
+
async recordConversationTurn(options) {
|
|
12784
|
+
const { sessionId, userId, turnId } = options;
|
|
12785
|
+
NeuroLink.assertConversationIdentity(sessionId, userId);
|
|
12786
|
+
const identity = { sessionId, userId, ...(turnId ? { turnId } : {}) };
|
|
12787
|
+
if (!options.userMessage.trim()) {
|
|
12788
|
+
return { recorded: false, reason: "empty-user-message", ...identity };
|
|
12789
|
+
}
|
|
12790
|
+
const hasToolEvents = (options.events ?? []).some((event) => event.type === "tool:start" || event.type === "tool:end");
|
|
12791
|
+
if (!options.aiResponse.trim() && !hasToolEvents) {
|
|
12792
|
+
return { recorded: false, reason: "empty-response", ...identity };
|
|
12793
|
+
}
|
|
12794
|
+
await this.initializeConversationMemoryForGeneration(options.requestId ?? `record-turn-${Date.now()}`, Date.now(), process.hrtime.bigint());
|
|
12795
|
+
if (!this.conversationMemory) {
|
|
12796
|
+
throw new NeuroLinkError({
|
|
12797
|
+
code: ERROR_CODES.MISSING_CONFIGURATION,
|
|
12798
|
+
message: "Conversation memory is not enabled",
|
|
12799
|
+
category: ErrorCategory.VALIDATION,
|
|
12800
|
+
severity: ErrorSeverity.HIGH,
|
|
12801
|
+
retriable: false,
|
|
12802
|
+
});
|
|
12803
|
+
}
|
|
12804
|
+
if (await this.isOwnedByAnotherUser(sessionId, userId)) {
|
|
12805
|
+
throw new NeuroLinkError({
|
|
12806
|
+
code: ERROR_CODES.INVALID_PARAMETERS,
|
|
12807
|
+
message: "The conversation belongs to another user",
|
|
12808
|
+
category: ErrorCategory.VALIDATION,
|
|
12809
|
+
severity: ErrorSeverity.MEDIUM,
|
|
12810
|
+
retriable: false,
|
|
12811
|
+
});
|
|
12812
|
+
}
|
|
12813
|
+
const memStoreStart = Date.now();
|
|
12814
|
+
try {
|
|
12815
|
+
await this.conversationMemory.storeConversationTurn({
|
|
12816
|
+
sessionId,
|
|
12817
|
+
userId,
|
|
12818
|
+
userMessage: options.userMessage,
|
|
12819
|
+
aiResponse: options.aiResponse,
|
|
12820
|
+
startTimeStamp: options.startTime,
|
|
12821
|
+
...(options.provider && options.model
|
|
12822
|
+
? {
|
|
12823
|
+
providerDetails: {
|
|
12824
|
+
provider: options.provider,
|
|
12825
|
+
model: options.model,
|
|
12826
|
+
},
|
|
12827
|
+
}
|
|
12828
|
+
: {}),
|
|
12829
|
+
enableSummarization: options.enableSummarization,
|
|
12830
|
+
events: options.events?.length ? options.events : undefined,
|
|
12831
|
+
requestId: options.requestId,
|
|
12832
|
+
...(options.modality ? { modality: options.modality } : {}),
|
|
12833
|
+
...(turnId ? { turnId } : {}),
|
|
12834
|
+
...(options.interrupted ? { interrupted: true } : {}),
|
|
12835
|
+
...(options.callEnded ? { callEnded: true } : {}),
|
|
12836
|
+
});
|
|
12837
|
+
this.recordMemorySpan("memory.store", { "memory.operation": "store", "memory.path": "record" }, Date.now() - memStoreStart, SpanStatus.OK);
|
|
12838
|
+
}
|
|
12839
|
+
catch (error) {
|
|
12840
|
+
this.recordMemorySpan("memory.store", { "memory.operation": "store", "memory.path": "record" }, Date.now() - memStoreStart, SpanStatus.ERROR, error instanceof Error ? error.message : String(error));
|
|
12841
|
+
throw error;
|
|
12842
|
+
}
|
|
12843
|
+
if (this.shouldWriteMemory(options.memory, userId, options.aiResponse)) {
|
|
12844
|
+
this.storeMemoryInBackground({
|
|
12845
|
+
prompt: options.userMessage,
|
|
12846
|
+
response: options.aiResponse.trim(),
|
|
12847
|
+
userId,
|
|
12848
|
+
sessionId,
|
|
12849
|
+
provider: options.provider ?? "",
|
|
12850
|
+
model: options.model,
|
|
12851
|
+
}, options.memory, { sessionId });
|
|
12852
|
+
}
|
|
12853
|
+
return { recorded: true, ...identity };
|
|
12854
|
+
}
|
|
12855
|
+
/**
|
|
12856
|
+
* The most recent user/assistant messages of a conversation, bounded, for
|
|
12857
|
+
* seeding a model that has no history of its own — a voice model at call
|
|
12858
|
+
* start. Summaries, pinned skills, tool traffic and blank messages are left
|
|
12859
|
+
* out; the oldest messages are dropped first when `maxChars` would be
|
|
12860
|
+
* exceeded. Best effort: a missing conversation, disabled memory or a read
|
|
12861
|
+
* failure yields an empty seed and never throws, because a call must start
|
|
12862
|
+
* either way.
|
|
12863
|
+
*/
|
|
12864
|
+
async getConversationSeed(options) {
|
|
12865
|
+
const { sessionId, userId } = options;
|
|
12866
|
+
const maxTurns = Math.max(0, Math.floor(options.maxTurns ?? 10));
|
|
12867
|
+
const maxChars = Math.max(0, Math.floor(options.maxChars ?? 6000));
|
|
12868
|
+
try {
|
|
12869
|
+
NeuroLink.assertConversationIdentity(sessionId, userId);
|
|
12870
|
+
const stored = await this.getSessionMessages(sessionId, userId);
|
|
12871
|
+
const spoken = [];
|
|
12872
|
+
for (const message of stored) {
|
|
12873
|
+
if ((message.role !== "user" && message.role !== "assistant") ||
|
|
12874
|
+
message.metadata?.isSummary ||
|
|
12875
|
+
message.metadata?.isSkill ||
|
|
12876
|
+
typeof message.content !== "string" ||
|
|
12877
|
+
!message.content.trim()) {
|
|
12878
|
+
continue;
|
|
12879
|
+
}
|
|
12880
|
+
spoken.push({
|
|
12881
|
+
role: message.role,
|
|
12882
|
+
content: message.content,
|
|
12883
|
+
...(message.metadata?.modality
|
|
12884
|
+
? { modality: message.metadata.modality }
|
|
12885
|
+
: {}),
|
|
12886
|
+
});
|
|
12887
|
+
}
|
|
12888
|
+
const recent = spoken.slice(-(maxTurns * 2));
|
|
12889
|
+
let used = 0;
|
|
12890
|
+
let firstKept = recent.length;
|
|
12891
|
+
for (let index = recent.length - 1; index >= 0; index -= 1) {
|
|
12892
|
+
used += recent[index].content.length;
|
|
12893
|
+
if (used > maxChars) {
|
|
12894
|
+
break;
|
|
12895
|
+
}
|
|
12896
|
+
firstKept = index;
|
|
12897
|
+
}
|
|
12898
|
+
return recent.slice(firstKept);
|
|
12899
|
+
}
|
|
12900
|
+
catch (error) {
|
|
12901
|
+
logger.warn("[NeuroLink] getConversationSeed failed, starting unseeded", {
|
|
12902
|
+
error: error instanceof Error ? error.message : String(error),
|
|
12903
|
+
});
|
|
12904
|
+
return [];
|
|
12905
|
+
}
|
|
12906
|
+
}
|
|
12907
|
+
/**
|
|
12908
|
+
* Whether a conversation already holds messages under this user. A client
|
|
12909
|
+
* may name the conversation a call appends to; the server accepts the name
|
|
12910
|
+
* only when this is true for the user the session is verified as.
|
|
12911
|
+
*/
|
|
12912
|
+
async conversationExists(options) {
|
|
12913
|
+
NeuroLink.assertConversationIdentity(options.sessionId, options.userId);
|
|
12914
|
+
const stored = await this.getSessionMessages(options.sessionId, options.userId);
|
|
12915
|
+
if (stored.length === 0) {
|
|
12916
|
+
return false;
|
|
12917
|
+
}
|
|
12918
|
+
return !(await this.isOwnedByAnotherUser(options.sessionId, options.userId));
|
|
12919
|
+
}
|
|
12920
|
+
/**
|
|
12921
|
+
* Whether a stored conversation is owned by someone other than `userId`.
|
|
12922
|
+
*
|
|
12923
|
+
* The Redis manager includes the user in the key, so a different user simply
|
|
12924
|
+
* never finds the conversation. The in-process manager keys sessions by id
|
|
12925
|
+
* alone and only records the owner, so ownership is checked here: a session
|
|
12926
|
+
* that exists with no recorded owner, or another one, is not `userId`'s.
|
|
12927
|
+
*/
|
|
12928
|
+
async isOwnedByAnotherUser(sessionId, userId) {
|
|
12929
|
+
const memory = this.conversationMemory;
|
|
12930
|
+
if (!memory || memory.isRedisConfigured) {
|
|
12931
|
+
return false;
|
|
12932
|
+
}
|
|
12933
|
+
const session = await memory.getSession(sessionId);
|
|
12934
|
+
return session !== undefined && session.userId !== userId;
|
|
12935
|
+
}
|
|
12936
|
+
/** Shared identity check for the voice-memory methods. */
|
|
12937
|
+
static assertConversationIdentity(sessionId, userId) {
|
|
12938
|
+
const problem = typeof sessionId !== "string" || !sessionId.trim()
|
|
12939
|
+
? "sessionId must be a non-empty string"
|
|
12940
|
+
: sessionId.includes(":")
|
|
12941
|
+
? 'sessionId must not contain ":"'
|
|
12942
|
+
: typeof userId !== "string" || !userId.trim()
|
|
12943
|
+
? "userId must be a non-empty string"
|
|
12944
|
+
: undefined;
|
|
12945
|
+
if (problem) {
|
|
12946
|
+
throw new NeuroLinkError({
|
|
12947
|
+
code: ERROR_CODES.INVALID_PARAMETERS,
|
|
12948
|
+
message: problem,
|
|
12949
|
+
category: ErrorCategory.VALIDATION,
|
|
12950
|
+
severity: ErrorSeverity.MEDIUM,
|
|
12951
|
+
retriable: false,
|
|
12952
|
+
});
|
|
12953
|
+
}
|
|
12954
|
+
}
|
|
12760
12955
|
/**
|
|
12761
12956
|
* Replace the entire messages array for a session.
|
|
12762
12957
|
* @param sessionId - The session ID to update
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
import type { ProviderCatalogJson } from "../../types/index.js";
|
|
1
|
+
import type { DecideCatalogJson, ProviderCatalogJson } from "../../types/index.js";
|
|
2
2
|
export declare const CATALOG_JSON_ENTRIES: ProviderCatalogJson[];
|
|
3
3
|
export declare const CATALOG_PROVIDER_IDS: readonly ["a2agent", "abliteration", "above-dev", "ai21", "aiand", "aihubmix", "aionlabs", "ambient", "api-route", "arcee", "atlas-cloud", "avian-io", "baidu-qianfan", "baseten", "bee-heossi", "byteplus-modelark", "bytez", "cerebras", "charm-hyper", "chutes", "cloudflare", "crusoe", "dashscope", "deepinfra", "deepseek", "empiriolabs", "featherless-ai", "fireworks", "friendli", "gmicloud", "gradientai", "groq", "hetzner-inference", "huggingface", "inception-labs", "inceptron", "inco", "inference-net", "io-intelligence", "koscompute", "lemonfox-ai", "lilac", "llmtech", "mancer", "meta-model-api", "minimax", "mistral", "moark", "modelscope", "moonshot-ai", "morph", "naver-clova-studio", "nebius", "neuralwatt", "novita", "ovhcloud", "parasail", "pareto-inference", "perplexity", "poolside", "prime-intellect", "reka", "sakana-ai", "sambanova", "sarvam", "scaleway", "siliconflow", "stackit", "stepfun", "subconscious", "synthetic", "telnyx", "thinking-machines", "tinfoil", "together-ai", "umans-ai", "upstage", "venice-ai", "vispark", "vultr-inference", "wafer", "wandb-inference", "xai", "z-ai"];
|
|
4
|
+
export declare const DECIDE_CATALOG_JSON_ENTRIES: DecideCatalogJson[];
|
|
5
|
+
export declare const DECIDE_CATALOG_PROVIDER_IDS: readonly ["laya"];
|
|
@@ -40,6 +40,7 @@ import incoJson from "./inco.json" with { type: "json" };
|
|
|
40
40
|
import inferenceNetJson from "./inference-net.json" with { type: "json" };
|
|
41
41
|
import ioIntelligenceJson from "./io-intelligence.json" with { type: "json" };
|
|
42
42
|
import koscomputeJson from "./koscompute.json" with { type: "json" };
|
|
43
|
+
import layaJson from "./laya.json" with { type: "json" };
|
|
43
44
|
import lemonfoxAiJson from "./lemonfox-ai.json" with { type: "json" };
|
|
44
45
|
import lilacJson from "./lilac.json" with { type: "json" };
|
|
45
46
|
import llmtechJson from "./llmtech.json" with { type: "json" };
|
|
@@ -84,6 +85,14 @@ import waferJson from "./wafer.json" with { type: "json" };
|
|
|
84
85
|
import wandbInferenceJson from "./wandb-inference.json" with { type: "json" };
|
|
85
86
|
import xaiJson from "./xai.json" with { type: "json" };
|
|
86
87
|
import zAiJson from "./z-ai.json" with { type: "json" };
|
|
88
|
+
import { isDecideCatalogEntry, parseCatalogEntry } from "./schema.js";
|
|
89
|
+
const asDecideEntry = (raw) => {
|
|
90
|
+
const entry = parseCatalogEntry(raw);
|
|
91
|
+
if (!isDecideCatalogEntry(entry)) {
|
|
92
|
+
throw new Error("a catalog file listed as a decide entry parsed as a chat entry");
|
|
93
|
+
}
|
|
94
|
+
return entry;
|
|
95
|
+
};
|
|
87
96
|
export const CATALOG_JSON_ENTRIES = [
|
|
88
97
|
a2agentJson,
|
|
89
98
|
abliterationJson,
|
|
@@ -256,3 +265,5 @@ export const CATALOG_PROVIDER_IDS = [
|
|
|
256
265
|
"xai",
|
|
257
266
|
"z-ai",
|
|
258
267
|
];
|
|
268
|
+
export const DECIDE_CATALOG_JSON_ENTRIES = [layaJson].map(asDecideEntry);
|
|
269
|
+
export const DECIDE_CATALOG_PROVIDER_IDS = ["laya"];
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./provider-catalog.schema.json",
|
|
3
|
+
"id": "laya",
|
|
4
|
+
"displayName": "Laya",
|
|
5
|
+
"aliases": [],
|
|
6
|
+
"inferenceKinds": ["decide"],
|
|
7
|
+
"wire": {},
|
|
8
|
+
"models": {
|
|
9
|
+
"default": "typed-decisions",
|
|
10
|
+
"topModels": ["typed-decisions", "multilingual", "english", "auto"],
|
|
11
|
+
"catalog": {
|
|
12
|
+
"typed-decisions": {
|
|
13
|
+
"contextWindow": 1024,
|
|
14
|
+
"maxOutputTokens": 256,
|
|
15
|
+
"vision": false,
|
|
16
|
+
"status": "production",
|
|
17
|
+
"description": "Recommended - Laya fine-tuned for typed decisions"
|
|
18
|
+
},
|
|
19
|
+
"english": {
|
|
20
|
+
"contextWindow": 512,
|
|
21
|
+
"maxOutputTokens": 256,
|
|
22
|
+
"vision": false,
|
|
23
|
+
"status": "production",
|
|
24
|
+
"description": "Laya, English base checkpoint (512-token window)"
|
|
25
|
+
},
|
|
26
|
+
"multilingual": {
|
|
27
|
+
"contextWindow": 1024,
|
|
28
|
+
"maxOutputTokens": 256,
|
|
29
|
+
"vision": false,
|
|
30
|
+
"status": "production",
|
|
31
|
+
"description": "Laya, 100+ languages"
|
|
32
|
+
},
|
|
33
|
+
"auto": {
|
|
34
|
+
"contextWindow": 512,
|
|
35
|
+
"maxOutputTokens": 256,
|
|
36
|
+
"vision": false,
|
|
37
|
+
"status": "production",
|
|
38
|
+
"description": "Let Laya pick a checkpoint by language"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"timeouts": { "decideMs": 5000 },
|
|
43
|
+
"decisionLimits": {
|
|
44
|
+
"maxStateTokens": 320,
|
|
45
|
+
"maxQuestions": 64,
|
|
46
|
+
"nonAsciiTokensPerChar": 1.5,
|
|
47
|
+
"models": {
|
|
48
|
+
"typed-decisions": { "maxStateTokens": 768 },
|
|
49
|
+
"multilingual": { "maxStateTokens": 768, "nonAsciiTokensPerChar": 0.6 },
|
|
50
|
+
"english": { "maxStateTokens": 320 },
|
|
51
|
+
"auto": { "maxStateTokens": 320 }
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"decide": {
|
|
55
|
+
"dialect": "system-one",
|
|
56
|
+
"order": 2,
|
|
57
|
+
"baseURLRequired": true,
|
|
58
|
+
"baseURLNormalize": ["strip-trailing-slash"],
|
|
59
|
+
"endpoint": "{baseURL}/predict",
|
|
60
|
+
"model": { "placement": "body-unless-alias", "alias": "auto" },
|
|
61
|
+
"requestIdHeaders": ["x-litellm-call-id"],
|
|
62
|
+
"response": { "modelPaths": ["routing.model", "model"] },
|
|
63
|
+
"errors": {
|
|
64
|
+
"rules": [
|
|
65
|
+
{ "status": [401, 403], "kind": "authentication" },
|
|
66
|
+
{ "status": 413, "kind": "max_tokens_exceeded" },
|
|
67
|
+
{ "status": 429, "kind": "rate_limit" },
|
|
68
|
+
{ "status": 503, "kind": "overloaded" },
|
|
69
|
+
{ "statusMin": 500, "kind": "server" }
|
|
70
|
+
],
|
|
71
|
+
"default": "invalid_request",
|
|
72
|
+
"bodyShapes": [
|
|
73
|
+
{ "path": "error.message" },
|
|
74
|
+
{
|
|
75
|
+
"path": "detail",
|
|
76
|
+
"isArray": true,
|
|
77
|
+
"formatter": "fastapi-validation",
|
|
78
|
+
"kind": "invalid_request",
|
|
79
|
+
"retryable": false
|
|
80
|
+
},
|
|
81
|
+
{ "path": "detail" }
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
"setup": {
|
|
86
|
+
"url": "https://github.com/NandhaKishorM/laya",
|
|
87
|
+
"apiKeyFormat": null,
|
|
88
|
+
"billingPolicy": "free-tier",
|
|
89
|
+
"description": "API key for your Laya endpoint",
|
|
90
|
+
"instructions": [
|
|
91
|
+
"1. Run a Laya server, or use a LiteLLM proxy with a pass-through route to one",
|
|
92
|
+
"2. Set LAYA_BASE_URL to its base URL (NeuroLink calls <LAYA_BASE_URL>/predict)",
|
|
93
|
+
"3. Set {apiKeyEnvVar} to the key it accepts (on LiteLLM, a virtual key with that route in its Allowed Routes)"
|
|
94
|
+
]
|
|
95
|
+
},
|
|
96
|
+
"evidence": {
|
|
97
|
+
"rosterVerified": {
|
|
98
|
+
"date": "2026-09-24",
|
|
99
|
+
"method": "Checkpoint ids typed-decisions, multilingual and english as listed in docs/getting-started/providers/laya.md and answered by a live Laya 0.3.5 server (routing.model); auto sends no model and lets Laya's router choose"
|
|
100
|
+
},
|
|
101
|
+
"liveMatrix": {
|
|
102
|
+
"date": "2026-09-24",
|
|
103
|
+
"result": "A 60,000-character state was answered 200 on a 1,024-token window, so Laya truncates silently and decisionLimits refuses over-window input (768 state tokens on typed-decisions and multilingual, 320 on english and auto). Chinese measured about 1.4 tokens per character on the English tokenizer and 0.56 on multilingual."
|
|
104
|
+
},
|
|
105
|
+
"addedInPR": "https://github.com/juspay/neurolink/pull/1978"
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import type { OpenAICompatCatalogEntry, ProviderCatalogJson, ProviderConfigOptions } from "../../types/index.js";
|
|
2
|
-
export declare function catalogCredentialsKey(entry:
|
|
3
|
-
export declare function catalogEnvVar(entry:
|
|
4
|
-
export declare function buildCatalogConfigOptions(entry:
|
|
1
|
+
import type { CatalogEnvSource, CatalogKeySource, CatalogSetupSource, DecideCatalogJson, OpenAICompatCatalogEntry, ProviderCatalogJson, ProviderConfigOptions } from "../../types/index.js";
|
|
2
|
+
export declare function catalogCredentialsKey(entry: CatalogKeySource): string;
|
|
3
|
+
export declare function catalogEnvVar(entry: CatalogEnvSource, kind: "apiKey" | "baseURL" | "model"): string;
|
|
4
|
+
export declare function buildCatalogConfigOptions(entry: CatalogSetupSource): ProviderConfigOptions;
|
|
5
|
+
export declare function freezeCatalogCopy<T>(value: T): T;
|
|
5
6
|
export declare function getCatalogJsonEntries(): readonly ProviderCatalogJson[];
|
|
7
|
+
/** The `decide` entries, copied and frozen exactly as getCatalogJsonEntries() does. */
|
|
8
|
+
export declare function getDecideCatalogEntries(): readonly DecideCatalogJson[];
|
|
6
9
|
export declare function buildCatalogEntries(): OpenAICompatCatalogEntry[];
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* knowledge about deriving env vars, template interpolation and error
|
|
5
5
|
* rule matching lives HERE — the JSON stays declarative.
|
|
6
6
|
*/
|
|
7
|
-
import { CATALOG_JSON_ENTRIES } from "./index.generated.js";
|
|
7
|
+
import { CATALOG_JSON_ENTRIES, DECIDE_CATALOG_JSON_ENTRIES, } from "./index.generated.js";
|
|
8
8
|
import { DEFAULT_ERROR_RULES } from "../../utils/errorClassifier.js";
|
|
9
9
|
import { AuthenticationError, RateLimitError, InvalidModelError, NetworkError, ProviderError, } from "../../types/index.js";
|
|
10
10
|
const ERROR_CLASS_MAP = {
|
|
@@ -18,7 +18,7 @@ const ERROR_CLASS_MAP = {
|
|
|
18
18
|
// import from tools/ (build-time tooling depends on runtime source, not the
|
|
19
19
|
// reverse).
|
|
20
20
|
function toCamelCase(id) {
|
|
21
|
-
return id.replace(/-([a-
|
|
21
|
+
return id.replace(/-([a-z0-9])/g, (_m, c) => c.toUpperCase());
|
|
22
22
|
}
|
|
23
23
|
// Resolves the NeurolinkCredentials key for an entry — respects a
|
|
24
24
|
// credentialsKey override (required where the derived camelCase would
|
|
@@ -108,8 +108,15 @@ function deepFreeze(value) {
|
|
|
108
108
|
// CATALOG_JSON_ENTRIES's own objects), then deepFreeze() the clone. All
|
|
109
109
|
// current callers only .find/.map/.flatMap/.filter/iterate, so this is
|
|
110
110
|
// behavior-preserving for them.
|
|
111
|
+
export function freezeCatalogCopy(value) {
|
|
112
|
+
return deepFreeze(structuredClone(value));
|
|
113
|
+
}
|
|
111
114
|
export function getCatalogJsonEntries() {
|
|
112
|
-
return
|
|
115
|
+
return freezeCatalogCopy(CATALOG_JSON_ENTRIES);
|
|
116
|
+
}
|
|
117
|
+
/** The `decide` entries, copied and frozen exactly as getCatalogJsonEntries() does. */
|
|
118
|
+
export function getDecideCatalogEntries() {
|
|
119
|
+
return freezeCatalogCopy(DECIDE_CATALOG_JSON_ENTRIES);
|
|
113
120
|
}
|
|
114
121
|
export function buildCatalogEntries() {
|
|
115
122
|
return CATALOG_JSON_ENTRIES.map((entry) => {
|