@deepagents/text2sql 0.19.0 → 0.22.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.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +759 -137
- package/dist/index.js.map +4 -4
- package/dist/lib/agents/exceptions.d.ts +20 -0
- package/dist/lib/agents/exceptions.d.ts.map +1 -0
- package/dist/lib/agents/result-tools.d.ts.map +1 -1
- package/dist/lib/agents/sql.agent.d.ts +0 -17
- package/dist/lib/agents/sql.agent.d.ts.map +1 -1
- package/dist/lib/synthesis/index.js +359 -103
- package/dist/lib/synthesis/index.js.map +4 -4
- 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 +4 -4
package/dist/index.js
CHANGED
|
@@ -511,6 +511,32 @@ var fragments = [
|
|
|
511
511
|
hint("When validating user SQL, explain any errors clearly")
|
|
512
512
|
];
|
|
513
513
|
|
|
514
|
+
// packages/text2sql/src/lib/agents/exceptions.ts
|
|
515
|
+
var sqlValidationMarker = Symbol("SQLValidationError");
|
|
516
|
+
var unanswerableSqlMarker = Symbol("UnanswerableSQLError");
|
|
517
|
+
var SQLValidationError = class _SQLValidationError extends Error {
|
|
518
|
+
[sqlValidationMarker];
|
|
519
|
+
constructor(message2) {
|
|
520
|
+
super(message2);
|
|
521
|
+
this.name = "SQLValidationError";
|
|
522
|
+
this[sqlValidationMarker] = true;
|
|
523
|
+
}
|
|
524
|
+
static isInstance(error) {
|
|
525
|
+
return error instanceof _SQLValidationError && error[sqlValidationMarker] === true;
|
|
526
|
+
}
|
|
527
|
+
};
|
|
528
|
+
var UnanswerableSQLError = class _UnanswerableSQLError extends Error {
|
|
529
|
+
[unanswerableSqlMarker];
|
|
530
|
+
constructor(message2) {
|
|
531
|
+
super(message2);
|
|
532
|
+
this.name = "UnanswerableSQLError";
|
|
533
|
+
this[unanswerableSqlMarker] = true;
|
|
534
|
+
}
|
|
535
|
+
static isInstance(error) {
|
|
536
|
+
return error instanceof _UnanswerableSQLError && error[unanswerableSqlMarker] === true;
|
|
537
|
+
}
|
|
538
|
+
};
|
|
539
|
+
|
|
514
540
|
// packages/text2sql/src/lib/agents/result-tools.ts
|
|
515
541
|
import { tool as tool2 } from "ai";
|
|
516
542
|
import { createBashTool } from "bash-tool";
|
|
@@ -659,6 +685,14 @@ var BLOCKED_DB_CLIENT_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
659
685
|
]);
|
|
660
686
|
var BLOCKED_RAW_SQL_COMMANDS = /* @__PURE__ */ new Set(["select", "with"]);
|
|
661
687
|
var ALLOWED_SQL_PROXY_SUBCOMMANDS = /* @__PURE__ */ new Set(["run", "validate"]);
|
|
688
|
+
var SHELL_INTERPRETER_COMMANDS = /* @__PURE__ */ new Set([
|
|
689
|
+
"bash",
|
|
690
|
+
"sh",
|
|
691
|
+
"zsh",
|
|
692
|
+
"dash",
|
|
693
|
+
"ksh"
|
|
694
|
+
]);
|
|
695
|
+
var WRAPPER_COMMANDS = /* @__PURE__ */ new Set(["env", "command", "eval"]);
|
|
662
696
|
var SQL_PROXY_ENFORCEMENT_MESSAGE = [
|
|
663
697
|
"Direct database querying through bash is blocked.",
|
|
664
698
|
"Use SQL proxy commands in this order:",
|
|
@@ -714,82 +748,94 @@ function isScriptNode(value) {
|
|
|
714
748
|
const node = value;
|
|
715
749
|
return node.type === "Script" && Array.isArray(node.statements);
|
|
716
750
|
}
|
|
717
|
-
function scriptContainsBlockedCommand(script, context) {
|
|
718
|
-
return statementsContainBlockedCommand(script.statements, context);
|
|
751
|
+
function scriptContainsBlockedCommand(script, context, mode = "blocked-only") {
|
|
752
|
+
return statementsContainBlockedCommand(script.statements, context, mode);
|
|
719
753
|
}
|
|
720
|
-
function statementsContainBlockedCommand(statements, context) {
|
|
754
|
+
function statementsContainBlockedCommand(statements, context, mode) {
|
|
721
755
|
for (const statement of statements) {
|
|
722
|
-
if (statementContainsBlockedCommand(statement, context)) {
|
|
756
|
+
if (statementContainsBlockedCommand(statement, context, mode)) {
|
|
723
757
|
return true;
|
|
724
758
|
}
|
|
725
759
|
}
|
|
726
760
|
return false;
|
|
727
761
|
}
|
|
728
|
-
function statementContainsBlockedCommand(statement, context) {
|
|
762
|
+
function statementContainsBlockedCommand(statement, context, mode) {
|
|
729
763
|
for (const pipeline of statement.pipelines) {
|
|
730
|
-
if (pipelineContainsBlockedCommand(pipeline, context)) {
|
|
764
|
+
if (pipelineContainsBlockedCommand(pipeline, context, mode)) {
|
|
731
765
|
return true;
|
|
732
766
|
}
|
|
733
767
|
}
|
|
734
768
|
return false;
|
|
735
769
|
}
|
|
736
|
-
function pipelineContainsBlockedCommand(pipeline, context) {
|
|
737
|
-
for (const command of pipeline.commands) {
|
|
770
|
+
function pipelineContainsBlockedCommand(pipeline, context, mode) {
|
|
771
|
+
for (const [index2, command] of pipeline.commands.entries()) {
|
|
738
772
|
if (command.type === "FunctionDef") {
|
|
739
773
|
context.functionDefinitions.set(command.name, command);
|
|
740
774
|
continue;
|
|
741
775
|
}
|
|
742
|
-
if (commandContainsBlockedCommand(command, context
|
|
776
|
+
if (commandContainsBlockedCommand(command, context, mode, {
|
|
777
|
+
stdinFromPipe: index2 > 0
|
|
778
|
+
})) {
|
|
743
779
|
return true;
|
|
744
780
|
}
|
|
745
781
|
}
|
|
746
782
|
return false;
|
|
747
783
|
}
|
|
748
|
-
function stringCommandContainsBlockedCommand(command, context) {
|
|
784
|
+
function stringCommandContainsBlockedCommand(command, context, mode = "blocked-only") {
|
|
749
785
|
let script;
|
|
750
786
|
try {
|
|
751
787
|
script = parse(command);
|
|
752
788
|
} catch {
|
|
753
789
|
return false;
|
|
754
790
|
}
|
|
755
|
-
return scriptContainsBlockedCommand(
|
|
791
|
+
return scriptContainsBlockedCommand(
|
|
792
|
+
script,
|
|
793
|
+
cloneInspectionContext(context),
|
|
794
|
+
mode
|
|
795
|
+
);
|
|
756
796
|
}
|
|
757
|
-
function wordContainsBlockedCommand(word, context) {
|
|
797
|
+
function wordContainsBlockedCommand(word, context, mode) {
|
|
758
798
|
if (!word) {
|
|
759
799
|
return false;
|
|
760
800
|
}
|
|
761
801
|
return wordPartContainsBlockedCommand(
|
|
762
802
|
word.parts,
|
|
763
|
-
context
|
|
803
|
+
context,
|
|
804
|
+
mode
|
|
764
805
|
);
|
|
765
806
|
}
|
|
766
|
-
function wordPartContainsBlockedCommand(parts, context) {
|
|
807
|
+
function wordPartContainsBlockedCommand(parts, context, mode) {
|
|
767
808
|
for (const part of parts) {
|
|
768
|
-
if (partContainsBlockedCommand(part, context)) {
|
|
809
|
+
if (partContainsBlockedCommand(part, context, mode)) {
|
|
769
810
|
return true;
|
|
770
811
|
}
|
|
771
812
|
}
|
|
772
813
|
return false;
|
|
773
814
|
}
|
|
774
|
-
function partContainsBlockedCommand(node, context) {
|
|
815
|
+
function partContainsBlockedCommand(node, context, mode) {
|
|
775
816
|
const type = node.type;
|
|
776
817
|
if (type === "CommandSubstitution" || type === "ProcessSubstitution") {
|
|
777
818
|
if (isScriptNode(node.body)) {
|
|
778
819
|
return scriptContainsBlockedCommand(
|
|
779
820
|
node.body,
|
|
780
|
-
cloneInspectionContext(context)
|
|
821
|
+
cloneInspectionContext(context),
|
|
822
|
+
mode
|
|
781
823
|
);
|
|
782
824
|
}
|
|
783
825
|
return false;
|
|
784
826
|
}
|
|
785
827
|
if (type === "ArithCommandSubst" && typeof node.command === "string") {
|
|
786
|
-
return stringCommandContainsBlockedCommand(node.command, context);
|
|
828
|
+
return stringCommandContainsBlockedCommand(node.command, context, mode);
|
|
787
829
|
}
|
|
788
830
|
for (const value of Object.values(node)) {
|
|
789
831
|
if (Array.isArray(value)) {
|
|
790
832
|
for (const item of value) {
|
|
791
833
|
if (typeof item === "object" && item !== null) {
|
|
792
|
-
if (partContainsBlockedCommand(
|
|
834
|
+
if (partContainsBlockedCommand(
|
|
835
|
+
item,
|
|
836
|
+
context,
|
|
837
|
+
mode
|
|
838
|
+
)) {
|
|
793
839
|
return true;
|
|
794
840
|
}
|
|
795
841
|
}
|
|
@@ -797,14 +843,18 @@ function partContainsBlockedCommand(node, context) {
|
|
|
797
843
|
continue;
|
|
798
844
|
}
|
|
799
845
|
if (typeof value === "object" && value !== null) {
|
|
800
|
-
if (partContainsBlockedCommand(
|
|
846
|
+
if (partContainsBlockedCommand(
|
|
847
|
+
value,
|
|
848
|
+
context,
|
|
849
|
+
mode
|
|
850
|
+
)) {
|
|
801
851
|
return true;
|
|
802
852
|
}
|
|
803
853
|
}
|
|
804
854
|
}
|
|
805
855
|
return false;
|
|
806
856
|
}
|
|
807
|
-
function functionInvocationContainsBlockedCommand(functionName, context) {
|
|
857
|
+
function functionInvocationContainsBlockedCommand(functionName, context, mode) {
|
|
808
858
|
const definition = context.functionDefinitions.get(functionName);
|
|
809
859
|
if (!definition) {
|
|
810
860
|
return false;
|
|
@@ -814,52 +864,306 @@ function functionInvocationContainsBlockedCommand(functionName, context) {
|
|
|
814
864
|
}
|
|
815
865
|
const invocationContext = cloneInspectionContext(context);
|
|
816
866
|
invocationContext.callStack.add(functionName);
|
|
817
|
-
return commandContainsBlockedCommand(
|
|
867
|
+
return commandContainsBlockedCommand(
|
|
868
|
+
definition.body,
|
|
869
|
+
invocationContext,
|
|
870
|
+
mode,
|
|
871
|
+
{ stdinFromPipe: false }
|
|
872
|
+
);
|
|
873
|
+
}
|
|
874
|
+
function isAsciiLetter(character) {
|
|
875
|
+
const charCode = character.charCodeAt(0);
|
|
876
|
+
return charCode >= 65 && charCode <= 90 || charCode >= 97 && charCode <= 122;
|
|
877
|
+
}
|
|
878
|
+
function isAsciiDigit(character) {
|
|
879
|
+
const charCode = character.charCodeAt(0);
|
|
880
|
+
return charCode >= 48 && charCode <= 57;
|
|
881
|
+
}
|
|
882
|
+
function isValidEnvVariableName(name) {
|
|
883
|
+
if (!name) {
|
|
884
|
+
return false;
|
|
885
|
+
}
|
|
886
|
+
const firstChar = name[0];
|
|
887
|
+
if (!(isAsciiLetter(firstChar) || firstChar === "_")) {
|
|
888
|
+
return false;
|
|
889
|
+
}
|
|
890
|
+
for (let index2 = 1; index2 < name.length; index2 += 1) {
|
|
891
|
+
const char = name[index2];
|
|
892
|
+
if (!(isAsciiLetter(char) || isAsciiDigit(char) || char === "_")) {
|
|
893
|
+
return false;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
return true;
|
|
897
|
+
}
|
|
898
|
+
function isEnvAssignmentToken(token) {
|
|
899
|
+
const separatorIndex = token.indexOf("=");
|
|
900
|
+
if (separatorIndex <= 0) {
|
|
901
|
+
return false;
|
|
902
|
+
}
|
|
903
|
+
return isValidEnvVariableName(token.slice(0, separatorIndex));
|
|
904
|
+
}
|
|
905
|
+
function parseShortOptionCluster(option) {
|
|
906
|
+
if (!option.startsWith("-") || option.startsWith("--") || option.length <= 1) {
|
|
907
|
+
return {
|
|
908
|
+
valid: false,
|
|
909
|
+
hasCommandFlag: false,
|
|
910
|
+
hasStdinFlag: false,
|
|
911
|
+
consumesNextArg: false
|
|
912
|
+
};
|
|
913
|
+
}
|
|
914
|
+
let hasCommandFlag = false;
|
|
915
|
+
let hasStdinFlag = false;
|
|
916
|
+
let consumesNextArg = false;
|
|
917
|
+
for (let index2 = 1; index2 < option.length; index2 += 1) {
|
|
918
|
+
const char = option[index2];
|
|
919
|
+
if (!isAsciiLetter(char)) {
|
|
920
|
+
return {
|
|
921
|
+
valid: false,
|
|
922
|
+
hasCommandFlag: false,
|
|
923
|
+
hasStdinFlag: false,
|
|
924
|
+
consumesNextArg: false
|
|
925
|
+
};
|
|
926
|
+
}
|
|
927
|
+
if (char === "c") {
|
|
928
|
+
hasCommandFlag = true;
|
|
929
|
+
} else if (char === "s") {
|
|
930
|
+
hasStdinFlag = true;
|
|
931
|
+
} else if (char === "O" || char === "o") {
|
|
932
|
+
consumesNextArg = true;
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
return { valid: true, hasCommandFlag, hasStdinFlag, consumesNextArg };
|
|
936
|
+
}
|
|
937
|
+
function getShellInvocationDescriptor(args) {
|
|
938
|
+
let readsFromStdin = false;
|
|
939
|
+
const longOptionsWithValue = /* @__PURE__ */ new Set(["--rcfile", "--init-file"]);
|
|
940
|
+
for (let index2 = 0; index2 < args.length; index2 += 1) {
|
|
941
|
+
const token = asStaticWordText(args[index2]);
|
|
942
|
+
if (token == null) {
|
|
943
|
+
return { kind: "unknown", payload: null };
|
|
944
|
+
}
|
|
945
|
+
if (token === "--") {
|
|
946
|
+
if (index2 + 1 >= args.length) {
|
|
947
|
+
break;
|
|
948
|
+
}
|
|
949
|
+
return {
|
|
950
|
+
kind: "script",
|
|
951
|
+
payload: asStaticWordText(args[index2 + 1])
|
|
952
|
+
};
|
|
953
|
+
}
|
|
954
|
+
if (token === "--command") {
|
|
955
|
+
return {
|
|
956
|
+
kind: "command",
|
|
957
|
+
payload: asStaticWordText(args[index2 + 1])
|
|
958
|
+
};
|
|
959
|
+
}
|
|
960
|
+
if (token.startsWith("--command=")) {
|
|
961
|
+
return {
|
|
962
|
+
kind: "command",
|
|
963
|
+
payload: token.slice("--command=".length)
|
|
964
|
+
};
|
|
965
|
+
}
|
|
966
|
+
if (token.startsWith("--")) {
|
|
967
|
+
if (token.includes("=")) {
|
|
968
|
+
continue;
|
|
969
|
+
}
|
|
970
|
+
if (longOptionsWithValue.has(token)) {
|
|
971
|
+
if (index2 + 1 >= args.length) {
|
|
972
|
+
return { kind: "unknown", payload: null };
|
|
973
|
+
}
|
|
974
|
+
index2 += 1;
|
|
975
|
+
}
|
|
976
|
+
continue;
|
|
977
|
+
}
|
|
978
|
+
if (token.startsWith("-") && !token.startsWith("--")) {
|
|
979
|
+
const parsed = parseShortOptionCluster(token);
|
|
980
|
+
if (!parsed.valid) {
|
|
981
|
+
return { kind: "unknown", payload: null };
|
|
982
|
+
}
|
|
983
|
+
if (parsed.hasCommandFlag) {
|
|
984
|
+
return {
|
|
985
|
+
kind: "command",
|
|
986
|
+
payload: asStaticWordText(args[index2 + 1])
|
|
987
|
+
};
|
|
988
|
+
}
|
|
989
|
+
if (parsed.hasStdinFlag) {
|
|
990
|
+
readsFromStdin = true;
|
|
991
|
+
}
|
|
992
|
+
if (parsed.consumesNextArg) {
|
|
993
|
+
if (index2 + 1 >= args.length) {
|
|
994
|
+
return { kind: "unknown", payload: null };
|
|
995
|
+
}
|
|
996
|
+
index2 += 1;
|
|
997
|
+
}
|
|
998
|
+
continue;
|
|
999
|
+
}
|
|
1000
|
+
return {
|
|
1001
|
+
kind: "script",
|
|
1002
|
+
payload: token
|
|
1003
|
+
};
|
|
1004
|
+
}
|
|
1005
|
+
if (readsFromStdin) {
|
|
1006
|
+
return { kind: "stdin", payload: null };
|
|
1007
|
+
}
|
|
1008
|
+
return { kind: "none", payload: null };
|
|
1009
|
+
}
|
|
1010
|
+
function getHereDocPayload(redirections) {
|
|
1011
|
+
const payloads = [];
|
|
1012
|
+
for (const redirection of redirections) {
|
|
1013
|
+
if (redirection.target.type !== "HereDoc") {
|
|
1014
|
+
continue;
|
|
1015
|
+
}
|
|
1016
|
+
if (!redirection.target.content) {
|
|
1017
|
+
payloads.push("");
|
|
1018
|
+
continue;
|
|
1019
|
+
}
|
|
1020
|
+
const payload = asStaticWordText(redirection.target.content);
|
|
1021
|
+
if (payload == null) {
|
|
1022
|
+
return { hasHereDoc: true, payload: null };
|
|
1023
|
+
}
|
|
1024
|
+
payloads.push(payload);
|
|
1025
|
+
}
|
|
1026
|
+
if (payloads.length === 0) {
|
|
1027
|
+
return { hasHereDoc: false, payload: null };
|
|
1028
|
+
}
|
|
1029
|
+
return { hasHereDoc: true, payload: payloads.join("\n") };
|
|
1030
|
+
}
|
|
1031
|
+
function joinStaticWords(words) {
|
|
1032
|
+
const tokens = [];
|
|
1033
|
+
for (const word of words) {
|
|
1034
|
+
const token = asStaticWordText(word);
|
|
1035
|
+
if (token == null) {
|
|
1036
|
+
return null;
|
|
1037
|
+
}
|
|
1038
|
+
tokens.push(token);
|
|
1039
|
+
}
|
|
1040
|
+
return tokens.join(" ");
|
|
1041
|
+
}
|
|
1042
|
+
function resolveEnvWrapperCommand(args) {
|
|
1043
|
+
let index2 = 0;
|
|
1044
|
+
while (index2 < args.length) {
|
|
1045
|
+
const token = asStaticWordText(args[index2]);
|
|
1046
|
+
if (token == null) {
|
|
1047
|
+
return { kind: "unknown" };
|
|
1048
|
+
}
|
|
1049
|
+
if (token === "--") {
|
|
1050
|
+
index2 += 1;
|
|
1051
|
+
break;
|
|
1052
|
+
}
|
|
1053
|
+
if (token === "-u" || token === "--unset" || token === "--chdir") {
|
|
1054
|
+
if (index2 + 1 >= args.length) {
|
|
1055
|
+
return { kind: "unknown" };
|
|
1056
|
+
}
|
|
1057
|
+
index2 += 2;
|
|
1058
|
+
continue;
|
|
1059
|
+
}
|
|
1060
|
+
if (token.startsWith("--unset=") || token.startsWith("--chdir=")) {
|
|
1061
|
+
index2 += 1;
|
|
1062
|
+
continue;
|
|
1063
|
+
}
|
|
1064
|
+
if (token.startsWith("-") && token !== "-" && !isEnvAssignmentToken(token)) {
|
|
1065
|
+
index2 += 1;
|
|
1066
|
+
continue;
|
|
1067
|
+
}
|
|
1068
|
+
if (isEnvAssignmentToken(token)) {
|
|
1069
|
+
index2 += 1;
|
|
1070
|
+
continue;
|
|
1071
|
+
}
|
|
1072
|
+
break;
|
|
1073
|
+
}
|
|
1074
|
+
if (index2 >= args.length) {
|
|
1075
|
+
return { kind: "none" };
|
|
1076
|
+
}
|
|
1077
|
+
return {
|
|
1078
|
+
kind: "resolved",
|
|
1079
|
+
name: args[index2],
|
|
1080
|
+
args: args.slice(index2 + 1)
|
|
1081
|
+
};
|
|
1082
|
+
}
|
|
1083
|
+
function resolveCommandWrapperCommand(args) {
|
|
1084
|
+
let index2 = 0;
|
|
1085
|
+
let lookupOnly = false;
|
|
1086
|
+
while (index2 < args.length) {
|
|
1087
|
+
const token = asStaticWordText(args[index2]);
|
|
1088
|
+
if (token == null) {
|
|
1089
|
+
return { kind: "unknown" };
|
|
1090
|
+
}
|
|
1091
|
+
if (token === "--") {
|
|
1092
|
+
index2 += 1;
|
|
1093
|
+
break;
|
|
1094
|
+
}
|
|
1095
|
+
if (token === "-v" || token === "-V") {
|
|
1096
|
+
lookupOnly = true;
|
|
1097
|
+
index2 += 1;
|
|
1098
|
+
continue;
|
|
1099
|
+
}
|
|
1100
|
+
if (token.startsWith("-") && token !== "-") {
|
|
1101
|
+
index2 += 1;
|
|
1102
|
+
continue;
|
|
1103
|
+
}
|
|
1104
|
+
break;
|
|
1105
|
+
}
|
|
1106
|
+
if (lookupOnly || index2 >= args.length) {
|
|
1107
|
+
return { kind: "none" };
|
|
1108
|
+
}
|
|
1109
|
+
return {
|
|
1110
|
+
kind: "resolved",
|
|
1111
|
+
name: args[index2],
|
|
1112
|
+
args: args.slice(index2 + 1)
|
|
1113
|
+
};
|
|
818
1114
|
}
|
|
819
|
-
function commandContainsBlockedCommand(command, context) {
|
|
1115
|
+
function commandContainsBlockedCommand(command, context, mode, options = { stdinFromPipe: false }) {
|
|
820
1116
|
switch (command.type) {
|
|
821
1117
|
case "SimpleCommand":
|
|
822
|
-
return isBlockedSimpleCommand(command, context);
|
|
1118
|
+
return isBlockedSimpleCommand(command, context, mode, options);
|
|
823
1119
|
case "If":
|
|
824
1120
|
return command.clauses.some(
|
|
825
1121
|
(clause) => statementsContainBlockedCommand(
|
|
826
1122
|
clause.condition,
|
|
827
|
-
cloneInspectionContext(context)
|
|
1123
|
+
cloneInspectionContext(context),
|
|
1124
|
+
mode
|
|
828
1125
|
) || statementsContainBlockedCommand(
|
|
829
1126
|
clause.body,
|
|
830
|
-
cloneInspectionContext(context)
|
|
1127
|
+
cloneInspectionContext(context),
|
|
1128
|
+
mode
|
|
831
1129
|
)
|
|
832
1130
|
) || (command.elseBody ? statementsContainBlockedCommand(
|
|
833
1131
|
command.elseBody,
|
|
834
|
-
cloneInspectionContext(context)
|
|
1132
|
+
cloneInspectionContext(context),
|
|
1133
|
+
mode
|
|
835
1134
|
) : false);
|
|
836
1135
|
case "For":
|
|
837
1136
|
case "CStyleFor":
|
|
838
1137
|
return statementsContainBlockedCommand(
|
|
839
1138
|
command.body,
|
|
840
|
-
cloneInspectionContext(context)
|
|
1139
|
+
cloneInspectionContext(context),
|
|
1140
|
+
mode
|
|
841
1141
|
);
|
|
842
1142
|
case "While":
|
|
843
1143
|
case "Until":
|
|
844
1144
|
return statementsContainBlockedCommand(
|
|
845
1145
|
command.condition,
|
|
846
|
-
cloneInspectionContext(context)
|
|
1146
|
+
cloneInspectionContext(context),
|
|
1147
|
+
mode
|
|
847
1148
|
) || statementsContainBlockedCommand(
|
|
848
1149
|
command.body,
|
|
849
|
-
cloneInspectionContext(context)
|
|
1150
|
+
cloneInspectionContext(context),
|
|
1151
|
+
mode
|
|
850
1152
|
);
|
|
851
1153
|
case "Case":
|
|
852
1154
|
return command.items.some(
|
|
853
1155
|
(item) => statementsContainBlockedCommand(
|
|
854
1156
|
item.body,
|
|
855
|
-
cloneInspectionContext(context)
|
|
1157
|
+
cloneInspectionContext(context),
|
|
1158
|
+
mode
|
|
856
1159
|
)
|
|
857
1160
|
);
|
|
858
1161
|
case "Subshell":
|
|
859
1162
|
case "Group":
|
|
860
1163
|
return statementsContainBlockedCommand(
|
|
861
1164
|
command.body,
|
|
862
|
-
cloneInspectionContext(context)
|
|
1165
|
+
cloneInspectionContext(context),
|
|
1166
|
+
mode
|
|
863
1167
|
);
|
|
864
1168
|
case "FunctionDef":
|
|
865
1169
|
return false;
|
|
@@ -872,16 +1176,16 @@ function commandContainsBlockedCommand(command, context) {
|
|
|
872
1176
|
}
|
|
873
1177
|
}
|
|
874
1178
|
}
|
|
875
|
-
function isBlockedSimpleCommand(command, context) {
|
|
876
|
-
if (wordContainsBlockedCommand(command.name, context)) {
|
|
1179
|
+
function isBlockedSimpleCommand(command, context, mode, options) {
|
|
1180
|
+
if (wordContainsBlockedCommand(command.name, context, mode)) {
|
|
877
1181
|
return true;
|
|
878
1182
|
}
|
|
879
|
-
if (command.args.some((arg) => wordContainsBlockedCommand(arg, context))) {
|
|
1183
|
+
if (command.args.some((arg) => wordContainsBlockedCommand(arg, context, mode))) {
|
|
880
1184
|
return true;
|
|
881
1185
|
}
|
|
882
1186
|
if (command.assignments.some(
|
|
883
|
-
(assignment) => wordContainsBlockedCommand(assignment.value, context) || (assignment.array?.some(
|
|
884
|
-
(value) => wordContainsBlockedCommand(value, context)
|
|
1187
|
+
(assignment) => wordContainsBlockedCommand(assignment.value, context, mode) || (assignment.array?.some(
|
|
1188
|
+
(value) => wordContainsBlockedCommand(value, context, mode)
|
|
885
1189
|
) ?? false)
|
|
886
1190
|
)) {
|
|
887
1191
|
return true;
|
|
@@ -890,11 +1194,16 @@ function isBlockedSimpleCommand(command, context) {
|
|
|
890
1194
|
if (redirection.target.type === "Word") {
|
|
891
1195
|
return wordContainsBlockedCommand(
|
|
892
1196
|
redirection.target,
|
|
893
|
-
context
|
|
1197
|
+
context,
|
|
1198
|
+
mode
|
|
894
1199
|
);
|
|
895
1200
|
}
|
|
896
1201
|
if (redirection.target.type === "HereDoc" && redirection.target.content) {
|
|
897
|
-
return wordContainsBlockedCommand(
|
|
1202
|
+
return wordContainsBlockedCommand(
|
|
1203
|
+
redirection.target.content,
|
|
1204
|
+
context,
|
|
1205
|
+
mode
|
|
1206
|
+
);
|
|
898
1207
|
}
|
|
899
1208
|
return false;
|
|
900
1209
|
})) {
|
|
@@ -913,9 +1222,92 @@ function isBlockedSimpleCommand(command, context) {
|
|
|
913
1222
|
}
|
|
914
1223
|
if (normalizedName === "sql") {
|
|
915
1224
|
const subcommand = asStaticWordText(command.args[0])?.toLowerCase();
|
|
916
|
-
|
|
1225
|
+
if (!subcommand) {
|
|
1226
|
+
return true;
|
|
1227
|
+
}
|
|
1228
|
+
if (mode === "block-all-sql") {
|
|
1229
|
+
return true;
|
|
1230
|
+
}
|
|
1231
|
+
return !ALLOWED_SQL_PROXY_SUBCOMMANDS.has(subcommand);
|
|
1232
|
+
}
|
|
1233
|
+
const inspectWrappedCommand = (resolved) => {
|
|
1234
|
+
if (resolved.kind === "none") {
|
|
1235
|
+
return false;
|
|
1236
|
+
}
|
|
1237
|
+
if (resolved.kind === "unknown" || !resolved.name || !resolved.args) {
|
|
1238
|
+
return true;
|
|
1239
|
+
}
|
|
1240
|
+
return isBlockedSimpleCommand(
|
|
1241
|
+
{
|
|
1242
|
+
name: resolved.name,
|
|
1243
|
+
args: resolved.args,
|
|
1244
|
+
assignments: [],
|
|
1245
|
+
redirections: []
|
|
1246
|
+
},
|
|
1247
|
+
context,
|
|
1248
|
+
"block-all-sql",
|
|
1249
|
+
options
|
|
1250
|
+
);
|
|
1251
|
+
};
|
|
1252
|
+
if (WRAPPER_COMMANDS.has(normalizedName)) {
|
|
1253
|
+
if (normalizedName === "env") {
|
|
1254
|
+
return inspectWrappedCommand(resolveEnvWrapperCommand(command.args));
|
|
1255
|
+
}
|
|
1256
|
+
if (normalizedName === "command") {
|
|
1257
|
+
return inspectWrappedCommand(resolveCommandWrapperCommand(command.args));
|
|
1258
|
+
}
|
|
1259
|
+
const evalScript = joinStaticWords(command.args);
|
|
1260
|
+
if (evalScript == null) {
|
|
1261
|
+
return true;
|
|
1262
|
+
}
|
|
1263
|
+
if (!evalScript.trim()) {
|
|
1264
|
+
return false;
|
|
1265
|
+
}
|
|
1266
|
+
return stringCommandContainsBlockedCommand(
|
|
1267
|
+
evalScript,
|
|
1268
|
+
context,
|
|
1269
|
+
"block-all-sql"
|
|
1270
|
+
);
|
|
1271
|
+
}
|
|
1272
|
+
if (SHELL_INTERPRETER_COMMANDS.has(normalizedName)) {
|
|
1273
|
+
const shellInvocation = getShellInvocationDescriptor(command.args);
|
|
1274
|
+
if (shellInvocation.kind === "unknown") {
|
|
1275
|
+
return true;
|
|
1276
|
+
}
|
|
1277
|
+
if (shellInvocation.kind === "command") {
|
|
1278
|
+
if (!shellInvocation.payload) {
|
|
1279
|
+
return true;
|
|
1280
|
+
}
|
|
1281
|
+
if (stringCommandContainsBlockedCommand(
|
|
1282
|
+
shellInvocation.payload,
|
|
1283
|
+
context,
|
|
1284
|
+
"block-all-sql"
|
|
1285
|
+
)) {
|
|
1286
|
+
return true;
|
|
1287
|
+
}
|
|
1288
|
+
return false;
|
|
1289
|
+
}
|
|
1290
|
+
const hereDoc = getHereDocPayload(command.redirections);
|
|
1291
|
+
if (hereDoc.hasHereDoc) {
|
|
1292
|
+
if (hereDoc.payload == null) {
|
|
1293
|
+
return true;
|
|
1294
|
+
}
|
|
1295
|
+
if (hereDoc.payload.trim().length > 0 && stringCommandContainsBlockedCommand(
|
|
1296
|
+
hereDoc.payload,
|
|
1297
|
+
context,
|
|
1298
|
+
"block-all-sql"
|
|
1299
|
+
)) {
|
|
1300
|
+
return true;
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
if (shellInvocation.kind === "script") {
|
|
1304
|
+
return true;
|
|
1305
|
+
}
|
|
1306
|
+
if (options.stdinFromPipe || shellInvocation.kind === "stdin") {
|
|
1307
|
+
return !hereDoc.hasHereDoc;
|
|
1308
|
+
}
|
|
917
1309
|
}
|
|
918
|
-
if (functionInvocationContainsBlockedCommand(commandName, context)) {
|
|
1310
|
+
if (functionInvocationContainsBlockedCommand(commandName, context, mode)) {
|
|
919
1311
|
return true;
|
|
920
1312
|
}
|
|
921
1313
|
return false;
|
|
@@ -1040,42 +1432,231 @@ import {
|
|
|
1040
1432
|
defaultSettingsMiddleware,
|
|
1041
1433
|
wrapLanguageModel
|
|
1042
1434
|
} from "ai";
|
|
1435
|
+
import dedent2 from "dedent";
|
|
1043
1436
|
import pRetry from "p-retry";
|
|
1044
1437
|
import z4 from "zod";
|
|
1045
1438
|
import "@deepagents/agent";
|
|
1046
1439
|
import {
|
|
1047
1440
|
ContextEngine as ContextEngine2,
|
|
1048
1441
|
InMemoryContextStore as InMemoryContextStore2,
|
|
1442
|
+
example,
|
|
1443
|
+
fragment as fragment2,
|
|
1444
|
+
guardrail,
|
|
1445
|
+
hint as hint2,
|
|
1049
1446
|
persona as persona3,
|
|
1447
|
+
policy,
|
|
1050
1448
|
structuredOutput as structuredOutput2,
|
|
1051
|
-
user as user2
|
|
1449
|
+
user as user2,
|
|
1450
|
+
workflow
|
|
1052
1451
|
} from "@deepagents/context";
|
|
1053
|
-
var RETRY_TEMPERATURES = [0, 0.
|
|
1452
|
+
var RETRY_TEMPERATURES = [0, 0.4, 0.8];
|
|
1453
|
+
var SQL_AGENT_ROLE = "Expert SQL query generator.";
|
|
1454
|
+
var SQL_AGENT_OBJECTIVE = "Generate precise SQL grounded in provided schema.";
|
|
1455
|
+
var SQL_AGENT_POLICIES = [
|
|
1456
|
+
fragment2(
|
|
1457
|
+
"schema_mapping",
|
|
1458
|
+
policy({
|
|
1459
|
+
rule: "Translate natural language into precise SQL grounded in available schema entities."
|
|
1460
|
+
}),
|
|
1461
|
+
hint2("Preserve schema spelling exactly, including typos in column names.")
|
|
1462
|
+
),
|
|
1463
|
+
fragment2(
|
|
1464
|
+
"projection_minimality",
|
|
1465
|
+
policy({
|
|
1466
|
+
rule: "Return only columns requested by the question; do not add helper columns unless explicitly requested."
|
|
1467
|
+
}),
|
|
1468
|
+
policy({
|
|
1469
|
+
rule: 'For requests of the form "X sorted/ordered by Y", project X only unless Y is explicitly requested as an output field.'
|
|
1470
|
+
}),
|
|
1471
|
+
policy({
|
|
1472
|
+
rule: "Prefer selecting schema columns directly without derived expressions when direct selection answers the request."
|
|
1473
|
+
}),
|
|
1474
|
+
hint2(
|
|
1475
|
+
"Do not include ORDER BY, GROUP BY, or JOIN helper columns in SELECT output unless the question explicitly asks for them."
|
|
1476
|
+
),
|
|
1477
|
+
policy({
|
|
1478
|
+
rule: "Use DISTINCT only when uniqueness is explicitly requested (for example distinct/unique/different/no duplicates)."
|
|
1479
|
+
}),
|
|
1480
|
+
hint2(
|
|
1481
|
+
'Do not infer DISTINCT from generic wording such as "some", plural nouns, or entity-set phrasing; for transactional/attendance-style tables, default to raw rows unless uniqueness is explicitly requested.'
|
|
1482
|
+
)
|
|
1483
|
+
),
|
|
1484
|
+
fragment2(
|
|
1485
|
+
"date_transform_safety",
|
|
1486
|
+
policy({
|
|
1487
|
+
rule: "Do not assume VARCHAR/TEXT values are parseable dates. Avoid date extraction functions on text columns by default."
|
|
1488
|
+
}),
|
|
1489
|
+
policy({
|
|
1490
|
+
rule: "Use date-part extraction only when both conditions hold: the question explicitly asks for transformation and schema values require transformation to produce that unit."
|
|
1491
|
+
}),
|
|
1492
|
+
hint2(
|
|
1493
|
+
"Do not apply SUBSTR, STRFTIME, DATE_PART, YEAR, or similar extraction functions unless the question explicitly asks for transformation and schema values require it."
|
|
1494
|
+
),
|
|
1495
|
+
hint2(
|
|
1496
|
+
"If a column already represents the requested concept (for example a stored year-like value), use the column as-is."
|
|
1497
|
+
)
|
|
1498
|
+
),
|
|
1499
|
+
fragment2(
|
|
1500
|
+
"sql_minimality",
|
|
1501
|
+
guardrail({
|
|
1502
|
+
rule: "Never hallucinate tables or columns.",
|
|
1503
|
+
reason: "Schema fidelity is required.",
|
|
1504
|
+
action: "Use only available schema entities."
|
|
1505
|
+
}),
|
|
1506
|
+
guardrail({
|
|
1507
|
+
rule: "Avoid unnecessary transformations and derived projections.",
|
|
1508
|
+
reason: "Extra transformations frequently change semantics and reduce correctness.",
|
|
1509
|
+
action: "Do not add date parsing, substring extraction, or derived columns unless explicitly required by the question or schema."
|
|
1510
|
+
})
|
|
1511
|
+
),
|
|
1512
|
+
fragment2(
|
|
1513
|
+
"preflight_checklist",
|
|
1514
|
+
workflow({
|
|
1515
|
+
task: "Final SQL preflight before returning output",
|
|
1516
|
+
steps: [
|
|
1517
|
+
"Verify selected columns match the question and remove unrequested helper projections.",
|
|
1518
|
+
"If aggregate values are used only for ranking/filtering, keep them out of SELECT unless explicitly requested.",
|
|
1519
|
+
"Prefer raw schema columns over derived expressions when raw columns already satisfy the request.",
|
|
1520
|
+
"If a candidate query uses STRFTIME, SUBSTR, DATE_PART, YEAR, or similar extraction on text-like columns, remove that transformation unless explicitly required by the question.",
|
|
1521
|
+
"Return only schema-grounded SQL using existing tables and columns."
|
|
1522
|
+
]
|
|
1523
|
+
})
|
|
1524
|
+
),
|
|
1525
|
+
fragment2(
|
|
1526
|
+
"set_semantics",
|
|
1527
|
+
policy({
|
|
1528
|
+
rule: "For questions asking where both condition A and condition B hold over an attribute, compute the intersection of qualifying sets for that attribute."
|
|
1529
|
+
}),
|
|
1530
|
+
policy({
|
|
1531
|
+
rule: "Do not force the same entity instance to satisfy both conditions unless the question explicitly requests the same person/row/entity."
|
|
1532
|
+
}),
|
|
1533
|
+
hint2(
|
|
1534
|
+
"Prefer INTERSECT (or logically equivalent set-based shape) over requiring the same physical row/entity to satisfy both conditions unless explicitly requested."
|
|
1535
|
+
),
|
|
1536
|
+
hint2(
|
|
1537
|
+
"When two conditions describe different row groups whose shared attribute is requested, build each group separately and intersect the attribute values."
|
|
1538
|
+
),
|
|
1539
|
+
hint2(
|
|
1540
|
+
"Do not collapse cross-group conditions into a single-row AND predicate when the intent is shared values across groups."
|
|
1541
|
+
),
|
|
1542
|
+
policy({
|
|
1543
|
+
rule: "If two predicates on the same field cannot both be true for one row, do not combine them with AND; use set operations across separate filtered subsets when shared values are requested."
|
|
1544
|
+
})
|
|
1545
|
+
),
|
|
1546
|
+
fragment2(
|
|
1547
|
+
"predicate_column_alignment",
|
|
1548
|
+
policy({
|
|
1549
|
+
rule: "Match literal values to semantically compatible columns. Do not compare descriptive names to identifier columns."
|
|
1550
|
+
}),
|
|
1551
|
+
hint2(
|
|
1552
|
+
"When a filter value is a descriptive label (for example a department name), join through the lookup table and filter on its name/title column, not on *_id columns."
|
|
1553
|
+
),
|
|
1554
|
+
hint2(
|
|
1555
|
+
"When relation roles are explicit in wording (for example host/home/source/destination), prefer foreign keys with matching role qualifiers over generic similarly named columns."
|
|
1556
|
+
),
|
|
1557
|
+
policy({
|
|
1558
|
+
rule: "When multiple foreign-key candidates exist, select the column whose qualifier best matches the relationship described in the question."
|
|
1559
|
+
}),
|
|
1560
|
+
policy({
|
|
1561
|
+
rule: "For hosting/held semantics, prefer host_* relationship columns when available over generic *_id alternatives."
|
|
1562
|
+
}),
|
|
1563
|
+
hint2(
|
|
1564
|
+
'Interpret wording like "held/hosted a competition or event" as a hosting relationship and map to host_* foreign keys when present.'
|
|
1565
|
+
),
|
|
1566
|
+
policy({
|
|
1567
|
+
rule: "Do not compare descriptive labels or names to *_id columns; join to the table containing the descriptive field and filter there."
|
|
1568
|
+
}),
|
|
1569
|
+
policy({
|
|
1570
|
+
rule: "Keep numeric identifiers unquoted when used as numeric equality filters unless schema indicates text identifiers."
|
|
1571
|
+
}),
|
|
1572
|
+
policy({
|
|
1573
|
+
rule: "When filtering by a descriptive label value and a related table exposes a corresponding *_name or title column, join to that table and filter on the descriptive column."
|
|
1574
|
+
})
|
|
1575
|
+
),
|
|
1576
|
+
fragment2(
|
|
1577
|
+
"ordering_semantics",
|
|
1578
|
+
policy({
|
|
1579
|
+
rule: "Respect explicit sort direction terms. If direction is not specified, use ascending order unless a superlative intent (most/least/highest/lowest) implies direction."
|
|
1580
|
+
}),
|
|
1581
|
+
policy({
|
|
1582
|
+
rule: "When ranking categories by frequency, use COUNT for ordering but keep output focused on requested category fields unless counts are explicitly requested."
|
|
1583
|
+
}),
|
|
1584
|
+
policy({
|
|
1585
|
+
rule: "Do not use DESC unless descending direction is explicit or a superlative intent requires descending ranking."
|
|
1586
|
+
}),
|
|
1587
|
+
policy({
|
|
1588
|
+
rule: 'For "most common/frequent <attribute>" requests, return the attribute value(s) only; use counts only for ordering/filtering unless the question explicitly asks to return counts.'
|
|
1589
|
+
}),
|
|
1590
|
+
hint2(
|
|
1591
|
+
'Use DESC with LIMIT 1 for "most/highest/largest"; use ASC with LIMIT 1 for "least/lowest/smallest".'
|
|
1592
|
+
)
|
|
1593
|
+
),
|
|
1594
|
+
fragment2(
|
|
1595
|
+
"negative_membership_queries",
|
|
1596
|
+
policy({
|
|
1597
|
+
rule: "For requests asking entities that did not participate/host/appear in related records, prefer NOT IN or NOT EXISTS against the related foreign-key set."
|
|
1598
|
+
}),
|
|
1599
|
+
hint2(
|
|
1600
|
+
"Map role-bearing relationship columns carefully (for example host_* foreign keys for hosting relationships) instead of generic IDs when role wording is explicit."
|
|
1601
|
+
),
|
|
1602
|
+
hint2(
|
|
1603
|
+
'For "never had/never exceeded" conditions over history tables, exclude entities via NOT IN/NOT EXISTS against the disqualifying entity-id set (often built with GROUP BY/HAVING MAX(...)).'
|
|
1604
|
+
)
|
|
1605
|
+
),
|
|
1606
|
+
fragment2(
|
|
1607
|
+
"join_completeness",
|
|
1608
|
+
policy({
|
|
1609
|
+
rule: "Preserve entity-restricting joins implied by the question. Do not widen results by querying only a broader attribute table when a subset entity table is available."
|
|
1610
|
+
}),
|
|
1611
|
+
policy({
|
|
1612
|
+
rule: "If an entity term in the question maps to a table, keep that table in query scope and join to attribute tables rather than dropping the entity table."
|
|
1613
|
+
}),
|
|
1614
|
+
hint2(
|
|
1615
|
+
"If the question targets a specific entity group, include that entity table and its join conditions even when selected columns come from a related table."
|
|
1616
|
+
),
|
|
1617
|
+
hint2(
|
|
1618
|
+
"When the question names an entity type and a relation table links to that entity via *_id, include the entity table in scope instead of counting only relation rows."
|
|
1619
|
+
),
|
|
1620
|
+
hint2(
|
|
1621
|
+
"Prefer INNER JOIN by default; use LEFT JOIN only when the question explicitly requests including unmatched rows or zero-related entities."
|
|
1622
|
+
)
|
|
1623
|
+
),
|
|
1624
|
+
fragment2(
|
|
1625
|
+
"aggregation_exactness",
|
|
1626
|
+
policy({
|
|
1627
|
+
rule: "Preserve requested aggregation semantics exactly: use COUNT(*) by default for total rows, use COUNT(DISTINCT ...) only when uniqueness is explicitly requested, and group by stable entity keys when computing per-entity aggregates."
|
|
1628
|
+
}),
|
|
1629
|
+
policy({
|
|
1630
|
+
rule: "For questions asking which entity has lowest/highest average of a metric, compute AVG(metric) per entity (GROUP BY entity) and rank those aggregates."
|
|
1631
|
+
}),
|
|
1632
|
+
hint2(
|
|
1633
|
+
'For "how many <entities>" questions over relation records, default to COUNT(*) on qualifying rows unless explicit uniqueness language is present.'
|
|
1634
|
+
)
|
|
1635
|
+
),
|
|
1636
|
+
fragment2(
|
|
1637
|
+
"query_shape_examples",
|
|
1638
|
+
example({
|
|
1639
|
+
question: "List categories ordered by how many records belong to each category.",
|
|
1640
|
+
answer: "SELECT category FROM records GROUP BY category ORDER BY COUNT(*)"
|
|
1641
|
+
}),
|
|
1642
|
+
example({
|
|
1643
|
+
question: "Show labels shared by rows with metric > 100 and rows with metric < 10.",
|
|
1644
|
+
answer: "SELECT label FROM records WHERE metric > 100 INTERSECT SELECT label FROM records WHERE metric < 10"
|
|
1645
|
+
}),
|
|
1646
|
+
example({
|
|
1647
|
+
question: "List locations that have not hosted any event.",
|
|
1648
|
+
answer: "SELECT location_name FROM locations WHERE location_id NOT IN (SELECT host_location_id FROM events)"
|
|
1649
|
+
}),
|
|
1650
|
+
example({
|
|
1651
|
+
question: "List the most common category across records.",
|
|
1652
|
+
answer: "SELECT category FROM records GROUP BY category ORDER BY COUNT(*) DESC LIMIT 1"
|
|
1653
|
+
})
|
|
1654
|
+
)
|
|
1655
|
+
];
|
|
1054
1656
|
function extractSql(output) {
|
|
1055
1657
|
const match = output.match(/```sql\n?([\s\S]*?)```/);
|
|
1056
1658
|
return match ? match[1].trim() : output.trim();
|
|
1057
1659
|
}
|
|
1058
|
-
var marker = Symbol("SQLValidationError");
|
|
1059
|
-
var SQLValidationError = class _SQLValidationError extends Error {
|
|
1060
|
-
[marker];
|
|
1061
|
-
constructor(message2) {
|
|
1062
|
-
super(message2);
|
|
1063
|
-
this.name = "SQLValidationError";
|
|
1064
|
-
this[marker] = true;
|
|
1065
|
-
}
|
|
1066
|
-
static isInstance(error) {
|
|
1067
|
-
return error instanceof _SQLValidationError && error[marker] === true;
|
|
1068
|
-
}
|
|
1069
|
-
};
|
|
1070
|
-
var UnanswerableSQLError = class _UnanswerableSQLError extends Error {
|
|
1071
|
-
constructor(message2) {
|
|
1072
|
-
super(message2);
|
|
1073
|
-
this.name = "UnanswerableSQLError";
|
|
1074
|
-
}
|
|
1075
|
-
static isInstance(error) {
|
|
1076
|
-
return error instanceof _UnanswerableSQLError;
|
|
1077
|
-
}
|
|
1078
|
-
};
|
|
1079
1660
|
async function toSql(options) {
|
|
1080
1661
|
const { maxRetries = 3 } = options;
|
|
1081
1662
|
return withRetry(
|
|
@@ -1088,20 +1669,38 @@ async function toSql(options) {
|
|
|
1088
1669
|
context.set(
|
|
1089
1670
|
persona3({
|
|
1090
1671
|
name: "Freya",
|
|
1091
|
-
role:
|
|
1092
|
-
objective:
|
|
1672
|
+
role: SQL_AGENT_ROLE,
|
|
1673
|
+
objective: SQL_AGENT_OBJECTIVE
|
|
1674
|
+
// role: `You are a data science expert that provides well-reasoned and detailed responses.`,
|
|
1675
|
+
// objective: `Your task is to understand the schema and generate a valid SQL query to answer the question. You first think about the reasoning process as an internal monologue and then provide the user with the answer.`,
|
|
1093
1676
|
}),
|
|
1677
|
+
...SQL_AGENT_POLICIES,
|
|
1094
1678
|
...options.fragments
|
|
1095
1679
|
);
|
|
1096
1680
|
if (errors.length) {
|
|
1681
|
+
const lastError = errors.at(-1);
|
|
1097
1682
|
context.set(
|
|
1098
|
-
user2(
|
|
1099
|
-
|
|
1100
|
-
|
|
1683
|
+
user2(dedent2`
|
|
1684
|
+
Answer the following question with the SQL code. Use the piece of evidence and base your answer on the database schema.
|
|
1685
|
+
Given the question, the evidence and the database schema, return the SQL script that addresses the question.
|
|
1686
|
+
|
|
1687
|
+
Question: ${options.input}
|
|
1688
|
+
`),
|
|
1689
|
+
UnanswerableSQLError.isInstance(lastError) ? user2(
|
|
1690
|
+
`<retry_instruction>Your previous response marked the task as unanswerable. Re-evaluate using best-effort schema mapping. If the core intent is answerable with existing tables/columns, return SQL. Return error only when required core intent cannot be mapped without inventing schema elements.</retry_instruction>`
|
|
1691
|
+
) : user2(
|
|
1692
|
+
`<validation_error>Your previous SQL query had the following error: ${lastError?.message}. Please fix the query.</validation_error>`
|
|
1101
1693
|
)
|
|
1102
1694
|
);
|
|
1103
1695
|
} else {
|
|
1104
|
-
context.set(
|
|
1696
|
+
context.set(
|
|
1697
|
+
user2(dedent2`
|
|
1698
|
+
Answer the following question with the SQL code. Use the piece of evidence and base your answer on the database schema.
|
|
1699
|
+
Given the question, the evidence and the database schema, return the SQL script that addresses the question.
|
|
1700
|
+
|
|
1701
|
+
Question: ${options.input}
|
|
1702
|
+
`)
|
|
1703
|
+
);
|
|
1105
1704
|
}
|
|
1106
1705
|
const temperature = RETRY_TEMPERATURES[attemptNumber - 1] ?? RETRY_TEMPERATURES[RETRY_TEMPERATURES.length - 1];
|
|
1107
1706
|
const baseModel = options.model ?? groq2("openai/gpt-oss-20b");
|
|
@@ -1127,19 +1726,45 @@ async function toSql(options) {
|
|
|
1127
1726
|
})
|
|
1128
1727
|
});
|
|
1129
1728
|
const { result: output } = await sqlOutput.generate();
|
|
1729
|
+
const finalizeSql = async (rawSql) => {
|
|
1730
|
+
const sql = options.adapter.format(extractSql(rawSql));
|
|
1731
|
+
const validationError = await options.adapter.validate(sql);
|
|
1732
|
+
if (validationError) {
|
|
1733
|
+
throw new SQLValidationError(validationError);
|
|
1734
|
+
}
|
|
1735
|
+
return {
|
|
1736
|
+
attempts,
|
|
1737
|
+
sql,
|
|
1738
|
+
errors: errors.length ? errors.map(formatErrorMessage) : void 0
|
|
1739
|
+
};
|
|
1740
|
+
};
|
|
1130
1741
|
if ("error" in output) {
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1742
|
+
context.set(
|
|
1743
|
+
user2(
|
|
1744
|
+
"<best_effort_fallback>Do not return unanswerable. Produce the best valid SQL query that answers the core intent using only available schema entities.</best_effort_fallback>"
|
|
1745
|
+
)
|
|
1746
|
+
);
|
|
1747
|
+
const forcedSqlOutput = structuredOutput2({
|
|
1748
|
+
model,
|
|
1749
|
+
context,
|
|
1750
|
+
schema: z4.object({
|
|
1751
|
+
sql: z4.string().describe(
|
|
1752
|
+
"Best-effort SQL query that answers the core intent using only available schema entities."
|
|
1753
|
+
),
|
|
1754
|
+
reasoning: z4.string().describe("Reasoning steps for best-effort schema mapping.")
|
|
1755
|
+
})
|
|
1756
|
+
});
|
|
1757
|
+
try {
|
|
1758
|
+
const forced = await forcedSqlOutput.generate();
|
|
1759
|
+
return await finalizeSql(forced.sql);
|
|
1760
|
+
} catch (error) {
|
|
1761
|
+
if (SQLValidationError.isInstance(error) || APICallError.isInstance(error) || JSONParseError.isInstance(error) || TypeValidationError.isInstance(error) || NoObjectGeneratedError.isInstance(error) || NoOutputGeneratedError.isInstance(error) || NoContentGeneratedError.isInstance(error)) {
|
|
1762
|
+
throw error;
|
|
1763
|
+
}
|
|
1764
|
+
throw new UnanswerableSQLError(output.error);
|
|
1765
|
+
}
|
|
1137
1766
|
}
|
|
1138
|
-
return
|
|
1139
|
-
attempts,
|
|
1140
|
-
sql,
|
|
1141
|
-
errors: errors.length ? errors.map(formatErrorMessage) : void 0
|
|
1142
|
-
};
|
|
1767
|
+
return await finalizeSql(output.sql);
|
|
1143
1768
|
},
|
|
1144
1769
|
{ retries: maxRetries - 1 }
|
|
1145
1770
|
);
|
|
@@ -1202,9 +1827,6 @@ async function withRetry(computation, options = { retries: 3 }) {
|
|
|
1202
1827
|
return APICallError.isInstance(context.error) || JSONParseError.isInstance(context.error) || TypeValidationError.isInstance(context.error) || NoObjectGeneratedError.isInstance(context.error) || NoOutputGeneratedError.isInstance(context.error) || NoContentGeneratedError.isInstance(context.error);
|
|
1203
1828
|
},
|
|
1204
1829
|
onFailedAttempt(context) {
|
|
1205
|
-
console.log(
|
|
1206
|
-
`Attempt ${context.attemptNumber} failed. There are ${context.retriesLeft} retries left.`
|
|
1207
|
-
);
|
|
1208
1830
|
errors.push(context.error);
|
|
1209
1831
|
}
|
|
1210
1832
|
}
|
|
@@ -1213,7 +1835,7 @@ async function withRetry(computation, options = { retries: 3 }) {
|
|
|
1213
1835
|
|
|
1214
1836
|
// packages/text2sql/src/lib/agents/suggestions.agents.ts
|
|
1215
1837
|
import { groq as groq3 } from "@ai-sdk/groq";
|
|
1216
|
-
import
|
|
1838
|
+
import dedent3 from "dedent";
|
|
1217
1839
|
import z5 from "zod";
|
|
1218
1840
|
import { agent, thirdPersonPrompt } from "@deepagents/agent";
|
|
1219
1841
|
var suggestionsAgent = agent({
|
|
@@ -1229,7 +1851,7 @@ var suggestionsAgent = agent({
|
|
|
1229
1851
|
).min(1).max(5).describe("A set of up to two advanced question + SQL pairs.")
|
|
1230
1852
|
}),
|
|
1231
1853
|
prompt: (state) => {
|
|
1232
|
-
return
|
|
1854
|
+
return dedent3`
|
|
1233
1855
|
${thirdPersonPrompt()}
|
|
1234
1856
|
|
|
1235
1857
|
<identity>
|
|
@@ -4193,26 +4815,26 @@ var TrackedFs = class {
|
|
|
4193
4815
|
// packages/text2sql/src/lib/instructions.ts
|
|
4194
4816
|
import {
|
|
4195
4817
|
clarification,
|
|
4196
|
-
example,
|
|
4818
|
+
example as example2,
|
|
4197
4819
|
explain,
|
|
4198
|
-
fragment as
|
|
4199
|
-
guardrail,
|
|
4200
|
-
hint as
|
|
4201
|
-
policy,
|
|
4820
|
+
fragment as fragment3,
|
|
4821
|
+
guardrail as guardrail2,
|
|
4822
|
+
hint as hint3,
|
|
4823
|
+
policy as policy2,
|
|
4202
4824
|
principle,
|
|
4203
4825
|
quirk,
|
|
4204
4826
|
role,
|
|
4205
4827
|
styleGuide,
|
|
4206
|
-
workflow
|
|
4828
|
+
workflow as workflow2
|
|
4207
4829
|
} from "@deepagents/context";
|
|
4208
4830
|
function reasoningFramework() {
|
|
4209
4831
|
return [
|
|
4210
4832
|
role(
|
|
4211
4833
|
"You are a very strong reasoner and planner. Use these critical instructions to structure your plans, thoughts, and responses."
|
|
4212
4834
|
),
|
|
4213
|
-
|
|
4835
|
+
fragment3(
|
|
4214
4836
|
"meta-cognitive-reasoning-framework",
|
|
4215
|
-
|
|
4837
|
+
hint3(
|
|
4216
4838
|
"Before taking any action (either tool calls *or* responses to the user), you must proactively, methodically, and independently plan and reason about:"
|
|
4217
4839
|
),
|
|
4218
4840
|
// 1) Logical dependencies and constraints
|
|
@@ -4220,19 +4842,19 @@ function reasoningFramework() {
|
|
|
4220
4842
|
title: "Logical dependencies and constraints",
|
|
4221
4843
|
description: "Analyze the intended action against the following factors. Resolve conflicts in order of importance:",
|
|
4222
4844
|
policies: [
|
|
4223
|
-
|
|
4845
|
+
policy2({
|
|
4224
4846
|
rule: "Policy-based rules, mandatory prerequisites, and constraints."
|
|
4225
4847
|
}),
|
|
4226
|
-
|
|
4848
|
+
policy2({
|
|
4227
4849
|
rule: "Order of operations: Ensure taking an action does not prevent a subsequent necessary action.",
|
|
4228
4850
|
policies: [
|
|
4229
4851
|
"The user may request actions in a random order, but you may need to reorder operations to maximize successful completion of the task."
|
|
4230
4852
|
]
|
|
4231
4853
|
}),
|
|
4232
|
-
|
|
4854
|
+
policy2({
|
|
4233
4855
|
rule: "Other prerequisites (information and/or actions needed)."
|
|
4234
4856
|
}),
|
|
4235
|
-
|
|
4857
|
+
policy2({ rule: "Explicit user constraints or preferences." })
|
|
4236
4858
|
]
|
|
4237
4859
|
}),
|
|
4238
4860
|
// 2) Risk assessment
|
|
@@ -4285,17 +4907,17 @@ function reasoningFramework() {
|
|
|
4285
4907
|
title: "Completeness",
|
|
4286
4908
|
description: "Ensure that all requirements, constraints, options, and preferences are exhaustively incorporated into your plan.",
|
|
4287
4909
|
policies: [
|
|
4288
|
-
|
|
4910
|
+
policy2({
|
|
4289
4911
|
rule: "Resolve conflicts using the order of importance in #1."
|
|
4290
4912
|
}),
|
|
4291
|
-
|
|
4913
|
+
policy2({
|
|
4292
4914
|
rule: "Avoid premature conclusions: There may be multiple relevant options for a given situation.",
|
|
4293
4915
|
policies: [
|
|
4294
4916
|
"To check for whether an option is relevant, reason about all information sources from #5.",
|
|
4295
4917
|
"You may need to consult the user to even know whether something is applicable. Do not assume it is not applicable without checking."
|
|
4296
4918
|
]
|
|
4297
4919
|
}),
|
|
4298
|
-
|
|
4920
|
+
policy2({
|
|
4299
4921
|
rule: "Review applicable sources of information from #5 to confirm which are relevant to the current state."
|
|
4300
4922
|
})
|
|
4301
4923
|
]
|
|
@@ -4327,33 +4949,33 @@ function guidelines(options = {}) {
|
|
|
4327
4949
|
// Include the meta-cognitive reasoning framework
|
|
4328
4950
|
...reasoningFramework(),
|
|
4329
4951
|
// Prerequisite policies (must do X before Y)
|
|
4330
|
-
|
|
4952
|
+
fragment3(
|
|
4331
4953
|
"prerequisite_policies",
|
|
4332
|
-
|
|
4954
|
+
policy2({
|
|
4333
4955
|
rule: "YOU MUST inspect schema structure and available tables",
|
|
4334
4956
|
before: "generating ANY SQL query",
|
|
4335
4957
|
reason: "NEVER generate SQL without knowing valid tables, columns, and relationships"
|
|
4336
4958
|
}),
|
|
4337
|
-
|
|
4959
|
+
policy2({
|
|
4338
4960
|
rule: "YOU MUST resolve ambiguous business terms with the user",
|
|
4339
4961
|
before: "making ANY assumptions about terminology meaning",
|
|
4340
4962
|
reason: "NEVER guess domain-specific language\u2014ask for clarification"
|
|
4341
4963
|
}),
|
|
4342
|
-
|
|
4964
|
+
policy2({
|
|
4343
4965
|
rule: "YOU MUST validate SQL syntax",
|
|
4344
4966
|
before: "executing ANY query against the database",
|
|
4345
4967
|
reason: "NEVER execute unvalidated queries"
|
|
4346
4968
|
}),
|
|
4347
|
-
|
|
4969
|
+
policy2({
|
|
4348
4970
|
rule: "YOU MUST complete ALL reasoning steps",
|
|
4349
4971
|
before: "taking ANY tool call or response action",
|
|
4350
4972
|
reason: "Once an action is taken, it CANNOT be undone. NO EXCEPTIONS."
|
|
4351
4973
|
})
|
|
4352
4974
|
),
|
|
4353
4975
|
// Few-shot: Applying reasoning principles
|
|
4354
|
-
|
|
4976
|
+
fragment3(
|
|
4355
4977
|
"reasoning-examples",
|
|
4356
|
-
|
|
4978
|
+
example2({
|
|
4357
4979
|
question: "Show me sales last month",
|
|
4358
4980
|
answer: `Applying Principle 1 (Logical dependencies):
|
|
4359
4981
|
- Need: schema to know which table has sales data
|
|
@@ -4365,7 +4987,7 @@ Applying Principle 5 (Information availability):
|
|
|
4365
4987
|
|
|
4366
4988
|
Action: Ask user for date range clarification BEFORE generating SQL.`
|
|
4367
4989
|
}),
|
|
4368
|
-
|
|
4990
|
+
example2({
|
|
4369
4991
|
question: "Why did my query return no results?",
|
|
4370
4992
|
answer: `Applying Principle 3 (Abductive reasoning):
|
|
4371
4993
|
- Hypothesis 1 (most likely): Filter too restrictive
|
|
@@ -4379,7 +5001,7 @@ Testing hypotheses:
|
|
|
4379
5001
|
|
|
4380
5002
|
Action: Start with most likely hypothesis, test incrementally. NEVER guess.`
|
|
4381
5003
|
}),
|
|
4382
|
-
|
|
5004
|
+
example2({
|
|
4383
5005
|
question: "Get me the top customers",
|
|
4384
5006
|
answer: `Applying Principle 1 (Logical dependencies):
|
|
4385
5007
|
- "Top" is ambiguous\u2014by revenue? by order count? by recency?
|
|
@@ -4391,16 +5013,16 @@ Action: Ask user: "Top by what metric\u2014total revenue, number of orders, or m
|
|
|
4391
5013
|
})
|
|
4392
5014
|
),
|
|
4393
5015
|
// Schema adherence - consolidated into clear rules
|
|
4394
|
-
|
|
5016
|
+
fragment3(
|
|
4395
5017
|
"schema_adherence",
|
|
4396
|
-
|
|
5018
|
+
hint3(
|
|
4397
5019
|
"Use only tables and columns from the schema. For unspecified columns, use SELECT *. When showing related items, include IDs and requested details."
|
|
4398
5020
|
),
|
|
4399
|
-
|
|
5021
|
+
hint3(
|
|
4400
5022
|
'"Show" means list items; "count" or "total" means aggregate. Use canonical values verbatim for filtering.'
|
|
4401
5023
|
)
|
|
4402
5024
|
),
|
|
4403
|
-
|
|
5025
|
+
fragment3(
|
|
4404
5026
|
"Column statistics",
|
|
4405
5027
|
explain({
|
|
4406
5028
|
concept: "nDistinct in column stats",
|
|
@@ -4412,18 +5034,18 @@ Action: Ask user: "Top by what metric\u2014total revenue, number of orders, or m
|
|
|
4412
5034
|
explanation: "Measures how closely the physical row order matches the logical sort order of the column. Values near 1 or -1 mean the data is well-ordered; near 0 means scattered",
|
|
4413
5035
|
therefore: "High correlation means range queries (BETWEEN, >, <) on that column benefit from index scans. Low correlation means the index is less effective for ranges"
|
|
4414
5036
|
}),
|
|
4415
|
-
|
|
5037
|
+
hint3(
|
|
4416
5038
|
"When min/max stats are available, use them to validate filter values. If a user asks for values outside the known range, warn them the query may return no results."
|
|
4417
5039
|
)
|
|
4418
5040
|
),
|
|
4419
5041
|
// Joins - use relationship metadata
|
|
4420
|
-
|
|
5042
|
+
hint3(
|
|
4421
5043
|
"Use JOINs based on schema relationships. Favor PK/indexed columns; follow relationship metadata for direction and cardinality."
|
|
4422
5044
|
),
|
|
4423
5045
|
// Aggregations - explain the concepts
|
|
4424
|
-
|
|
5046
|
+
fragment3(
|
|
4425
5047
|
"Aggregations",
|
|
4426
|
-
|
|
5048
|
+
hint3(
|
|
4427
5049
|
"Apply COUNT, SUM, AVG when the question implies summarization. Use window functions for ranking, running totals, or row comparisons."
|
|
4428
5050
|
),
|
|
4429
5051
|
explain({
|
|
@@ -4433,7 +5055,7 @@ Action: Ask user: "Top by what metric\u2014total revenue, number of orders, or m
|
|
|
4433
5055
|
})
|
|
4434
5056
|
),
|
|
4435
5057
|
// Query semantics - explain concepts and document quirks
|
|
4436
|
-
|
|
5058
|
+
fragment3(
|
|
4437
5059
|
"Query interpretation",
|
|
4438
5060
|
explain({
|
|
4439
5061
|
concept: "threshold language",
|
|
@@ -4448,7 +5070,7 @@ Action: Ask user: "Top by what metric\u2014total revenue, number of orders, or m
|
|
|
4448
5070
|
issue: "NULL values behave unexpectedly in comparisons and aggregations",
|
|
4449
5071
|
workaround: "Use IS NULL, IS NOT NULL, or COALESCE() to handle NULLs explicitly"
|
|
4450
5072
|
}),
|
|
4451
|
-
|
|
5073
|
+
hint3(
|
|
4452
5074
|
"Always include mentioned filters from joined tables in WHERE conditions."
|
|
4453
5075
|
)
|
|
4454
5076
|
),
|
|
@@ -4461,24 +5083,24 @@ Action: Ask user: "Top by what metric\u2014total revenue, number of orders, or m
|
|
|
4461
5083
|
prefer: "Concise, business-friendly summaries with key comparisons and helpful follow-ups."
|
|
4462
5084
|
}),
|
|
4463
5085
|
// Safety guardrails - consolidated
|
|
4464
|
-
|
|
5086
|
+
fragment3(
|
|
4465
5087
|
"Query safety",
|
|
4466
|
-
|
|
5088
|
+
guardrail2({
|
|
4467
5089
|
rule: "Generate only valid, executable SELECT/WITH statements.",
|
|
4468
5090
|
reason: "Read-only access prevents data modification.",
|
|
4469
5091
|
action: "Never generate INSERT, UPDATE, DELETE, DROP, or DDL statements."
|
|
4470
5092
|
}),
|
|
4471
|
-
|
|
5093
|
+
guardrail2({
|
|
4472
5094
|
rule: "Avoid unbounded scans and cartesian joins.",
|
|
4473
5095
|
reason: "Protects performance and correctness.",
|
|
4474
5096
|
action: "Apply filters on indexed columns. If join keys are unclear, ask for clarification."
|
|
4475
5097
|
}),
|
|
4476
|
-
|
|
5098
|
+
guardrail2({
|
|
4477
5099
|
rule: "Preserve query semantics.",
|
|
4478
5100
|
reason: "Arbitrary modifications change results.",
|
|
4479
5101
|
action: 'Only add LIMIT for explicit "top N" requests. Add ORDER BY for deterministic results.'
|
|
4480
5102
|
}),
|
|
4481
|
-
|
|
5103
|
+
guardrail2({
|
|
4482
5104
|
rule: "Seek clarification for genuine ambiguity.",
|
|
4483
5105
|
reason: "Prevents incorrect assumptions.",
|
|
4484
5106
|
action: "Ask a focused question before guessing."
|
|
@@ -4489,10 +5111,10 @@ Action: Ask user: "Top by what metric\u2014total revenue, number of orders, or m
|
|
|
4489
5111
|
ask: "Clarify the ranking metric or definition.",
|
|
4490
5112
|
reason: "Ensures correct aggregation and ordering."
|
|
4491
5113
|
}),
|
|
4492
|
-
|
|
5114
|
+
hint3(
|
|
4493
5115
|
'Use sample cell values from schema hints to match exact casing and format in WHERE conditions (e.g., "Male" vs "male" vs "M").'
|
|
4494
5116
|
),
|
|
4495
|
-
|
|
5117
|
+
workflow2({
|
|
4496
5118
|
task: "SQL generation",
|
|
4497
5119
|
steps: [
|
|
4498
5120
|
"Schema linking: identify which tables and columns are mentioned or implied by the question.",
|
|
@@ -4504,7 +5126,7 @@ Action: Ask user: "Top by what metric\u2014total revenue, number of orders, or m
|
|
|
4504
5126
|
"Verify: mentally translate SQL back to natural language. Does it match the original question?"
|
|
4505
5127
|
]
|
|
4506
5128
|
}),
|
|
4507
|
-
|
|
5129
|
+
workflow2({
|
|
4508
5130
|
task: "Error recovery",
|
|
4509
5131
|
triggers: ["SQL error", "query failed", "execution error"],
|
|
4510
5132
|
steps: [
|
|
@@ -4517,7 +5139,7 @@ Action: Ask user: "Top by what metric\u2014total revenue, number of orders, or m
|
|
|
4517
5139
|
],
|
|
4518
5140
|
notes: "Maximum 3 retry attempts. If still failing, explain the issue to the user."
|
|
4519
5141
|
}),
|
|
4520
|
-
|
|
5142
|
+
workflow2({
|
|
4521
5143
|
task: "Complex query decomposition",
|
|
4522
5144
|
triggers: [
|
|
4523
5145
|
"multiple conditions",
|
|
@@ -4534,7 +5156,7 @@ Action: Ask user: "Top by what metric\u2014total revenue, number of orders, or m
|
|
|
4534
5156
|
],
|
|
4535
5157
|
notes: "Complex questions often need CTEs (WITH clauses) for clarity and reusability."
|
|
4536
5158
|
}),
|
|
4537
|
-
|
|
5159
|
+
workflow2({
|
|
4538
5160
|
task: "Multi-turn context",
|
|
4539
5161
|
triggers: ["follow-up", "and also", "what about", "same but", "instead"],
|
|
4540
5162
|
steps: [
|
|
@@ -4547,9 +5169,9 @@ Action: Ask user: "Top by what metric\u2014total revenue, number of orders, or m
|
|
|
4547
5169
|
],
|
|
4548
5170
|
notes: "If reference is ambiguous, ask which previous result or entity the user means."
|
|
4549
5171
|
}),
|
|
4550
|
-
|
|
5172
|
+
fragment3(
|
|
4551
5173
|
"Bash tool usage",
|
|
4552
|
-
|
|
5174
|
+
workflow2({
|
|
4553
5175
|
task: "Query execution",
|
|
4554
5176
|
steps: [
|
|
4555
5177
|
'Execute SQL through bash tool: sql run "SELECT ..."',
|
|
@@ -4558,16 +5180,16 @@ Action: Ask user: "Top by what metric\u2014total revenue, number of orders, or m
|
|
|
4558
5180
|
"For large results, slice first: cat <path> | jq '.[:10]'"
|
|
4559
5181
|
]
|
|
4560
5182
|
}),
|
|
4561
|
-
|
|
5183
|
+
hint3(
|
|
4562
5184
|
`You cannot access sql through a tool, it'll fail so the proper way to access it is through the bash tool using "sql run" and "sql validate" commands.`
|
|
4563
5185
|
),
|
|
4564
|
-
|
|
5186
|
+
hint3(
|
|
4565
5187
|
"The sql command outputs: file path, column names (comma-separated), and row count. Use column names to construct precise jq queries."
|
|
4566
5188
|
),
|
|
4567
|
-
|
|
5189
|
+
hint3(
|
|
4568
5190
|
'This is virtual bash environment and "sql" commands proxy to the database hence you cannot access sql files directly.'
|
|
4569
5191
|
),
|
|
4570
|
-
|
|
5192
|
+
hint3(
|
|
4571
5193
|
"If a query fails, the sql command returns an error message in stderr."
|
|
4572
5194
|
)
|
|
4573
5195
|
)
|
|
@@ -4582,7 +5204,7 @@ Action: Ask user: "Top by what metric\u2014total revenue, number of orders, or m
|
|
|
4582
5204
|
);
|
|
4583
5205
|
} else {
|
|
4584
5206
|
baseTeachings.push(
|
|
4585
|
-
|
|
5207
|
+
hint3(
|
|
4586
5208
|
'When a month, day, or time period is mentioned without a year (e.g., "in August", "on Monday"), assume ALL occurrences of that period in the data. Do not ask for year clarification.'
|
|
4587
5209
|
)
|
|
4588
5210
|
);
|