sandal-db 1.0.4 → 1.0.5
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/README.md +2 -0
- package/dist/cli.js +70 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# SANDAL: Safe Agentic Natural-language Database Access Layer
|
|
2
2
|
|
|
3
|
+

|
|
4
|
+
|
|
3
5
|
SANDAL is an agentic command-line interface that enables software engineers, data analysts, and site reliability engineers to query, inspect, and manage databases using natural language. Powered by LangGraph and multi-provider language models, SANDAL translates natural language prompts into parameterized database queries, displays execution previews and impact estimations, and enforces multi-layered guardrails before executing any mutating statements.
|
|
4
6
|
|
|
5
7
|
---
|
package/dist/cli.js
CHANGED
|
@@ -1220,33 +1220,90 @@ function createDatabaseAdapter(url) {
|
|
|
1220
1220
|
import { ChatGoogleGenerativeAI } from "@langchain/google-genai";
|
|
1221
1221
|
import { ChatOpenAI } from "@langchain/openai";
|
|
1222
1222
|
import { ChatAnthropic } from "@langchain/anthropic";
|
|
1223
|
-
function
|
|
1224
|
-
const
|
|
1223
|
+
function isReasoningOrFixedTempModel(provider, model) {
|
|
1224
|
+
const normalized = model.toLowerCase().replace(/^models\//, "").trim();
|
|
1225
1225
|
switch (provider) {
|
|
1226
1226
|
case "google":
|
|
1227
|
-
return
|
|
1227
|
+
return normalized.includes("thinking") || normalized.includes("reasoning") || /^gemini-([3-9]|\d{2,}|2\.[5-9])/i.test(normalized);
|
|
1228
|
+
case "openai":
|
|
1229
|
+
return /^o[1-9]/i.test(normalized) || normalized.includes("reasoning") || normalized.includes("thinking");
|
|
1230
|
+
case "anthropic":
|
|
1231
|
+
return normalized.includes("thinking") || normalized.includes("reasoning");
|
|
1232
|
+
default:
|
|
1233
|
+
return false;
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
function resolveTemperature(provider, model, requestedTemperature) {
|
|
1237
|
+
if (isReasoningOrFixedTempModel(provider, model)) {
|
|
1238
|
+
return void 0;
|
|
1239
|
+
}
|
|
1240
|
+
return requestedTemperature ?? 0;
|
|
1241
|
+
}
|
|
1242
|
+
function attachTemperatureFallback(model) {
|
|
1243
|
+
const originalInvoke = model.invoke.bind(model);
|
|
1244
|
+
model.invoke = (async (input3, options) => {
|
|
1245
|
+
try {
|
|
1246
|
+
return await originalInvoke(input3, options);
|
|
1247
|
+
} catch (err) {
|
|
1248
|
+
const errMsg = err?.message || String(err);
|
|
1249
|
+
const isTempError = errMsg.toLowerCase().includes("temperature") && (errMsg.toLowerCase().includes("unsupported") || errMsg.toLowerCase().includes("does not support") || errMsg.toLowerCase().includes("not support"));
|
|
1250
|
+
if (isTempError) {
|
|
1251
|
+
model.temperature = void 0;
|
|
1252
|
+
if (model.client?.generationConfig) {
|
|
1253
|
+
delete model.client.generationConfig.temperature;
|
|
1254
|
+
}
|
|
1255
|
+
return await originalInvoke(input3, options);
|
|
1256
|
+
}
|
|
1257
|
+
throw err;
|
|
1258
|
+
}
|
|
1259
|
+
});
|
|
1260
|
+
return model;
|
|
1261
|
+
}
|
|
1262
|
+
function createChatModel(options) {
|
|
1263
|
+
const { provider, model, apiKey, temperature: requestedTemp } = options;
|
|
1264
|
+
const temperature = resolveTemperature(provider, model, requestedTemp);
|
|
1265
|
+
let chatModel;
|
|
1266
|
+
switch (provider) {
|
|
1267
|
+
case "google": {
|
|
1268
|
+
const config = {
|
|
1228
1269
|
model,
|
|
1229
1270
|
apiKey,
|
|
1230
|
-
temperature,
|
|
1231
1271
|
maxRetries: 2
|
|
1232
|
-
}
|
|
1233
|
-
|
|
1234
|
-
|
|
1272
|
+
};
|
|
1273
|
+
if (temperature !== void 0) {
|
|
1274
|
+
config.temperature = temperature;
|
|
1275
|
+
}
|
|
1276
|
+
chatModel = new ChatGoogleGenerativeAI(config);
|
|
1277
|
+
break;
|
|
1278
|
+
}
|
|
1279
|
+
case "openai": {
|
|
1280
|
+
const config = {
|
|
1235
1281
|
modelName: model,
|
|
1236
1282
|
apiKey,
|
|
1237
|
-
temperature,
|
|
1238
1283
|
maxRetries: 2
|
|
1239
|
-
}
|
|
1240
|
-
|
|
1241
|
-
|
|
1284
|
+
};
|
|
1285
|
+
if (temperature !== void 0) {
|
|
1286
|
+
config.temperature = temperature;
|
|
1287
|
+
}
|
|
1288
|
+
chatModel = new ChatOpenAI(config);
|
|
1289
|
+
break;
|
|
1290
|
+
}
|
|
1291
|
+
case "anthropic": {
|
|
1292
|
+
const config = {
|
|
1242
1293
|
modelName: model,
|
|
1243
1294
|
anthropicApiKey: apiKey,
|
|
1244
|
-
temperature,
|
|
1245
1295
|
maxRetries: 2
|
|
1246
|
-
}
|
|
1296
|
+
};
|
|
1297
|
+
if (temperature !== void 0) {
|
|
1298
|
+
config.temperature = temperature;
|
|
1299
|
+
}
|
|
1300
|
+
chatModel = new ChatAnthropic(config);
|
|
1301
|
+
break;
|
|
1302
|
+
}
|
|
1247
1303
|
default:
|
|
1248
1304
|
throw new Error(`Unsupported LLM provider: ${provider}`);
|
|
1249
1305
|
}
|
|
1306
|
+
return attachTemperatureFallback(chatModel);
|
|
1250
1307
|
}
|
|
1251
1308
|
|
|
1252
1309
|
// src/repl.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sandal-db",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "SANDAL - Safe Agentic Natural-language Database Access Layer. Production-grade agentic database assistant CLI using LangGraph and LLMs (Google Gemini, OpenAI, Anthropic)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|