tune-basic-toolset 0.1.6 → 0.1.8
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 +56 -0
- package/package.json +1 -1
- package/src/patch.tool.js +2 -2
- package/src/proc.proc.js +139 -0
- package/src/shp.proc.js +2 -2
- package/src/sqlite.schema.json +23 -0
- package/src/sqlite.tool.js +19 -0
package/README.md
CHANGED
|
@@ -17,10 +17,12 @@ Basic toolset for [Tune](https://github.com/iovdin/tune).
|
|
|
17
17
|
- [jina_r](#jina_r) fetch webpage content
|
|
18
18
|
- [turn](#turn) turn based agent
|
|
19
19
|
- [list](#list) keep list of tasks todo (loops for LLM)
|
|
20
|
+
- [sqlite](#sqlite) execute sqlite queries
|
|
20
21
|
- [py](#py) run python code
|
|
21
22
|
- [js](#js) run javascript code
|
|
22
23
|
- [message](#message) talk to another chat/agent
|
|
23
24
|
- [Processors](#processors)
|
|
25
|
+
- [proc](#proc) converts tool to processor
|
|
24
26
|
- [shp](#shp) include shell command output
|
|
25
27
|
- [init](#init) set initial value
|
|
26
28
|
- [json_format](#json_format) make LLM respond with JSON
|
|
@@ -284,6 +286,45 @@ tool_result:
|
|
|
284
286
|
list updated
|
|
285
287
|
```
|
|
286
288
|
|
|
289
|
+
### `sqlite`
|
|
290
|
+
execut sqlite queries with sqlite shell
|
|
291
|
+
|
|
292
|
+
```chat
|
|
293
|
+
user:
|
|
294
|
+
@sqlite
|
|
295
|
+
|
|
296
|
+
create a simple todo table in db.sqlite and fill it with fake data
|
|
297
|
+
assistant:
|
|
298
|
+
|
|
299
|
+
tool_call: sqlite {"filename":"db.sqlite"}
|
|
300
|
+
CREATE TABLE todo (
|
|
301
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
302
|
+
task TEXT NOT NULL,
|
|
303
|
+
status TEXT CHECK(status IN ('pending', 'completed')) NOT NULL DEFAULT 'pending',
|
|
304
|
+
due_date DATE
|
|
305
|
+
);
|
|
306
|
+
|
|
307
|
+
INSERT INTO todo (task, status, due_date) VALUES
|
|
308
|
+
('Buy groceries', 'pending', '2024-05-01'),
|
|
309
|
+
('Finish project report', 'pending', '2024-05-03'),
|
|
310
|
+
('Book flight tickets', 'completed', '2024-04-25'),
|
|
311
|
+
('Call plumber', 'pending', '2024-04-30'),
|
|
312
|
+
('Schedule meeting with team', 'completed', '2024-04-20');
|
|
313
|
+
tool_result:
|
|
314
|
+
|
|
315
|
+
user:
|
|
316
|
+
check pending tasks
|
|
317
|
+
|
|
318
|
+
assistant:
|
|
319
|
+
|
|
320
|
+
tool_call: sqlite {"filename":"db.sqlite","format":"json"}
|
|
321
|
+
SELECT * FROM todo WHERE status = 'pending';
|
|
322
|
+
tool_result:
|
|
323
|
+
[{"id":1,"task":"Buy groceries","status":"pending","due_date":"2024-05-01"},
|
|
324
|
+
{"id":2,"task":"Finish project report","status":"pending","due_date":"2024-05-03"},
|
|
325
|
+
{"id":4,"task":"Call plumber","status":"pending","due_date":"2024-04-30"}]
|
|
326
|
+
```
|
|
327
|
+
|
|
287
328
|
|
|
288
329
|
### `py`
|
|
289
330
|
execute python code
|
|
@@ -370,6 +411,20 @@ Because it had a root canal!
|
|
|
370
411
|
## Processors
|
|
371
412
|
[Processors](https://iovdin.github.io/tune/template-language/processors) is a way to modify variable or insert new ones into chat.
|
|
372
413
|
|
|
414
|
+
### `proc`
|
|
415
|
+
converts any tool to a processor
|
|
416
|
+
``` chat
|
|
417
|
+
system:
|
|
418
|
+
include project file list to system prompt
|
|
419
|
+
@{| proc sh git ls-files }
|
|
420
|
+
|
|
421
|
+
execute script with sqlite on db `db.sqlite` and insert result
|
|
422
|
+
@{ script.sql | proc sqlite filename=db.sqlite }
|
|
423
|
+
|
|
424
|
+
execut python script text="384 * 123" and insert back result
|
|
425
|
+
@{| proc py 384 * 123 }
|
|
426
|
+
```
|
|
427
|
+
|
|
373
428
|
### `shp`
|
|
374
429
|
Insert shell command output
|
|
375
430
|
```chat
|
|
@@ -623,3 +678,4 @@ tool_call: todo
|
|
|
623
678
|
tool_result:
|
|
624
679
|
list updated
|
|
625
680
|
```
|
|
681
|
+
|
package/package.json
CHANGED
package/src/patch.tool.js
CHANGED
|
@@ -17,7 +17,7 @@ module.exports = async function patch({ text, filename }, ctx) {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
if (patches.length === 0) {
|
|
20
|
-
|
|
20
|
+
return "No valid patch segments found";
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
let fileContent = await ctx.read(filename);
|
|
@@ -35,4 +35,4 @@ module.exports = async function patch({ text, filename }, ctx) {
|
|
|
35
35
|
|
|
36
36
|
await ctx.write(filename, fileContent);
|
|
37
37
|
return "patched";
|
|
38
|
-
};
|
|
38
|
+
};
|
package/src/proc.proc.js
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
|
|
2
|
+
module.exports = async function proc(node, args, ctx) {
|
|
3
|
+
/*
|
|
4
|
+
@{| proc sh }
|
|
5
|
+
@{ script.sh | proc sh }
|
|
6
|
+
@{| proc sqlite SELECT * FROM table }
|
|
7
|
+
@{| proc sqlite filename=db.sqlite text="SELECT * FROM table" }
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const [ toolName, params ] = parseCommandLine(args)
|
|
11
|
+
if (node && !params.text) {
|
|
12
|
+
params.text = await node.read()
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const tool = await ctx.resolve(toolName, { "type": "tool" })
|
|
16
|
+
|
|
17
|
+
console.log(params)
|
|
18
|
+
|
|
19
|
+
if (!tool || tool.type !== "tool") {
|
|
20
|
+
throw Error(`tool '${toolName}' not found`)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
...node,
|
|
25
|
+
type: "text",
|
|
26
|
+
read: async() => tool.exec.call(ctx, params, ctx)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Examples:
|
|
31
|
+
// parseCommandLine('command hello world bla bla')
|
|
32
|
+
// -> ["command", { text: "hello world bla bla" }]
|
|
33
|
+
//
|
|
34
|
+
// parseCommandLine('cmd hello=world num=8 text="bla \\"bla"')
|
|
35
|
+
// -> ["cmd", { hello: "world", num: 8, text: 'bla "bla' }]
|
|
36
|
+
//
|
|
37
|
+
// parseCommandLine('cmd')
|
|
38
|
+
// -> ["cmd", {}]
|
|
39
|
+
|
|
40
|
+
function parseCommandLine(input) {
|
|
41
|
+
const s = String(input);
|
|
42
|
+
let i = 0, len = s.length;
|
|
43
|
+
|
|
44
|
+
function skipWS() { while (i < len && /\s/.test(s[i])) i++; }
|
|
45
|
+
|
|
46
|
+
// 1) Parse leading alphanumeric command
|
|
47
|
+
skipWS();
|
|
48
|
+
const cmdStart = i;
|
|
49
|
+
while (i < len && /[A-Za-z0-9]/.test(s[i])) i++;
|
|
50
|
+
const command = s.slice(cmdStart, i);
|
|
51
|
+
if (!command) return [null, {}];
|
|
52
|
+
|
|
53
|
+
// 2) Parse the remainder as args
|
|
54
|
+
skipWS();
|
|
55
|
+
const r = s.slice(i);
|
|
56
|
+
if (!r.trim()) return [command, {}];
|
|
57
|
+
|
|
58
|
+
function parseArgs(str) {
|
|
59
|
+
if (!/^\s*[A-Za-z0-9]+=/.test(str)) {
|
|
60
|
+
return { text: str.trim() };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
let j = 0;
|
|
64
|
+
const L = str.length;
|
|
65
|
+
const out = {};
|
|
66
|
+
|
|
67
|
+
function skipW() { while (j < L && /\s/.test(str[j])) j++; }
|
|
68
|
+
|
|
69
|
+
function parseKey() {
|
|
70
|
+
const start = j;
|
|
71
|
+
while (j < L && str[j] !== '=' && !/\s/.test(str[j])) j++;
|
|
72
|
+
return str.slice(start, j);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function parseQuotedValue(q) {
|
|
76
|
+
j++; // skip opening quote
|
|
77
|
+
let val = '';
|
|
78
|
+
while (j < L) {
|
|
79
|
+
const ch = str[j++];
|
|
80
|
+
if (ch === '\\') {
|
|
81
|
+
if (j >= L) break;
|
|
82
|
+
const esc = str[j++];
|
|
83
|
+
if (esc === 'n') val += '\n';
|
|
84
|
+
else if (esc === 't') val += '\t';
|
|
85
|
+
else if (esc === 'r') val += '\r';
|
|
86
|
+
else val += esc; // includes \" \\ \'
|
|
87
|
+
} else if (ch === q) {
|
|
88
|
+
return val;
|
|
89
|
+
} else {
|
|
90
|
+
val += ch;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return val; // best-effort if unclosed
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function parseUnquotedValue() {
|
|
97
|
+
const start = j;
|
|
98
|
+
while (j < L && !/\s/.test(str[j])) j++;
|
|
99
|
+
return str.slice(start, j);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function coerce(v) {
|
|
103
|
+
if (/^-?\d+(\.\d+)?$/.test(v)) return Number(v);
|
|
104
|
+
const low = v.toLowerCase();
|
|
105
|
+
if (low === 'true') return true;
|
|
106
|
+
if (low === 'false') return false;
|
|
107
|
+
if (low === 'null') return null;
|
|
108
|
+
return v;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
while (j < L) {
|
|
112
|
+
skipW();
|
|
113
|
+
if (j >= L) break;
|
|
114
|
+
|
|
115
|
+
const key = parseKey();
|
|
116
|
+
if (!key) return { text: str.trim() };
|
|
117
|
+
|
|
118
|
+
skipW();
|
|
119
|
+
if (j < L && str[j] === '=') {
|
|
120
|
+
j++; // skip '='
|
|
121
|
+
skipW();
|
|
122
|
+
let valueStr = '';
|
|
123
|
+
if (j < L && (str[j] === '"' || str[j] === "'")) {
|
|
124
|
+
valueStr = parseQuotedValue(str[j]);
|
|
125
|
+
} else {
|
|
126
|
+
valueStr = parseUnquotedValue();
|
|
127
|
+
}
|
|
128
|
+
out[key] = coerce(valueStr);
|
|
129
|
+
} else {
|
|
130
|
+
// If a non key=value token appears, treat the whole remainder as text
|
|
131
|
+
return { text: str.trim() };
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return out;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return [command, parseArgs(r)];
|
|
139
|
+
}
|
package/src/shp.proc.js
CHANGED
|
@@ -12,7 +12,7 @@ const shp = async (node, args, ctx) => ({
|
|
|
12
12
|
let result;
|
|
13
13
|
try {
|
|
14
14
|
if (input !== null) {
|
|
15
|
-
const res = spawnSync(args.trim(), {
|
|
15
|
+
const res = spawnSync(args.trim() || "sh", {
|
|
16
16
|
input,
|
|
17
17
|
encoding: 'utf8',
|
|
18
18
|
shell: true
|
|
@@ -28,4 +28,4 @@ const shp = async (node, args, ctx) => ({
|
|
|
28
28
|
}
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
-
module.exports = shp;
|
|
31
|
+
module.exports = shp;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "Execute SQLite multiple queries on a given database file",
|
|
3
|
+
"parameters": {
|
|
4
|
+
"type": "object",
|
|
5
|
+
"properties": {
|
|
6
|
+
"filename": {
|
|
7
|
+
"type": "string",
|
|
8
|
+
"description": "Path to the SQLite database file"
|
|
9
|
+
},
|
|
10
|
+
"text": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"description": "SQL queries to execute, 1 per line"
|
|
13
|
+
},
|
|
14
|
+
"format": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"description": "Output format for the query result",
|
|
17
|
+
"enum": ["line", "list", "csv", "html", "json", "table", "tabs"],
|
|
18
|
+
"default": "line"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"required": ["filename", "text"]
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const cp = require('node:child_process')
|
|
2
|
+
|
|
3
|
+
module.exports = async function sqlite({ filename, text, format = "table"}, ctx) {
|
|
4
|
+
let result = ""
|
|
5
|
+
try {
|
|
6
|
+
result = cp.execSync(`sqlite3 -${format} ${filename}`, { encoding: "utf8", input: text })
|
|
7
|
+
} catch (e) {
|
|
8
|
+
if (e.stderr) {
|
|
9
|
+
result += e.stderr
|
|
10
|
+
}
|
|
11
|
+
if (e.stdout) {
|
|
12
|
+
result += e.stdout
|
|
13
|
+
}
|
|
14
|
+
if (!result) {
|
|
15
|
+
result = e.stack
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return (result || "").replaceAll("@", "\\@");
|
|
19
|
+
}
|