@juspay/neurolink 12.12.4 → 12.12.6
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 +2 -2
- package/dist/auth/tokenStore.d.ts +9 -1
- package/dist/auth/tokenStore.js +29 -0
- package/dist/browser/neurolink.min.js +404 -403
- package/dist/cli/commands/proxy.js +65 -80
- package/dist/core/baseProvider.d.ts +10 -0
- package/dist/core/baseProvider.js +12 -2
- package/dist/middleware/builtin/guardrails.js +23 -13
- package/dist/middleware/utils/guardrailsUtils.d.ts +3 -15
- package/dist/middleware/utils/guardrailsUtils.js +14 -7
- package/dist/middleware/wrapLanguageModel.d.ts +3 -4
- package/dist/middleware/wrapLanguageModel.js +3 -4
- package/dist/providers/openaiChatCompletionsBase.js +214 -24
- package/dist/proxy/claudeFormat.d.ts +2 -2
- package/dist/proxy/claudeFormat.js +2 -2
- package/dist/proxy/codexFallback.d.ts +7 -1
- package/dist/proxy/codexFallback.js +181 -0
- package/dist/proxy/rollingProxyServer.js +10 -4
- package/dist/proxy/rollingWorkerProcess.js +8 -2
- package/dist/proxy/rollingWorkerProtocol.d.ts +2 -0
- package/dist/proxy/rollingWorkerProtocol.js +2 -0
- package/dist/proxy/rollingWorkerSupervisor.d.ts +5 -0
- package/dist/proxy/rollingWorkerSupervisor.js +75 -14
- package/dist/server/routes/claudeProxyRoutes.js +236 -61
- package/dist/server/routes/codexProxyRoutes.js +33 -6
- package/dist/types/cli.d.ts +2 -1
- package/dist/types/codex.d.ts +5 -0
- package/dist/types/proxy.d.ts +10 -2
- package/package.json +2 -1
|
@@ -66,7 +66,6 @@ const gatedShareRequests = new WeakSet();
|
|
|
66
66
|
const PROXY_LIFECYCLE_SHUTDOWN_TIMEOUT_MS = 5_000;
|
|
67
67
|
/** How long shutdown waits on the share listener before moving on. */
|
|
68
68
|
const SHARE_LISTENER_CLOSE_TIMEOUT_MS = 10_000;
|
|
69
|
-
const PROXY_STATUS_TOKEN_READ_TIMEOUT_MS = 2_000;
|
|
70
69
|
const PROXY_STATUS_RECONCILE_TIMEOUT_MS = 750;
|
|
71
70
|
const PROXY_STATUS_ACCOUNT_INVENTORY_TIMEOUT_MS = 750;
|
|
72
71
|
// Allowed drift between a pid's OS-reported start time and the persisted
|
|
@@ -1164,6 +1163,10 @@ function registerProxyRequestTracking(app, requestMetadata, readiness) {
|
|
|
1164
1163
|
});
|
|
1165
1164
|
},
|
|
1166
1165
|
onTerminal: ({ outcome, observedBodyBytes, responseChunks }) => {
|
|
1166
|
+
if (outcome === "completed" &&
|
|
1167
|
+
metadata.terminalErrorType === "stream_error") {
|
|
1168
|
+
outcome = "stream_error";
|
|
1169
|
+
}
|
|
1167
1170
|
logProxyLifecycleEvent({
|
|
1168
1171
|
event: "request_terminal",
|
|
1169
1172
|
requestId: metadata.requestId,
|
|
@@ -1500,6 +1503,7 @@ export async function createProxyStartApp(params) {
|
|
|
1500
1503
|
shareOutcome.release();
|
|
1501
1504
|
}
|
|
1502
1505
|
}
|
|
1506
|
+
const requestAbortController = new AbortController();
|
|
1503
1507
|
const ctx = {
|
|
1504
1508
|
requestId: metadata?.requestId ?? crypto.randomUUID(),
|
|
1505
1509
|
method: c.req.method,
|
|
@@ -1509,6 +1513,10 @@ export async function createProxyStartApp(params) {
|
|
|
1509
1513
|
params: c.req.param(),
|
|
1510
1514
|
body,
|
|
1511
1515
|
rawBody,
|
|
1516
|
+
abortSignal: AbortSignal.any([
|
|
1517
|
+
c.req.raw.signal,
|
|
1518
|
+
requestAbortController.signal,
|
|
1519
|
+
]),
|
|
1512
1520
|
// The proxy runtime exposes only the structural slice of NeuroLink the
|
|
1513
1521
|
// routes use; narrow (overlap-checked) to the full class for ServerContext.
|
|
1514
1522
|
neurolink: params.neurolink,
|
|
@@ -1555,39 +1563,42 @@ export async function createProxyStartApp(params) {
|
|
|
1555
1563
|
Symbol.asyncIterator in Object(result)) {
|
|
1556
1564
|
const iterator = result[Symbol.asyncIterator]();
|
|
1557
1565
|
let cancelled = false;
|
|
1566
|
+
const encoder = new TextEncoder();
|
|
1558
1567
|
const responseStream = new ReadableStream({
|
|
1559
|
-
async
|
|
1568
|
+
async pull(controller) {
|
|
1569
|
+
if (cancelled) {
|
|
1570
|
+
return;
|
|
1571
|
+
}
|
|
1560
1572
|
try {
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
controller.
|
|
1573
|
+
const next = await iterator.next();
|
|
1574
|
+
if (cancelled) {
|
|
1575
|
+
return;
|
|
1576
|
+
}
|
|
1577
|
+
if (next.done) {
|
|
1578
|
+
controller.close();
|
|
1579
|
+
}
|
|
1580
|
+
else {
|
|
1581
|
+
controller.enqueue(encoder.encode(next.value));
|
|
1567
1582
|
}
|
|
1568
|
-
controller.close();
|
|
1569
1583
|
}
|
|
1570
1584
|
catch (streamErr) {
|
|
1571
1585
|
if (cancelled) {
|
|
1572
|
-
controller.close();
|
|
1573
1586
|
return;
|
|
1574
1587
|
}
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
: String(streamErr);
|
|
1578
|
-
const errorEvent = `event: error\ndata: ${JSON.stringify({ type: "error", error: { type: "api_error", message: `Stream interrupted: ${errMsg}` } })}\n\n`;
|
|
1579
|
-
try {
|
|
1580
|
-
controller.enqueue(new TextEncoder().encode(errorEvent));
|
|
1581
|
-
}
|
|
1582
|
-
catch {
|
|
1583
|
-
// Controller already errored — ignore
|
|
1588
|
+
if (metadata) {
|
|
1589
|
+
metadata.terminalErrorType = "stream_error";
|
|
1584
1590
|
}
|
|
1591
|
+
logger.debug("[proxy] response stream interrupted", {
|
|
1592
|
+
error: streamErr,
|
|
1593
|
+
});
|
|
1594
|
+
controller.enqueue(encoder.encode(`event: error\ndata: ${JSON.stringify({ type: "error", error: { type: "api_error", message: "Stream interrupted" } })}\n\n`));
|
|
1585
1595
|
controller.close();
|
|
1586
1596
|
}
|
|
1587
1597
|
},
|
|
1588
|
-
async cancel() {
|
|
1598
|
+
async cancel(reason) {
|
|
1589
1599
|
cancelled = true;
|
|
1590
|
-
|
|
1600
|
+
requestAbortController.abort(reason);
|
|
1601
|
+
await withTimeout(Promise.resolve(iterator.return?.()), 1000, "[proxy] response cancellation timed out").catch(() => undefined);
|
|
1591
1602
|
},
|
|
1592
1603
|
});
|
|
1593
1604
|
return new Response(responseStream, {
|
|
@@ -1697,48 +1708,24 @@ export async function createProxyStartApp(params) {
|
|
|
1697
1708
|
let accountInventoryLoaded = false;
|
|
1698
1709
|
try {
|
|
1699
1710
|
const { tokenStore } = await import("../../auth/tokenStore.js");
|
|
1700
|
-
const
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
const inventory = await withTimeout((async () => {
|
|
1716
|
-
const tokenExpirations = await Promise.all(storedKeys.map(async (key) => {
|
|
1717
|
-
try {
|
|
1718
|
-
const tokens = await withTimeout(tokenStore.peekTokens(key), PROXY_STATUS_TOKEN_READ_TIMEOUT_MS, "[proxy] /status token inspection timed out");
|
|
1719
|
-
return tokens ? [key, tokens.expiresAt] : undefined;
|
|
1720
|
-
}
|
|
1721
|
-
catch (error) {
|
|
1722
|
-
logger.debug(`[proxy] /status: failed to inspect token metadata for ${normalizeAnthropicAccountKey(key)}: ${error instanceof Error ? error.message : String(error)}`);
|
|
1723
|
-
return undefined;
|
|
1724
|
-
}
|
|
1725
|
-
}));
|
|
1726
|
-
const disabledKeys = await tokenStore.listDisabled();
|
|
1727
|
-
return { tokenExpirations, disabledKeys };
|
|
1728
|
-
})(), PROXY_STATUS_ACCOUNT_INVENTORY_TIMEOUT_MS, "[proxy] /status account metadata timed out");
|
|
1729
|
-
for (const expiration of inventory.tokenExpirations) {
|
|
1730
|
-
if (expiration) {
|
|
1731
|
-
const key = expiration[0].startsWith("anthropic:")
|
|
1732
|
-
? normalizeAnthropicAccountKey(expiration[0])
|
|
1733
|
-
: expiration[0];
|
|
1734
|
-
storedAccountExpirations.set(key, expiration[1]);
|
|
1711
|
+
const inventory = await withTimeout(tokenStore.getProviderSnapshot(), PROXY_STATUS_ACCOUNT_INVENTORY_TIMEOUT_MS, "[proxy] /status account inspection timed out");
|
|
1712
|
+
for (const [storedKey, entry] of Object.entries(inventory)) {
|
|
1713
|
+
if (!storedKey.startsWith("anthropic:") &&
|
|
1714
|
+
!storedKey.startsWith("codex:")) {
|
|
1715
|
+
continue;
|
|
1716
|
+
}
|
|
1717
|
+
const key = storedKey.startsWith("anthropic:")
|
|
1718
|
+
? normalizeAnthropicAccountKey(storedKey)
|
|
1719
|
+
: storedKey;
|
|
1720
|
+
(key.startsWith("anthropic:")
|
|
1721
|
+
? storedAnthropicAccountKeys
|
|
1722
|
+
: storedCodexAccountKeys).add(key);
|
|
1723
|
+
storedAccountExpirations.set(key, entry.tokens.expiresAt);
|
|
1724
|
+
if (entry.disabled) {
|
|
1725
|
+
disabledProviderAccountKeys.add(key);
|
|
1735
1726
|
}
|
|
1736
1727
|
}
|
|
1737
|
-
|
|
1738
|
-
disabledProviderAccountKeys.add(key.startsWith("anthropic:")
|
|
1739
|
-
? normalizeAnthropicAccountKey(key)
|
|
1740
|
-
: key);
|
|
1741
|
-
}
|
|
1728
|
+
accountInventoryLoaded = true;
|
|
1742
1729
|
}
|
|
1743
1730
|
catch (err) {
|
|
1744
1731
|
logger.debug(`[proxy] /status: failed to resolve account cooldown labels: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -3090,7 +3077,15 @@ function printStatusStats(stats) {
|
|
|
3090
3077
|
}
|
|
3091
3078
|
}
|
|
3092
3079
|
}
|
|
3093
|
-
|
|
3080
|
+
const historical = (stats.accounts ?? []).filter((account) => account.status === "unattributed");
|
|
3081
|
+
const accounts = (stats.accounts ?? []).filter((account) => account.status !== "unattributed");
|
|
3082
|
+
if (historical.length > 0) {
|
|
3083
|
+
const sum = (field) => historical.reduce((total, account) => total + (account[field] ?? 0), 0);
|
|
3084
|
+
console.info(`\n Historical usage (provider unknown; included in totals):`);
|
|
3085
|
+
console.info(` ${historical.length} legacy records: ${sum("attempts")} attempts, ${sum("success")} success, ${sum("errors")} errors, ${sum("rateLimits")} rate-limited attempts`);
|
|
3086
|
+
console.info(" Per-record history remains available with --format json.");
|
|
3087
|
+
}
|
|
3088
|
+
if (accounts.length > 0) {
|
|
3094
3089
|
console.info(`\n Accounts:`);
|
|
3095
3090
|
const headers = [
|
|
3096
3091
|
"ACCOUNT",
|
|
@@ -3101,8 +3096,8 @@ function printStatusStats(stats) {
|
|
|
3101
3096
|
"RL",
|
|
3102
3097
|
"STATUS",
|
|
3103
3098
|
];
|
|
3104
|
-
const rows =
|
|
3105
|
-
account.label,
|
|
3099
|
+
const rows = accounts.map((account) => [
|
|
3100
|
+
account.key ?? account.label,
|
|
3106
3101
|
account.type,
|
|
3107
3102
|
String(account.attempts ?? account.requests ?? 0),
|
|
3108
3103
|
String(account.success ?? 0),
|
|
@@ -3267,7 +3262,7 @@ export const proxyStatusCommand = {
|
|
|
3267
3262
|
});
|
|
3268
3263
|
if (statusResp.ok) {
|
|
3269
3264
|
const statusData = (await statusResp.json());
|
|
3270
|
-
liveStats = statusData.stats;
|
|
3265
|
+
liveStats = statusData.stats ?? null;
|
|
3271
3266
|
liveConfig = statusData.config;
|
|
3272
3267
|
status.workerVersion =
|
|
3273
3268
|
typeof statusData.version === "string"
|
|
@@ -3342,6 +3337,9 @@ export const proxyStatusCommand = {
|
|
|
3342
3337
|
else if (status.rolling?.draining.length) {
|
|
3343
3338
|
logger.always(` ${chalk.bold("Handoff:")} ${chalk.cyan(`${status.rolling.draining.length} previous worker(s) draining`)}`);
|
|
3344
3339
|
}
|
|
3340
|
+
if (status.rolling) {
|
|
3341
|
+
logger.always(` ${chalk.bold("Socket handoff:")} ${status.rolling.pendingTransfers ?? "unknown"} pending, ${status.rolling.queuedSockets} queued; ${status.rolling.rejectedSockets} rejected, ${status.rolling.failedTransfers} failed transfers since supervisor start`);
|
|
3342
|
+
}
|
|
3345
3343
|
if (status.deferredUpdate) {
|
|
3346
3344
|
const active = status.deferredUpdate.activeRequests === null
|
|
3347
3345
|
? "unknown activity"
|
|
@@ -3398,21 +3396,8 @@ export const proxyStatusCommand = {
|
|
|
3398
3396
|
logger.always("");
|
|
3399
3397
|
logger.always(chalk.gray(" (Could not reach proxy for live status)"));
|
|
3400
3398
|
}
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
const liveUrl = status.url;
|
|
3404
|
-
const statusResp = await fetch(`${liveUrl}/status`, {
|
|
3405
|
-
signal: AbortSignal.timeout(2_000),
|
|
3406
|
-
});
|
|
3407
|
-
if (statusResp.ok) {
|
|
3408
|
-
const statusData = (await statusResp.json());
|
|
3409
|
-
if (statusData.stats) {
|
|
3410
|
-
printStatusStats(statusData.stats);
|
|
3411
|
-
}
|
|
3412
|
-
}
|
|
3413
|
-
}
|
|
3414
|
-
catch {
|
|
3415
|
-
/* non-fatal */
|
|
3399
|
+
if (liveStats) {
|
|
3400
|
+
printStatusStats(liveStats);
|
|
3416
3401
|
}
|
|
3417
3402
|
}
|
|
3418
3403
|
else {
|
|
@@ -468,6 +468,16 @@ export declare abstract class BaseProvider implements AIProvider {
|
|
|
468
468
|
* TODO(#1576): Implement global level middlewares that can be used
|
|
469
469
|
*/
|
|
470
470
|
protected getAISDKModelWithMiddleware(options?: TextGenerationOptions | StreamOptions): Promise<LanguageModel>;
|
|
471
|
+
/**
|
|
472
|
+
* Apply the configured middleware chain to a caller-supplied base model.
|
|
473
|
+
*
|
|
474
|
+
* `getAISDKModelWithMiddleware()` always wraps `getAISDKModel()`, which is
|
|
475
|
+
* the model the non-streaming path drives. Streaming paths build a
|
|
476
|
+
* different base — one whose `doStream` starts the provider's own stream
|
|
477
|
+
* loop — and need the same chain applied to it, so the wrapping is split
|
|
478
|
+
* out here rather than duplicated per provider.
|
|
479
|
+
*/
|
|
480
|
+
protected applyMiddlewareToModel(baseModel: LanguageModel, options?: TextGenerationOptions | StreamOptions): Promise<LanguageModel>;
|
|
471
481
|
/**
|
|
472
482
|
* Extract middleware options - delegated to Utilities
|
|
473
483
|
*/
|
|
@@ -2014,8 +2014,18 @@ export class BaseProvider {
|
|
|
2014
2014
|
* TODO(#1576): Implement global level middlewares that can be used
|
|
2015
2015
|
*/
|
|
2016
2016
|
async getAISDKModelWithMiddleware(options = {}) {
|
|
2017
|
-
|
|
2018
|
-
|
|
2017
|
+
return this.applyMiddlewareToModel(await this.getAISDKModel(), options);
|
|
2018
|
+
}
|
|
2019
|
+
/**
|
|
2020
|
+
* Apply the configured middleware chain to a caller-supplied base model.
|
|
2021
|
+
*
|
|
2022
|
+
* `getAISDKModelWithMiddleware()` always wraps `getAISDKModel()`, which is
|
|
2023
|
+
* the model the non-streaming path drives. Streaming paths build a
|
|
2024
|
+
* different base — one whose `doStream` starts the provider's own stream
|
|
2025
|
+
* loop — and need the same chain applied to it, so the wrapping is split
|
|
2026
|
+
* out here rather than duplicated per provider.
|
|
2027
|
+
*/
|
|
2028
|
+
async applyMiddlewareToModel(baseModel, options = {}) {
|
|
2019
2029
|
logger.debug(`Retrieved base model for ${this.providerName}`, {
|
|
2020
2030
|
provider: this.providerName,
|
|
2021
2031
|
model: this.modelName,
|
|
@@ -43,7 +43,6 @@ export function createGuardrailsMiddleware(config = {}) {
|
|
|
43
43
|
const blockingState = new WeakMap();
|
|
44
44
|
const middleware = {
|
|
45
45
|
specificationVersion: "v3",
|
|
46
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
47
46
|
transformParams: async ({ params }) => {
|
|
48
47
|
if (config.precallEvaluation?.enabled) {
|
|
49
48
|
const { shouldBlock, transformedParams } = await handlePrecallGuardrails(params, config.precallEvaluation);
|
|
@@ -53,7 +52,6 @@ export function createGuardrailsMiddleware(config = {}) {
|
|
|
53
52
|
}
|
|
54
53
|
return params;
|
|
55
54
|
},
|
|
56
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
57
55
|
wrapGenerate: async ({ doGenerate, params }) => {
|
|
58
56
|
logger.debug(`[GuardrailsMiddleware] Applying to generate call.`);
|
|
59
57
|
// Check if this request should be blocked (set by transformParams)
|
|
@@ -61,14 +59,23 @@ export function createGuardrailsMiddleware(config = {}) {
|
|
|
61
59
|
return createBlockedResponse();
|
|
62
60
|
}
|
|
63
61
|
let result = await doGenerate();
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
result
|
|
67
|
-
|
|
62
|
+
result = {
|
|
63
|
+
...result,
|
|
64
|
+
content: result.content.map((part) => part.type === "text"
|
|
65
|
+
? {
|
|
66
|
+
...part,
|
|
67
|
+
text: applyContentFiltering(part.text, config.badWords, "generate").filteredText,
|
|
68
|
+
}
|
|
69
|
+
: part),
|
|
70
|
+
};
|
|
68
71
|
if (config.modelFilter?.enabled && config.modelFilter.filterModel) {
|
|
69
72
|
logger.debug(`[GuardrailsMiddleware] Invoking model-based filter.`);
|
|
70
73
|
try {
|
|
71
|
-
const
|
|
74
|
+
const text = result.content
|
|
75
|
+
.filter((part) => part.type === "text")
|
|
76
|
+
.map((part) => part.text)
|
|
77
|
+
.join("");
|
|
78
|
+
const filterPrompt = `Is the following text safe? Respond with only "safe" or "unsafe".\n\nText: "${text}"`;
|
|
72
79
|
// `ModelFilterConfig.filterModel` is typed `LanguageModel`, which
|
|
73
80
|
// admits a bare model id, and the documented examples used one.
|
|
74
81
|
// `generateOnceNative` needs a handle exposing doGenerate, so a
|
|
@@ -78,7 +85,12 @@ export function createGuardrailsMiddleware(config = {}) {
|
|
|
78
85
|
const { text: filterResponse } = await generateOnceNative(filterModel, { prompt: filterPrompt });
|
|
79
86
|
if (filterResponse.toLowerCase().trim() === "unsafe") {
|
|
80
87
|
logger.warn(`[GuardrailsMiddleware] Model-based filter flagged content as unsafe.`);
|
|
81
|
-
result = {
|
|
88
|
+
result = {
|
|
89
|
+
...result,
|
|
90
|
+
content: result.content.map((part) => part.type === "text"
|
|
91
|
+
? { ...part, text: "<REDACTED BY AI GUARDRAIL>" }
|
|
92
|
+
: part),
|
|
93
|
+
};
|
|
82
94
|
}
|
|
83
95
|
}
|
|
84
96
|
catch (error) {
|
|
@@ -89,7 +101,6 @@ export function createGuardrailsMiddleware(config = {}) {
|
|
|
89
101
|
}
|
|
90
102
|
return result;
|
|
91
103
|
},
|
|
92
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
93
104
|
wrapStream: async ({ doStream, params }) => {
|
|
94
105
|
logger.debug(`[GuardrailsMiddleware] Applying to stream call.`);
|
|
95
106
|
// Check if this request should be blocked (set by transformParams)
|
|
@@ -106,13 +117,12 @@ export function createGuardrailsMiddleware(config = {}) {
|
|
|
106
117
|
transform(chunk, controller) {
|
|
107
118
|
hasYieldedChunks = true;
|
|
108
119
|
let filteredChunk = chunk;
|
|
109
|
-
if (
|
|
110
|
-
|
|
111
|
-
const filterResult = applyContentFiltering(filteredChunk.textDelta, config.badWords, "stream");
|
|
120
|
+
if (filteredChunk.type === "text-delta") {
|
|
121
|
+
const filterResult = applyContentFiltering(filteredChunk.delta, config.badWords, "stream");
|
|
112
122
|
if (filterResult.hasChanges) {
|
|
113
123
|
filteredChunk = {
|
|
114
124
|
...filteredChunk,
|
|
115
|
-
|
|
125
|
+
delta: filterResult.filteredText,
|
|
116
126
|
};
|
|
117
127
|
}
|
|
118
128
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BadWordsConfig, ContentFilteringResult, EvaluationActionResult, PrecallEvaluationConfig, PrecallEvaluationResult } from "../../types/index.js";
|
|
1
|
+
import type { BadWordsConfig, LanguageModelV3GenerateResult, LanguageModelV3StreamPart, ContentFilteringResult, EvaluationActionResult, PrecallEvaluationConfig, PrecallEvaluationResult } from "../../types/index.js";
|
|
2
2
|
export declare const DEFAULT_EVALUATION_PROMPT = "\nEvaluate the following user input for safety and appropriateness. Respond ONLY with valid JSON:\n\n{\n \"overall\": \"safe|unsafe|suspicious|inappropriate\",\n \"safetyScore\": <1-10>,\n \"appropriatenessScore\": <1-10>,\n \"confidenceLevel\": <1-10>,\n \"issues\": [\n {\n \"category\": \"explicit_content|hate_speech|violence|personal_info|spam|other\",\n \"severity\": \"low|medium|high|critical\",\n \"description\": \"Brief description\"\n }\n ],\n \"suggestedAction\": \"allow|block|sanitize|warn\",\n \"reasoning\": \"Brief explanation\"\n}\n\nUser Input: \"{USER_INPUT}\"\n";
|
|
3
3
|
/**
|
|
4
4
|
* Extract user input from middleware params
|
|
@@ -25,20 +25,8 @@ export declare function applyEvaluationActions(evaluation: PrecallEvaluationResu
|
|
|
25
25
|
*/
|
|
26
26
|
export declare function applySanitization(params: any, sanitizedInput: string): any;
|
|
27
27
|
export declare function escapeRegExp(string: string): string;
|
|
28
|
-
export declare function createBlockedResponse():
|
|
29
|
-
|
|
30
|
-
usage: {
|
|
31
|
-
promptTokens: number;
|
|
32
|
-
completionTokens: number;
|
|
33
|
-
};
|
|
34
|
-
finishReason: "stop";
|
|
35
|
-
warnings: never[];
|
|
36
|
-
rawCall: {
|
|
37
|
-
rawPrompt: null;
|
|
38
|
-
rawSettings: {};
|
|
39
|
-
};
|
|
40
|
-
};
|
|
41
|
-
export declare function createBlockedStream(): ReadableStream<any>;
|
|
28
|
+
export declare function createBlockedResponse(): LanguageModelV3GenerateResult;
|
|
29
|
+
export declare function createBlockedStream(): ReadableStream<LanguageModelV3StreamPart>;
|
|
42
30
|
/**
|
|
43
31
|
* Apply content filtering using bad words configuration
|
|
44
32
|
* Handles both regex patterns and string lists with proper priority
|
|
@@ -258,24 +258,31 @@ export function escapeRegExp(string) {
|
|
|
258
258
|
}
|
|
259
259
|
export function createBlockedResponse() {
|
|
260
260
|
return {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
261
|
+
content: [
|
|
262
|
+
{
|
|
263
|
+
type: "text",
|
|
264
|
+
text: "Request contains inappropriate content and has been blocked.",
|
|
265
|
+
},
|
|
266
|
+
],
|
|
267
|
+
usage: { inputTokens: { total: 0 }, outputTokens: { total: 0 } },
|
|
268
|
+
finishReason: { unified: "stop" },
|
|
264
269
|
warnings: [],
|
|
265
|
-
rawCall: { rawPrompt: null, rawSettings: {} },
|
|
266
270
|
};
|
|
267
271
|
}
|
|
268
272
|
export function createBlockedStream() {
|
|
269
273
|
return new ReadableStream({
|
|
270
274
|
start(controller) {
|
|
275
|
+
controller.enqueue({ type: "text-start", id: "blocked" });
|
|
271
276
|
controller.enqueue({
|
|
272
277
|
type: "text-delta",
|
|
273
|
-
|
|
278
|
+
id: "blocked",
|
|
279
|
+
delta: "Request contains inappropriate content and has been blocked.",
|
|
274
280
|
});
|
|
281
|
+
controller.enqueue({ type: "text-end", id: "blocked" });
|
|
275
282
|
controller.enqueue({
|
|
276
283
|
type: "finish",
|
|
277
|
-
finishReason: "stop",
|
|
278
|
-
usage: {
|
|
284
|
+
finishReason: { unified: "stop" },
|
|
285
|
+
usage: { inputTokens: { total: 0 }, outputTokens: { total: 0 } },
|
|
279
286
|
});
|
|
280
287
|
controller.close();
|
|
281
288
|
},
|
|
@@ -6,10 +6,9 @@
|
|
|
6
6
|
* `wrapGenerate` / `wrapStream` hooks. Reproduced here so the middleware
|
|
7
7
|
* factory no longer needs the ai package.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
* streaming
|
|
11
|
-
*
|
|
12
|
-
* introduced.
|
|
9
|
+
* The OpenAI-compatible streaming path also uses this wrapper. Other native
|
|
10
|
+
* streaming implementations must opt in explicitly; exposing a middleware
|
|
11
|
+
* option or a model-shaped handle alone does not apply the chain.
|
|
13
12
|
*/
|
|
14
13
|
import type { LanguageModelV3, LanguageModelV3Middleware } from "../types/index.js";
|
|
15
14
|
export declare const wrapLanguageModel: ({ model, middleware, }: {
|
|
@@ -6,10 +6,9 @@
|
|
|
6
6
|
* `wrapGenerate` / `wrapStream` hooks. Reproduced here so the middleware
|
|
7
7
|
* factory no longer needs the ai package.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
* streaming
|
|
11
|
-
*
|
|
12
|
-
* introduced.
|
|
9
|
+
* The OpenAI-compatible streaming path also uses this wrapper. Other native
|
|
10
|
+
* streaming implementations must opt in explicitly; exposing a middleware
|
|
11
|
+
* option or a model-shaped handle alone does not apply the chain.
|
|
13
12
|
*/
|
|
14
13
|
const doWrap = (model, middleware) => {
|
|
15
14
|
const transform = async (params, type) => middleware.transformParams
|