palaryn 0.4.8 → 0.4.10
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/dist/src/dlp/llm-classifier.d.ts.map +1 -1
- package/dist/src/dlp/llm-classifier.js +10 -2
- package/dist/src/dlp/llm-classifier.js.map +1 -1
- package/dist/src/llm-utils.d.ts +45 -0
- package/dist/src/llm-utils.d.ts.map +1 -0
- package/dist/src/llm-utils.js +107 -0
- package/dist/src/llm-utils.js.map +1 -0
- package/dist/src/saas/routes.d.ts.map +1 -1
- package/dist/src/saas/routes.js +29 -52
- package/dist/src/saas/routes.js.map +1 -1
- package/dist/tests/unit/saas-routes-branches.test.js +3 -3
- package/dist/tests/unit/saas-routes-branches.test.js.map +1 -1
- package/package.json +1 -1
- package/src/dlp/llm-classifier.ts +11 -2
- package/src/llm-utils.ts +140 -0
- package/src/saas/routes.ts +31 -56
package/src/saas/routes.ts
CHANGED
|
@@ -17,6 +17,7 @@ import { PlanTier } from '../types/subscription';
|
|
|
17
17
|
import { MODEL_PRICING, resolveModelPricing } from '../budget/model-pricing';
|
|
18
18
|
import { PolicyEngine } from '../policy/engine';
|
|
19
19
|
import { ToolCall } from '../types/tool-call';
|
|
20
|
+
import { detectLlmProvider, llmFetchWithFallback, LlmProvider } from '../llm-utils';
|
|
20
21
|
|
|
21
22
|
/** Strip HTML tags from user-provided display strings to prevent stored XSS. */
|
|
22
23
|
function stripHtmlTags(input: string): string {
|
|
@@ -1239,18 +1240,9 @@ Input: "Allow GET to example.com, block DELETE everywhere"
|
|
|
1239
1240
|
Output: [{"name":"allow-get-example","description":"Allow GET requests to example.com","effect":"ALLOW","priority":10,"conditions":{"methods":["GET"],"domains":["example.com"]}},{"name":"block-delete-all","description":"Block all DELETE operations","effect":"DENY","priority":5,"conditions":{"capabilities":["delete"],"methods":["DELETE"]}}]`;
|
|
1240
1241
|
|
|
1241
1242
|
try {
|
|
1242
|
-
const
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
// Detect provider from API key prefix
|
|
1246
|
-
const isOpenAI = apiKey.startsWith('sk-proj-') || apiKey.startsWith('sk-');
|
|
1247
|
-
const llmUrl = isOpenAI
|
|
1248
|
-
? 'https://api.openai.com/v1/chat/completions'
|
|
1249
|
-
: 'https://api.anthropic.com/v1/messages';
|
|
1250
|
-
const llmHeaders: Record<string, string> = isOpenAI
|
|
1251
|
-
? { 'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}` }
|
|
1252
|
-
: { 'Content-Type': 'application/json', 'x-api-key': apiKey, 'anthropic-version': '2023-06-01' };
|
|
1253
|
-
const llmBody = isOpenAI
|
|
1243
|
+
const fallbackKey = process.env.PALARYN_LLM_FALLBACK_KEY;
|
|
1244
|
+
|
|
1245
|
+
const buildBody = (provider: LlmProvider) => provider.isOpenAI
|
|
1254
1246
|
? JSON.stringify({
|
|
1255
1247
|
model: 'gpt-4.1-mini',
|
|
1256
1248
|
max_tokens: 1024,
|
|
@@ -1266,24 +1258,20 @@ Output: [{"name":"allow-get-example","description":"Allow GET requests to exampl
|
|
|
1266
1258
|
messages: [{ role: 'user', content: trimmed }],
|
|
1267
1259
|
});
|
|
1268
1260
|
|
|
1269
|
-
const llmRes = await
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1261
|
+
const { response: llmRes, provider, usedFallback } = await llmFetchWithFallback({
|
|
1262
|
+
primaryKey: apiKey,
|
|
1263
|
+
fallbackKey,
|
|
1264
|
+
buildBody,
|
|
1265
|
+
timeoutMs: 15000,
|
|
1266
|
+
tag: '[generate-rule]',
|
|
1274
1267
|
});
|
|
1275
1268
|
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
if (!llmRes.ok) {
|
|
1279
|
-
const errBody = await llmRes.text();
|
|
1280
|
-
console.error('[generate-rule] LLM API error:', llmRes.status, errBody);
|
|
1281
|
-
res.status(502).json({ error: 'Failed to generate rule. LLM API returned an error.' });
|
|
1282
|
-
return;
|
|
1269
|
+
if (usedFallback) {
|
|
1270
|
+
console.log(`[generate-rule] Used fallback provider (${provider.isOpenAI ? 'OpenAI' : 'Anthropic'})`);
|
|
1283
1271
|
}
|
|
1284
1272
|
|
|
1285
1273
|
const llmData = await llmRes.json() as any;
|
|
1286
|
-
const text = isOpenAI
|
|
1274
|
+
const text = provider.isOpenAI
|
|
1287
1275
|
? (llmData.choices?.[0]?.message?.content || '')
|
|
1288
1276
|
: (llmData.content?.[0]?.text || '');
|
|
1289
1277
|
|
|
@@ -1326,8 +1314,9 @@ Output: [{"name":"allow-get-example","description":"Allow GET requests to exampl
|
|
|
1326
1314
|
res.status(504).json({ error: 'Rule generation timed out. Please try again.' });
|
|
1327
1315
|
return;
|
|
1328
1316
|
}
|
|
1329
|
-
|
|
1330
|
-
|
|
1317
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
1318
|
+
console.error('[generate-rule] Unexpected error:', errMsg);
|
|
1319
|
+
res.status(502).json({ error: 'Failed to generate rule. LLM API returned an error.' });
|
|
1331
1320
|
}
|
|
1332
1321
|
});
|
|
1333
1322
|
|
|
@@ -1457,13 +1446,7 @@ ${current_policy ? `\nThe user has an existing policy. Refine it based on their
|
|
|
1457
1446
|
res.flushHeaders();
|
|
1458
1447
|
|
|
1459
1448
|
try {
|
|
1460
|
-
const
|
|
1461
|
-
const llmUrl = isOpenAI
|
|
1462
|
-
? 'https://api.openai.com/v1/chat/completions'
|
|
1463
|
-
: 'https://api.anthropic.com/v1/messages';
|
|
1464
|
-
const llmHeaders: Record<string, string> = isOpenAI
|
|
1465
|
-
? { 'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}` }
|
|
1466
|
-
: { 'Content-Type': 'application/json', 'x-api-key': apiKey, 'anthropic-version': '2023-06-01' };
|
|
1449
|
+
const fallbackKey = process.env.PALARYN_LLM_FALLBACK_KEY;
|
|
1467
1450
|
|
|
1468
1451
|
// Build message array for multi-turn context
|
|
1469
1452
|
const chatMessages = messages.map((m: { role: string; content: string }) => ({
|
|
@@ -1471,7 +1454,7 @@ ${current_policy ? `\nThe user has an existing policy. Refine it based on their
|
|
|
1471
1454
|
content: m.content.slice(0, 2000),
|
|
1472
1455
|
}));
|
|
1473
1456
|
|
|
1474
|
-
const
|
|
1457
|
+
const buildBody = (provider: LlmProvider) => provider.isOpenAI
|
|
1475
1458
|
? JSON.stringify({
|
|
1476
1459
|
model: 'gpt-4.1-mini',
|
|
1477
1460
|
max_tokens: 2048,
|
|
@@ -1489,32 +1472,24 @@ ${current_policy ? `\nThe user has an existing policy. Refine it based on their
|
|
|
1489
1472
|
messages: chatMessages,
|
|
1490
1473
|
});
|
|
1491
1474
|
|
|
1492
|
-
const
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
// Handle client disconnect
|
|
1496
|
-
req.on('close', () => {
|
|
1497
|
-
controller.abort();
|
|
1498
|
-
clearTimeout(timeout);
|
|
1499
|
-
});
|
|
1475
|
+
const clientAbort = new AbortController();
|
|
1476
|
+
req.on('close', () => clientAbort.abort());
|
|
1500
1477
|
|
|
1501
|
-
const llmRes = await
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1478
|
+
const { response: llmRes, provider, usedFallback } = await llmFetchWithFallback({
|
|
1479
|
+
primaryKey: apiKey,
|
|
1480
|
+
fallbackKey,
|
|
1481
|
+
buildBody,
|
|
1482
|
+
timeoutMs: 15000,
|
|
1483
|
+
signal: clientAbort.signal,
|
|
1484
|
+
tag: '[policy-chat]',
|
|
1506
1485
|
});
|
|
1507
1486
|
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
if (!llmRes.ok) {
|
|
1511
|
-
const errBody = await llmRes.text();
|
|
1512
|
-
console.error('[policy-chat] LLM API error:', llmRes.status, errBody);
|
|
1513
|
-
res.write(`event: error\ndata: ${JSON.stringify({ error: 'LLM API returned an error.' })}\n\n`);
|
|
1514
|
-
res.end();
|
|
1515
|
-
return;
|
|
1487
|
+
if (usedFallback) {
|
|
1488
|
+
console.log(`[policy-chat] Used fallback provider (${provider.isOpenAI ? 'OpenAI' : 'Anthropic'})`);
|
|
1516
1489
|
}
|
|
1517
1490
|
|
|
1491
|
+
const isOpenAI = provider.isOpenAI;
|
|
1492
|
+
|
|
1518
1493
|
if (!llmRes.body) {
|
|
1519
1494
|
res.write(`event: error\ndata: ${JSON.stringify({ error: 'No response stream from LLM.' })}\n\n`);
|
|
1520
1495
|
res.end();
|