@juspay/neurolink 10.1.2 → 10.2.1
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 +12 -0
- package/dist/browser/neurolink.min.js +389 -389
- package/dist/cli/commands/proxy.js +85 -14
- package/dist/lib/providers/azureOpenai.d.ts +6 -0
- package/dist/lib/providers/azureOpenai.js +8 -0
- package/dist/lib/providers/openAI.d.ts +6 -0
- package/dist/lib/providers/openAI.js +8 -0
- package/dist/lib/providers/openaiChatCompletionsBase.d.ts +15 -0
- package/dist/lib/providers/openaiChatCompletionsBase.js +21 -1
- package/dist/lib/proxy/proxyPaths.d.ts +1 -0
- package/dist/lib/proxy/proxyPaths.js +5 -0
- package/dist/lib/proxy/usageStats.d.ts +58 -4
- package/dist/lib/proxy/usageStats.js +639 -79
- package/dist/lib/types/proxy.d.ts +39 -0
- package/dist/providers/azureOpenai.d.ts +6 -0
- package/dist/providers/azureOpenai.js +8 -0
- package/dist/providers/openAI.d.ts +6 -0
- package/dist/providers/openAI.js +8 -0
- package/dist/providers/openaiChatCompletionsBase.d.ts +15 -0
- package/dist/providers/openaiChatCompletionsBase.js +21 -1
- package/dist/proxy/proxyPaths.d.ts +1 -0
- package/dist/proxy/proxyPaths.js +5 -0
- package/dist/proxy/usageStats.d.ts +58 -4
- package/dist/proxy/usageStats.js +639 -79
- package/dist/types/proxy.d.ts +39 -0
- package/package.json +3 -2
|
@@ -700,6 +700,40 @@ export type ProxyStats = {
|
|
|
700
700
|
totalQuotaRateLimits: number;
|
|
701
701
|
accounts: Record<string, AccountStats>;
|
|
702
702
|
};
|
|
703
|
+
/** Durability and reconciliation state for the proxy usage counters. */
|
|
704
|
+
export type ProxyStatsPersistenceStatus = {
|
|
705
|
+
enabled: boolean;
|
|
706
|
+
filePath: string | null;
|
|
707
|
+
revision: number;
|
|
708
|
+
pendingMutations: number;
|
|
709
|
+
inFlightMutations: number;
|
|
710
|
+
unpersistedMutations: number;
|
|
711
|
+
lastFlushedAt: number | null;
|
|
712
|
+
lastReconciledAt: number | null;
|
|
713
|
+
lastRecoveryAt: number | null;
|
|
714
|
+
lastError: string | null;
|
|
715
|
+
};
|
|
716
|
+
/** Versioned on-disk snapshot shared by overlapping proxy workers. */
|
|
717
|
+
export type PersistedProxyStatsSnapshot = {
|
|
718
|
+
schemaVersion: 1;
|
|
719
|
+
revision: number;
|
|
720
|
+
updatedAt: number;
|
|
721
|
+
stats: ProxyStats;
|
|
722
|
+
};
|
|
723
|
+
/** Ownership metadata for the proxy statistics cross-process lock. */
|
|
724
|
+
export type ProxyStatsLockOwner = {
|
|
725
|
+
token: string;
|
|
726
|
+
pid: number;
|
|
727
|
+
acquiredAt: number;
|
|
728
|
+
};
|
|
729
|
+
/** Construction options for an isolated proxy statistics store. */
|
|
730
|
+
export type ProxyUsageStatsStoreOptions = {
|
|
731
|
+
filePath?: string;
|
|
732
|
+
flushIntervalMs?: number;
|
|
733
|
+
lockTimeoutMs?: number;
|
|
734
|
+
staleLockMs?: number;
|
|
735
|
+
now?: () => number;
|
|
736
|
+
};
|
|
703
737
|
export type RefreshableAccount = {
|
|
704
738
|
token: string;
|
|
705
739
|
refreshToken?: string;
|
|
@@ -874,6 +908,8 @@ export type ProxyPaths = {
|
|
|
874
908
|
quotaFile: string;
|
|
875
909
|
/** account-cooldowns.json — restart-safe account cooldown state */
|
|
876
910
|
cooldownFile: string;
|
|
911
|
+
/** proxy-usage-stats.json — restart- and handoff-safe usage counters */
|
|
912
|
+
statsFile?: string;
|
|
877
913
|
/** Whether this is a dev-mode isolated instance */
|
|
878
914
|
isDev: boolean;
|
|
879
915
|
};
|
|
@@ -1876,6 +1912,7 @@ export type ProxyStartApp = {
|
|
|
1876
1912
|
};
|
|
1877
1913
|
/** Stats shape consumed by the proxy status printer. */
|
|
1878
1914
|
export type StatusStats = {
|
|
1915
|
+
startedAt?: number;
|
|
1879
1916
|
totalAttempts?: number;
|
|
1880
1917
|
totalAttemptErrors?: number;
|
|
1881
1918
|
totalRequests: number;
|
|
@@ -1896,7 +1933,9 @@ export type StatusStats = {
|
|
|
1896
1933
|
transientRateLimits?: number;
|
|
1897
1934
|
quotaRateLimits?: number;
|
|
1898
1935
|
cooling: boolean;
|
|
1936
|
+
status?: "active" | "cooling" | "disabled" | "excluded" | "removed";
|
|
1899
1937
|
}[];
|
|
1938
|
+
persistence?: ProxyStatsPersistenceStatus;
|
|
1900
1939
|
};
|
|
1901
1940
|
/** Sub-action of the `proxy telemetry` CLI command. */
|
|
1902
1941
|
export type ProxyTelemetryAction = "setup" | "start" | "stop" | "status" | "logs" | "import-dashboard";
|
|
@@ -22,6 +22,12 @@ export declare class AzureOpenAIProvider extends OpenAIChatCompletionsProvider {
|
|
|
22
22
|
protected readonly azureDeploymentPathPrefix: string;
|
|
23
23
|
protected readonly useMaxCompletionTokensOverride?: boolean;
|
|
24
24
|
constructor(modelName?: string, sdk?: unknown, _region?: string, credentials?: NeurolinkCredentials["azure"]);
|
|
25
|
+
/**
|
|
26
|
+
* Azure OpenAI natively supports `response_format: json_schema` together
|
|
27
|
+
* with tool calling in one request, so structured output stays
|
|
28
|
+
* wire-enforced mid-loop instead of deferring to post-hoc coercion.
|
|
29
|
+
*/
|
|
30
|
+
protected suppressResponseFormatWithTools(): boolean;
|
|
25
31
|
protected getProviderName(): AIProviderName;
|
|
26
32
|
/**
|
|
27
33
|
* The "default model" for Azure is the deployment name — it's the
|
|
@@ -139,6 +139,14 @@ export class AzureOpenAIProvider extends OpenAIChatCompletionsProvider {
|
|
|
139
139
|
// ===========================================================================
|
|
140
140
|
// Abstract-hook implementations
|
|
141
141
|
// ===========================================================================
|
|
142
|
+
/**
|
|
143
|
+
* Azure OpenAI natively supports `response_format: json_schema` together
|
|
144
|
+
* with tool calling in one request, so structured output stays
|
|
145
|
+
* wire-enforced mid-loop instead of deferring to post-hoc coercion.
|
|
146
|
+
*/
|
|
147
|
+
suppressResponseFormatWithTools() {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
142
150
|
getProviderName() {
|
|
143
151
|
return "azure";
|
|
144
152
|
}
|
|
@@ -16,6 +16,12 @@ import { OpenAIChatCompletionsProvider } from "./openaiChatCompletionsBase.js";
|
|
|
16
16
|
*/
|
|
17
17
|
export declare class OpenAIProvider extends OpenAIChatCompletionsProvider {
|
|
18
18
|
constructor(modelName?: string, sdk?: unknown, _region?: string, credentials?: NeurolinkCredentials["openai"]);
|
|
19
|
+
/**
|
|
20
|
+
* OpenAI natively supports `response_format: json_schema` together with
|
|
21
|
+
* tool calling in one request, so structured output stays wire-enforced
|
|
22
|
+
* mid-loop instead of deferring to post-hoc coercion.
|
|
23
|
+
*/
|
|
24
|
+
protected suppressResponseFormatWithTools(): boolean;
|
|
19
25
|
protected getProviderName(): AIProviderName;
|
|
20
26
|
protected getDefaultModel(): string;
|
|
21
27
|
formatProviderError(error: unknown): Error;
|
package/dist/providers/openAI.js
CHANGED
|
@@ -74,6 +74,14 @@ export class OpenAIProvider extends OpenAIChatCompletionsProvider {
|
|
|
74
74
|
// ===========================================================================
|
|
75
75
|
// Abstract hook implementations
|
|
76
76
|
// ===========================================================================
|
|
77
|
+
/**
|
|
78
|
+
* OpenAI natively supports `response_format: json_schema` together with
|
|
79
|
+
* tool calling in one request, so structured output stays wire-enforced
|
|
80
|
+
* mid-loop instead of deferring to post-hoc coercion.
|
|
81
|
+
*/
|
|
82
|
+
suppressResponseFormatWithTools() {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
77
85
|
getProviderName() {
|
|
78
86
|
return AIProviderNameEnum.OPENAI;
|
|
79
87
|
}
|
|
@@ -62,6 +62,21 @@ export declare abstract class OpenAIChatCompletionsProvider extends BaseProvider
|
|
|
62
62
|
* downgraded `json_schema` to `json_object`. Subclasses replicate that here.
|
|
63
63
|
*/
|
|
64
64
|
protected adjustResponseFormat(rf: OpenAICompatResponseFormat | undefined, _modelId: string): OpenAICompatResponseFormat | undefined;
|
|
65
|
+
/**
|
|
66
|
+
* When true (default), `response_format` is NOT sent on requests that carry
|
|
67
|
+
* tools. The AI SDK sets responseFormat on EVERY step of a tool loop, and
|
|
68
|
+
* generic/proxy backends (LiteLLM→vllm/GLM, openai-compatible, local
|
|
69
|
+
* servers) may silently honor it over tool calling — answering with
|
|
70
|
+
* final-shape JSON on step 1 instead of running the agentic loop. No error
|
|
71
|
+
* is raised, so the runtime tools↔schema conflict detector cannot catch it.
|
|
72
|
+
* The schema is still enforced post-hoc (GenerationHandler coerces the final
|
|
73
|
+
* text against it) — the same contract as the Gemini tools↔schema exclusion.
|
|
74
|
+
* Mirrors the streaming path, which never sends response_format.
|
|
75
|
+
*
|
|
76
|
+
* Backends with first-party support for tools + json_schema in one request
|
|
77
|
+
* (OpenAI, Azure OpenAI) override this to false.
|
|
78
|
+
*/
|
|
79
|
+
protected suppressResponseFormatWithTools(): boolean;
|
|
65
80
|
/**
|
|
66
81
|
* Hook to adjust the fully-built wire request body before it is sent, on
|
|
67
82
|
* both the streaming and non-streaming paths. Default identity. Override for
|
|
@@ -76,6 +76,23 @@ export class OpenAIChatCompletionsProvider extends BaseProvider {
|
|
|
76
76
|
adjustResponseFormat(rf, _modelId) {
|
|
77
77
|
return rf;
|
|
78
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* When true (default), `response_format` is NOT sent on requests that carry
|
|
81
|
+
* tools. The AI SDK sets responseFormat on EVERY step of a tool loop, and
|
|
82
|
+
* generic/proxy backends (LiteLLM→vllm/GLM, openai-compatible, local
|
|
83
|
+
* servers) may silently honor it over tool calling — answering with
|
|
84
|
+
* final-shape JSON on step 1 instead of running the agentic loop. No error
|
|
85
|
+
* is raised, so the runtime tools↔schema conflict detector cannot catch it.
|
|
86
|
+
* The schema is still enforced post-hoc (GenerationHandler coerces the final
|
|
87
|
+
* text against it) — the same contract as the Gemini tools↔schema exclusion.
|
|
88
|
+
* Mirrors the streaming path, which never sends response_format.
|
|
89
|
+
*
|
|
90
|
+
* Backends with first-party support for tools + json_schema in one request
|
|
91
|
+
* (OpenAI, Azure OpenAI) override this to false.
|
|
92
|
+
*/
|
|
93
|
+
suppressResponseFormatWithTools() {
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
79
96
|
/**
|
|
80
97
|
* Hook to adjust the fully-built wire request body before it is sent, on
|
|
81
98
|
* both the streaming and non-streaming paths. Default identity. Override for
|
|
@@ -221,6 +238,7 @@ export class OpenAIChatCompletionsProvider extends BaseProvider {
|
|
|
221
238
|
const adjustResponseFormat = this.adjustResponseFormat.bind(this);
|
|
222
239
|
const adjustRequestBody = this.adjustRequestBody.bind(this);
|
|
223
240
|
const adjustBodyAfter400 = this.adjustBodyAfter400.bind(this);
|
|
241
|
+
const suppressResponseFormatWithTools = this.suppressResponseFormatWithTools.bind(this);
|
|
224
242
|
const getTimeoutForOptions = (opts) => this.getTimeout((opts ?? {}));
|
|
225
243
|
return {
|
|
226
244
|
specificationVersion: "v3",
|
|
@@ -229,7 +247,9 @@ export class OpenAIChatCompletionsProvider extends BaseProvider {
|
|
|
229
247
|
supportedUrls: {},
|
|
230
248
|
doGenerate: async (options) => {
|
|
231
249
|
const baseMessages = messageBuilderToOpenAI(options.prompt);
|
|
232
|
-
const
|
|
250
|
+
const hasTools = Array.isArray(options.tools) && options.tools.length > 0;
|
|
251
|
+
const responseFormat = options.responseFormat &&
|
|
252
|
+
!(hasTools && suppressResponseFormatWithTools())
|
|
233
253
|
? adjustResponseFormat(v3ResponseFormatToOpenAI(options.responseFormat), modelId)
|
|
234
254
|
: undefined;
|
|
235
255
|
// ensureJsonWordInBody runs LAST — on the body after adjustRequestBody —
|
package/dist/proxy/proxyPaths.js
CHANGED
|
@@ -22,6 +22,7 @@ export function resolveProxyPaths(dev) {
|
|
|
22
22
|
logsDir: join(base, "logs"),
|
|
23
23
|
quotaFile: join(base, "account-quotas.json"),
|
|
24
24
|
cooldownFile: join(base, "account-cooldowns.json"),
|
|
25
|
+
statsFile: join(base, "proxy-usage-stats.json"),
|
|
25
26
|
isDev: true,
|
|
26
27
|
};
|
|
27
28
|
}
|
|
@@ -31,6 +32,10 @@ export function resolveProxyPaths(dev) {
|
|
|
31
32
|
logsDir: join(base, "logs"),
|
|
32
33
|
quotaFile: join(base, "account-quotas.json"),
|
|
33
34
|
cooldownFile: join(base, "account-cooldowns.json"),
|
|
35
|
+
statsFile: join(base, "proxy-usage-stats.json"),
|
|
34
36
|
isDev: false,
|
|
35
37
|
};
|
|
36
38
|
}
|
|
39
|
+
export function resolveProxyUsageStatsPath(paths) {
|
|
40
|
+
return paths.statsFile ?? join(paths.stateDir, "proxy-usage-stats.json");
|
|
41
|
+
}
|
|
@@ -1,13 +1,67 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Proxy
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Proxy usage statistics with restart- and handoff-safe persistence.
|
|
3
|
+
*
|
|
4
|
+
* Request handlers update memory synchronously. Small deltas are merged into a
|
|
5
|
+
* shared snapshot asynchronously, under a cross-process lock, so overlapping
|
|
6
|
+
* rolling workers cannot overwrite each other's counters.
|
|
5
7
|
*/
|
|
6
|
-
import type { AccountStats, ProxyStats } from "../types/index.js";
|
|
8
|
+
import type { AccountStats, ProxyStats, ProxyStatsPersistenceStatus, ProxyUsageStatsStoreOptions } from "../types/index.js";
|
|
9
|
+
export declare class ProxyUsageStatsStore {
|
|
10
|
+
private readonly now;
|
|
11
|
+
private readonly flushIntervalMs;
|
|
12
|
+
private readonly lockTimeoutMs;
|
|
13
|
+
private readonly staleLockMs;
|
|
14
|
+
private filePath?;
|
|
15
|
+
private stats;
|
|
16
|
+
private pending;
|
|
17
|
+
private pendingMutations;
|
|
18
|
+
private inFlightMutations;
|
|
19
|
+
private revision;
|
|
20
|
+
private flushTimer;
|
|
21
|
+
private readonly flushMutex;
|
|
22
|
+
private lastFlushedAt?;
|
|
23
|
+
private lastReconciledAt?;
|
|
24
|
+
private lastRecoveryAt?;
|
|
25
|
+
private lastError?;
|
|
26
|
+
constructor(options?: ProxyUsageStatsStoreOptions);
|
|
27
|
+
initialize(filePath?: string): Promise<void>;
|
|
28
|
+
recordAttempt(accountLabel: string, accountType: string): void;
|
|
29
|
+
recordFinalSuccess(accountLabel?: string, accountType?: string): void;
|
|
30
|
+
recordAttemptError(accountLabel: string, accountType: string, status: number, rateLimitKind?: "transient" | "quota"): void;
|
|
31
|
+
recordFinalError(_status: number, accountLabel?: string, accountType?: string): void;
|
|
32
|
+
getStats(): ProxyStats;
|
|
33
|
+
getAccountStats(label: string): AccountStats | undefined;
|
|
34
|
+
getPersistenceStatus(): ProxyStatsPersistenceStatus;
|
|
35
|
+
flush(): Promise<void>;
|
|
36
|
+
reconcile(): Promise<ProxyStats>;
|
|
37
|
+
resetMemory(): void;
|
|
38
|
+
resetForTests(): Promise<void>;
|
|
39
|
+
private markMutation;
|
|
40
|
+
private applyAttempt;
|
|
41
|
+
private applyFinalSuccess;
|
|
42
|
+
private applyAttemptError;
|
|
43
|
+
private applyFinalError;
|
|
44
|
+
private ensureAccount;
|
|
45
|
+
private scheduleFlush;
|
|
46
|
+
private cancelFlushTimer;
|
|
47
|
+
private readSnapshot;
|
|
48
|
+
private applySnapshot;
|
|
49
|
+
private recoverCorruptSnapshot;
|
|
50
|
+
private quarantineCorruptSnapshot;
|
|
51
|
+
private pruneCorruptSnapshots;
|
|
52
|
+
private describeError;
|
|
53
|
+
}
|
|
54
|
+
export declare function initUsageStats(filePath: string): Promise<void>;
|
|
7
55
|
export declare function recordAttempt(accountLabel: string, accountType: string): void;
|
|
8
56
|
export declare function recordFinalSuccess(accountLabel?: string, accountType?: string): void;
|
|
9
57
|
export declare function recordAttemptError(accountLabel: string, accountType: string, status: number, rateLimitKind?: "transient" | "quota"): void;
|
|
10
58
|
export declare function recordFinalError(_status: number, accountLabel?: string, accountType?: string): void;
|
|
11
59
|
export declare function getStats(): ProxyStats;
|
|
60
|
+
export declare function getReconciledStats(): Promise<ProxyStats>;
|
|
12
61
|
export declare function getAccountStats(label: string): AccountStats | undefined;
|
|
62
|
+
export declare function getUsageStatsPersistenceStatus(): ProxyStatsPersistenceStatus;
|
|
63
|
+
export declare function flushUsageStats(): Promise<void>;
|
|
64
|
+
/** Reset process-local counters while intentionally preserving durable state. */
|
|
13
65
|
export declare function resetStats(): void;
|
|
66
|
+
/** Disconnect persistence and clear singleton state between isolated tests. */
|
|
67
|
+
export declare function resetUsageStatsForTests(): Promise<void>;
|