@deepagents/text2sql 0.17.1 → 0.19.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/dist/index.js +343 -18
- package/dist/index.js.map +2 -2
- package/dist/lib/agents/result-tools.d.ts +15 -10
- package/dist/lib/agents/result-tools.d.ts.map +1 -1
- package/dist/lib/agents/sql.agent.d.ts +2 -4
- package/dist/lib/agents/sql.agent.d.ts.map +1 -1
- package/dist/lib/sql.d.ts.map +1 -1
- package/dist/lib/synthesis/index.js +30 -20
- package/dist/lib/synthesis/index.js.map +2 -2
- package/dist/lib/synthesis/synthesizers/depth-evolver.d.ts.map +1 -1
- package/dist/lib/synthesis/synthesizers/schema-synthesizer.d.ts.map +1 -1
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -519,7 +519,8 @@ import {
|
|
|
519
519
|
Bash,
|
|
520
520
|
MountableFs,
|
|
521
521
|
OverlayFs,
|
|
522
|
-
defineCommand
|
|
522
|
+
defineCommand,
|
|
523
|
+
parse
|
|
523
524
|
} from "just-bash";
|
|
524
525
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
525
526
|
import * as path from "node:path";
|
|
@@ -650,6 +651,300 @@ function createSqlCommand(adapter, metaStore) {
|
|
|
650
651
|
}
|
|
651
652
|
});
|
|
652
653
|
}
|
|
654
|
+
var BLOCKED_DB_CLIENT_COMMANDS = /* @__PURE__ */ new Set([
|
|
655
|
+
"psql",
|
|
656
|
+
"sqlite3",
|
|
657
|
+
"mysql",
|
|
658
|
+
"duckdb"
|
|
659
|
+
]);
|
|
660
|
+
var BLOCKED_RAW_SQL_COMMANDS = /* @__PURE__ */ new Set(["select", "with"]);
|
|
661
|
+
var ALLOWED_SQL_PROXY_SUBCOMMANDS = /* @__PURE__ */ new Set(["run", "validate"]);
|
|
662
|
+
var SQL_PROXY_ENFORCEMENT_MESSAGE = [
|
|
663
|
+
"Direct database querying through bash is blocked.",
|
|
664
|
+
"Use SQL proxy commands in this order:",
|
|
665
|
+
'1) sql validate "SELECT ..."',
|
|
666
|
+
'2) sql run "SELECT ..."'
|
|
667
|
+
].join("\n");
|
|
668
|
+
function cloneInspectionContext(context) {
|
|
669
|
+
return {
|
|
670
|
+
functionDefinitions: new Map(context.functionDefinitions),
|
|
671
|
+
callStack: new Set(context.callStack)
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
function asStaticWordText(word) {
|
|
675
|
+
if (!word) {
|
|
676
|
+
return null;
|
|
677
|
+
}
|
|
678
|
+
return asStaticWordPartText(
|
|
679
|
+
word.parts
|
|
680
|
+
);
|
|
681
|
+
}
|
|
682
|
+
function asStaticWordPartText(parts) {
|
|
683
|
+
let text = "";
|
|
684
|
+
for (const part of parts) {
|
|
685
|
+
const type = part.type;
|
|
686
|
+
if (type === "Literal" || type === "SingleQuoted" || type === "Escaped") {
|
|
687
|
+
if (typeof part.value !== "string") {
|
|
688
|
+
return null;
|
|
689
|
+
}
|
|
690
|
+
text += part.value;
|
|
691
|
+
continue;
|
|
692
|
+
}
|
|
693
|
+
if (type === "DoubleQuoted") {
|
|
694
|
+
if (!Array.isArray(part.parts)) {
|
|
695
|
+
return null;
|
|
696
|
+
}
|
|
697
|
+
const inner = asStaticWordPartText(
|
|
698
|
+
part.parts
|
|
699
|
+
);
|
|
700
|
+
if (inner == null) {
|
|
701
|
+
return null;
|
|
702
|
+
}
|
|
703
|
+
text += inner;
|
|
704
|
+
continue;
|
|
705
|
+
}
|
|
706
|
+
return null;
|
|
707
|
+
}
|
|
708
|
+
return text;
|
|
709
|
+
}
|
|
710
|
+
function isScriptNode(value) {
|
|
711
|
+
if (typeof value !== "object" || value === null) {
|
|
712
|
+
return false;
|
|
713
|
+
}
|
|
714
|
+
const node = value;
|
|
715
|
+
return node.type === "Script" && Array.isArray(node.statements);
|
|
716
|
+
}
|
|
717
|
+
function scriptContainsBlockedCommand(script, context) {
|
|
718
|
+
return statementsContainBlockedCommand(script.statements, context);
|
|
719
|
+
}
|
|
720
|
+
function statementsContainBlockedCommand(statements, context) {
|
|
721
|
+
for (const statement of statements) {
|
|
722
|
+
if (statementContainsBlockedCommand(statement, context)) {
|
|
723
|
+
return true;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
return false;
|
|
727
|
+
}
|
|
728
|
+
function statementContainsBlockedCommand(statement, context) {
|
|
729
|
+
for (const pipeline of statement.pipelines) {
|
|
730
|
+
if (pipelineContainsBlockedCommand(pipeline, context)) {
|
|
731
|
+
return true;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
return false;
|
|
735
|
+
}
|
|
736
|
+
function pipelineContainsBlockedCommand(pipeline, context) {
|
|
737
|
+
for (const command of pipeline.commands) {
|
|
738
|
+
if (command.type === "FunctionDef") {
|
|
739
|
+
context.functionDefinitions.set(command.name, command);
|
|
740
|
+
continue;
|
|
741
|
+
}
|
|
742
|
+
if (commandContainsBlockedCommand(command, context)) {
|
|
743
|
+
return true;
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
return false;
|
|
747
|
+
}
|
|
748
|
+
function stringCommandContainsBlockedCommand(command, context) {
|
|
749
|
+
let script;
|
|
750
|
+
try {
|
|
751
|
+
script = parse(command);
|
|
752
|
+
} catch {
|
|
753
|
+
return false;
|
|
754
|
+
}
|
|
755
|
+
return scriptContainsBlockedCommand(script, cloneInspectionContext(context));
|
|
756
|
+
}
|
|
757
|
+
function wordContainsBlockedCommand(word, context) {
|
|
758
|
+
if (!word) {
|
|
759
|
+
return false;
|
|
760
|
+
}
|
|
761
|
+
return wordPartContainsBlockedCommand(
|
|
762
|
+
word.parts,
|
|
763
|
+
context
|
|
764
|
+
);
|
|
765
|
+
}
|
|
766
|
+
function wordPartContainsBlockedCommand(parts, context) {
|
|
767
|
+
for (const part of parts) {
|
|
768
|
+
if (partContainsBlockedCommand(part, context)) {
|
|
769
|
+
return true;
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
return false;
|
|
773
|
+
}
|
|
774
|
+
function partContainsBlockedCommand(node, context) {
|
|
775
|
+
const type = node.type;
|
|
776
|
+
if (type === "CommandSubstitution" || type === "ProcessSubstitution") {
|
|
777
|
+
if (isScriptNode(node.body)) {
|
|
778
|
+
return scriptContainsBlockedCommand(
|
|
779
|
+
node.body,
|
|
780
|
+
cloneInspectionContext(context)
|
|
781
|
+
);
|
|
782
|
+
}
|
|
783
|
+
return false;
|
|
784
|
+
}
|
|
785
|
+
if (type === "ArithCommandSubst" && typeof node.command === "string") {
|
|
786
|
+
return stringCommandContainsBlockedCommand(node.command, context);
|
|
787
|
+
}
|
|
788
|
+
for (const value of Object.values(node)) {
|
|
789
|
+
if (Array.isArray(value)) {
|
|
790
|
+
for (const item of value) {
|
|
791
|
+
if (typeof item === "object" && item !== null) {
|
|
792
|
+
if (partContainsBlockedCommand(item, context)) {
|
|
793
|
+
return true;
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
continue;
|
|
798
|
+
}
|
|
799
|
+
if (typeof value === "object" && value !== null) {
|
|
800
|
+
if (partContainsBlockedCommand(value, context)) {
|
|
801
|
+
return true;
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
return false;
|
|
806
|
+
}
|
|
807
|
+
function functionInvocationContainsBlockedCommand(functionName, context) {
|
|
808
|
+
const definition = context.functionDefinitions.get(functionName);
|
|
809
|
+
if (!definition) {
|
|
810
|
+
return false;
|
|
811
|
+
}
|
|
812
|
+
if (context.callStack.has(functionName)) {
|
|
813
|
+
return false;
|
|
814
|
+
}
|
|
815
|
+
const invocationContext = cloneInspectionContext(context);
|
|
816
|
+
invocationContext.callStack.add(functionName);
|
|
817
|
+
return commandContainsBlockedCommand(definition.body, invocationContext);
|
|
818
|
+
}
|
|
819
|
+
function commandContainsBlockedCommand(command, context) {
|
|
820
|
+
switch (command.type) {
|
|
821
|
+
case "SimpleCommand":
|
|
822
|
+
return isBlockedSimpleCommand(command, context);
|
|
823
|
+
case "If":
|
|
824
|
+
return command.clauses.some(
|
|
825
|
+
(clause) => statementsContainBlockedCommand(
|
|
826
|
+
clause.condition,
|
|
827
|
+
cloneInspectionContext(context)
|
|
828
|
+
) || statementsContainBlockedCommand(
|
|
829
|
+
clause.body,
|
|
830
|
+
cloneInspectionContext(context)
|
|
831
|
+
)
|
|
832
|
+
) || (command.elseBody ? statementsContainBlockedCommand(
|
|
833
|
+
command.elseBody,
|
|
834
|
+
cloneInspectionContext(context)
|
|
835
|
+
) : false);
|
|
836
|
+
case "For":
|
|
837
|
+
case "CStyleFor":
|
|
838
|
+
return statementsContainBlockedCommand(
|
|
839
|
+
command.body,
|
|
840
|
+
cloneInspectionContext(context)
|
|
841
|
+
);
|
|
842
|
+
case "While":
|
|
843
|
+
case "Until":
|
|
844
|
+
return statementsContainBlockedCommand(
|
|
845
|
+
command.condition,
|
|
846
|
+
cloneInspectionContext(context)
|
|
847
|
+
) || statementsContainBlockedCommand(
|
|
848
|
+
command.body,
|
|
849
|
+
cloneInspectionContext(context)
|
|
850
|
+
);
|
|
851
|
+
case "Case":
|
|
852
|
+
return command.items.some(
|
|
853
|
+
(item) => statementsContainBlockedCommand(
|
|
854
|
+
item.body,
|
|
855
|
+
cloneInspectionContext(context)
|
|
856
|
+
)
|
|
857
|
+
);
|
|
858
|
+
case "Subshell":
|
|
859
|
+
case "Group":
|
|
860
|
+
return statementsContainBlockedCommand(
|
|
861
|
+
command.body,
|
|
862
|
+
cloneInspectionContext(context)
|
|
863
|
+
);
|
|
864
|
+
case "FunctionDef":
|
|
865
|
+
return false;
|
|
866
|
+
case "ArithmeticCommand":
|
|
867
|
+
case "ConditionalCommand":
|
|
868
|
+
return false;
|
|
869
|
+
default: {
|
|
870
|
+
const _unreachable = command;
|
|
871
|
+
return _unreachable;
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
function isBlockedSimpleCommand(command, context) {
|
|
876
|
+
if (wordContainsBlockedCommand(command.name, context)) {
|
|
877
|
+
return true;
|
|
878
|
+
}
|
|
879
|
+
if (command.args.some((arg) => wordContainsBlockedCommand(arg, context))) {
|
|
880
|
+
return true;
|
|
881
|
+
}
|
|
882
|
+
if (command.assignments.some(
|
|
883
|
+
(assignment) => wordContainsBlockedCommand(assignment.value, context) || (assignment.array?.some(
|
|
884
|
+
(value) => wordContainsBlockedCommand(value, context)
|
|
885
|
+
) ?? false)
|
|
886
|
+
)) {
|
|
887
|
+
return true;
|
|
888
|
+
}
|
|
889
|
+
if (command.redirections.some((redirection) => {
|
|
890
|
+
if (redirection.target.type === "Word") {
|
|
891
|
+
return wordContainsBlockedCommand(
|
|
892
|
+
redirection.target,
|
|
893
|
+
context
|
|
894
|
+
);
|
|
895
|
+
}
|
|
896
|
+
if (redirection.target.type === "HereDoc" && redirection.target.content) {
|
|
897
|
+
return wordContainsBlockedCommand(redirection.target.content, context);
|
|
898
|
+
}
|
|
899
|
+
return false;
|
|
900
|
+
})) {
|
|
901
|
+
return true;
|
|
902
|
+
}
|
|
903
|
+
const commandName = asStaticWordText(command.name);
|
|
904
|
+
if (!commandName) {
|
|
905
|
+
return false;
|
|
906
|
+
}
|
|
907
|
+
const normalizedName = path.posix.basename(commandName).toLowerCase();
|
|
908
|
+
if (BLOCKED_DB_CLIENT_COMMANDS.has(normalizedName)) {
|
|
909
|
+
return true;
|
|
910
|
+
}
|
|
911
|
+
if (BLOCKED_RAW_SQL_COMMANDS.has(normalizedName)) {
|
|
912
|
+
return true;
|
|
913
|
+
}
|
|
914
|
+
if (normalizedName === "sql") {
|
|
915
|
+
const subcommand = asStaticWordText(command.args[0])?.toLowerCase();
|
|
916
|
+
return !subcommand || !ALLOWED_SQL_PROXY_SUBCOMMANDS.has(subcommand);
|
|
917
|
+
}
|
|
918
|
+
if (functionInvocationContainsBlockedCommand(commandName, context)) {
|
|
919
|
+
return true;
|
|
920
|
+
}
|
|
921
|
+
return false;
|
|
922
|
+
}
|
|
923
|
+
function getSqlProxyEnforcementResult(command) {
|
|
924
|
+
const trimmed = command.trim();
|
|
925
|
+
if (!trimmed) {
|
|
926
|
+
return null;
|
|
927
|
+
}
|
|
928
|
+
let script;
|
|
929
|
+
try {
|
|
930
|
+
script = parse(trimmed);
|
|
931
|
+
} catch {
|
|
932
|
+
return null;
|
|
933
|
+
}
|
|
934
|
+
const blocked = scriptContainsBlockedCommand(script, {
|
|
935
|
+
functionDefinitions: /* @__PURE__ */ new Map(),
|
|
936
|
+
callStack: /* @__PURE__ */ new Set()
|
|
937
|
+
});
|
|
938
|
+
if (!blocked) {
|
|
939
|
+
return null;
|
|
940
|
+
}
|
|
941
|
+
return {
|
|
942
|
+
stdout: "",
|
|
943
|
+
stderr: `${SQL_PROXY_ENFORCEMENT_MESSAGE}
|
|
944
|
+
`,
|
|
945
|
+
exitCode: 1
|
|
946
|
+
};
|
|
947
|
+
}
|
|
653
948
|
async function createResultTools(options) {
|
|
654
949
|
const { adapter, skillMounts, filesystem: baseFs } = options;
|
|
655
950
|
const metaStore = new AsyncLocalStorage();
|
|
@@ -685,6 +980,16 @@ async function createResultTools(options) {
|
|
|
685
980
|
return { result };
|
|
686
981
|
}
|
|
687
982
|
});
|
|
983
|
+
const guardedSandbox = {
|
|
984
|
+
...sandbox,
|
|
985
|
+
executeCommand: async (command) => {
|
|
986
|
+
const blockedResult = getSqlProxyEnforcementResult(command);
|
|
987
|
+
if (blockedResult) {
|
|
988
|
+
return blockedResult;
|
|
989
|
+
}
|
|
990
|
+
return sandbox.executeCommand(command);
|
|
991
|
+
}
|
|
992
|
+
};
|
|
688
993
|
const bash = tool2({
|
|
689
994
|
...tools2.bash,
|
|
690
995
|
inputSchema: z3.object({
|
|
@@ -696,6 +1001,10 @@ async function createResultTools(options) {
|
|
|
696
1001
|
if (!execute) {
|
|
697
1002
|
throw new Error("bash tool execution is not available");
|
|
698
1003
|
}
|
|
1004
|
+
const blockedResult = getSqlProxyEnforcementResult(command);
|
|
1005
|
+
if (blockedResult) {
|
|
1006
|
+
return blockedResult;
|
|
1007
|
+
}
|
|
699
1008
|
return metaStore.run({}, async () => {
|
|
700
1009
|
const result = await execute({ command }, execOptions);
|
|
701
1010
|
const meta = metaStore.getStore()?.value;
|
|
@@ -703,12 +1012,15 @@ async function createResultTools(options) {
|
|
|
703
1012
|
});
|
|
704
1013
|
},
|
|
705
1014
|
toModelOutput: ({ output }) => {
|
|
1015
|
+
if (typeof output !== "object" || output === null) {
|
|
1016
|
+
return { type: "json", value: output };
|
|
1017
|
+
}
|
|
706
1018
|
const { meta, ...rest } = output;
|
|
707
1019
|
return { type: "json", value: rest };
|
|
708
1020
|
}
|
|
709
1021
|
});
|
|
710
1022
|
return {
|
|
711
|
-
sandbox,
|
|
1023
|
+
sandbox: guardedSandbox,
|
|
712
1024
|
tools: {
|
|
713
1025
|
...tools2,
|
|
714
1026
|
bash
|
|
@@ -779,8 +1091,7 @@ async function toSql(options) {
|
|
|
779
1091
|
role: "You are an expert SQL query generator. You translate natural language questions into precise, efficient SQL queries based on the provided database schema.",
|
|
780
1092
|
objective: "Translate natural language questions into precise, efficient SQL queries"
|
|
781
1093
|
}),
|
|
782
|
-
...options.
|
|
783
|
-
...options.schemaFragments
|
|
1094
|
+
...options.fragments
|
|
784
1095
|
);
|
|
785
1096
|
if (errors.length) {
|
|
786
1097
|
context.set(
|
|
@@ -801,19 +1112,21 @@ async function toSql(options) {
|
|
|
801
1112
|
const sqlOutput = structuredOutput2({
|
|
802
1113
|
model,
|
|
803
1114
|
context,
|
|
804
|
-
schema: z4.
|
|
805
|
-
z4.
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
1115
|
+
schema: z4.object({
|
|
1116
|
+
result: z4.union([
|
|
1117
|
+
z4.object({
|
|
1118
|
+
sql: z4.string().describe("The SQL query that answers the question"),
|
|
1119
|
+
reasoning: z4.string().describe("The reasoning steps taken to generate the SQL")
|
|
1120
|
+
}),
|
|
1121
|
+
z4.object({
|
|
1122
|
+
error: z4.string().describe(
|
|
1123
|
+
"Error message explaining why the question cannot be answered with the given schema"
|
|
1124
|
+
)
|
|
1125
|
+
})
|
|
1126
|
+
])
|
|
1127
|
+
})
|
|
815
1128
|
});
|
|
816
|
-
const output = await sqlOutput.generate();
|
|
1129
|
+
const { result: output } = await sqlOutput.generate();
|
|
817
1130
|
if ("error" in output) {
|
|
818
1131
|
throw new UnanswerableSQLError(output.error);
|
|
819
1132
|
}
|
|
@@ -843,6 +1156,16 @@ function formatErrorMessage(error) {
|
|
|
843
1156
|
}
|
|
844
1157
|
return error.message;
|
|
845
1158
|
}
|
|
1159
|
+
function isModelUnavailableError(error) {
|
|
1160
|
+
if (!APICallError.isInstance(error)) {
|
|
1161
|
+
return false;
|
|
1162
|
+
}
|
|
1163
|
+
const message2 = error.message.toLowerCase();
|
|
1164
|
+
const responseBody = (error.responseBody ?? "").toLowerCase();
|
|
1165
|
+
const is404ModelError = error.statusCode === 404 && (message2.includes("model") || responseBody.includes("model_not_found"));
|
|
1166
|
+
const errorCode = typeof error.data === "object" && error.data !== null && "error" in error.data && typeof error.data.error === "object" && error.data.error !== null && "code" in error.data.error && typeof error.data.error.code === "string" ? error.data.error.code.toLowerCase() : void 0;
|
|
1167
|
+
return is404ModelError || errorCode === "model_not_found" || responseBody.includes('"code":"model_not_found"') || message2.includes("model") && message2.includes("does not exist or you do not have access to it");
|
|
1168
|
+
}
|
|
846
1169
|
async function withRetry(computation, options = { retries: 3 }) {
|
|
847
1170
|
const errors = [];
|
|
848
1171
|
let attempts = 0;
|
|
@@ -856,6 +1179,9 @@ async function withRetry(computation, options = { retries: 3 }) {
|
|
|
856
1179
|
if (UnanswerableSQLError.isInstance(context.error)) {
|
|
857
1180
|
return false;
|
|
858
1181
|
}
|
|
1182
|
+
if (isModelUnavailableError(context.error)) {
|
|
1183
|
+
return false;
|
|
1184
|
+
}
|
|
859
1185
|
if (SQLValidationError.isInstance(context.error)) {
|
|
860
1186
|
return true;
|
|
861
1187
|
}
|
|
@@ -4302,8 +4628,7 @@ var Text2Sql = class {
|
|
|
4302
4628
|
const result = await toSql({
|
|
4303
4629
|
input,
|
|
4304
4630
|
adapter: this.#config.adapter,
|
|
4305
|
-
schemaFragments,
|
|
4306
|
-
instructions: [],
|
|
4631
|
+
fragments: schemaFragments,
|
|
4307
4632
|
model: this.#config.model
|
|
4308
4633
|
});
|
|
4309
4634
|
return result.sql;
|