feat: render function calls
This commit is contained in:
1 parent
4d878dfb61
commit
6a81529790
2 files changed
+75
-5
No files matched your search
+21
-4
@@ -45,6 +45,25 @@ function makeExecutor(bundle) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a name -> rendered-arguments function for the bundle, used to log
|
||||||
|
* each call. Tools that do not provide a `render` fall back to a plain
|
||||||
|
* JSON rendering of the arguments.
|
||||||
|
*/
|
||||||
|
function makeRenderer(bundle) {
|
||||||
|
const byName = new Map(bundle.map((tool) => [tool.name, tool]));
|
||||||
|
|
||||||
|
return function render(name, args) {
|
||||||
|
const tool = byName.get(name);
|
||||||
|
if (!tool || typeof tool.render !== 'function') return JSON.stringify(args || {});
|
||||||
|
try {
|
||||||
|
return tool.render(args || {});
|
||||||
|
} catch (err) {
|
||||||
|
return JSON.stringify(args || {});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------ *
|
/* ------------------------------------------------------------------ *
|
||||||
* Tool-call extraction
|
* Tool-call extraction
|
||||||
* ------------------------------------------------------------------ */
|
* ------------------------------------------------------------------ */
|
||||||
@@ -142,6 +161,7 @@ async function run({
|
|||||||
parameters,
|
parameters,
|
||||||
}));
|
}));
|
||||||
const execute = makeExecutor(bundle);
|
const execute = makeExecutor(bundle);
|
||||||
|
const render = makeRenderer(bundle);
|
||||||
|
|
||||||
const history = Array.isArray(messages) ? messages.slice() : [];
|
const history = Array.isArray(messages) ? messages.slice() : [];
|
||||||
|
|
||||||
@@ -188,10 +208,7 @@ async function run({
|
|||||||
role: 'tool',
|
role: 'tool',
|
||||||
content: call,
|
content: call,
|
||||||
});
|
});
|
||||||
}
|
process.stderr.write(`called: ${call.tool_name} ${render(call.tool_name, call.arguments)}\n`);
|
||||||
|
|
||||||
if (calls.length) {
|
|
||||||
console.log('called: ' + calls.map(c => c.tool_name));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (verify && verify()) {
|
if (verify && verify()) {
|
||||||
|
|||||||
+54
-1
@@ -149,6 +149,19 @@ function applyEdit(content, { search, replace = '', regex, global }) {
|
|||||||
return content.slice(0, at) + replace + content.slice(at + search.length);
|
return content.slice(0, at) + replace + content.slice(at + search.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Collapse whitespace and clip to `max` characters for single-line output. */
|
||||||
|
function oneLine(value, max = 60) {
|
||||||
|
const text = String(value ?? '').replace(/\s+/g, ' ').trim();
|
||||||
|
return text.length > max ? `${text.slice(0, max - 1)}\u2026` : text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Fallback argument rendering: values joined, JSON for non-strings. */
|
||||||
|
function defaultRender(args) {
|
||||||
|
return Object.values(args ?? {})
|
||||||
|
.map((value) => (typeof value === 'string' ? value : JSON.stringify(value)))
|
||||||
|
.join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------ *
|
/* ------------------------------------------------------------------ *
|
||||||
* Tool base class
|
* Tool base class
|
||||||
* ------------------------------------------------------------------ */
|
* ------------------------------------------------------------------ */
|
||||||
@@ -181,6 +194,16 @@ export class Tool {
|
|||||||
return this.run(args ?? {});
|
return this.run(args ?? {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One-line rendering of a call's arguments, used when logging the call.
|
||||||
|
* Subclasses override this when a more legible summary is possible.
|
||||||
|
*/
|
||||||
|
render(args) {
|
||||||
|
// returning all json for all calls bloats
|
||||||
|
return '';
|
||||||
|
//return defaultRender(args);
|
||||||
|
}
|
||||||
|
|
||||||
/** Resolve `file` against the root, asserting the given permission. */
|
/** Resolve `file` against the root, asserting the given permission. */
|
||||||
resolve(file, verb) {
|
resolve(file, verb) {
|
||||||
const abs = path.resolve(this.root, file);
|
const abs = path.resolve(this.root, file);
|
||||||
@@ -206,6 +229,11 @@ class ReadFile extends Tool {
|
|||||||
required: ['path'],
|
required: ['path'],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
render({ path: file, lineStart, lineEnd }) {
|
||||||
|
const range = lineStart || lineEnd ? `:${lineStart ?? 1}-${lineEnd ?? 'eof'}` : '';
|
||||||
|
return `${file}${range}`;
|
||||||
|
}
|
||||||
|
|
||||||
run({ path: file, lineStart, lineEnd }) {
|
run({ path: file, lineStart, lineEnd }) {
|
||||||
const abs = this.resolve(file, 'read');
|
const abs = this.resolve(file, 'read');
|
||||||
|
|
||||||
@@ -236,6 +264,10 @@ class ListDir extends Tool {
|
|||||||
required: [],
|
required: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
render({ path: dir }) {
|
||||||
|
return dir || '.';
|
||||||
|
}
|
||||||
|
|
||||||
run({ path: dir }) {
|
run({ path: dir }) {
|
||||||
const abs = this.resolve(dir || '.', 'read');
|
const abs = this.resolve(dir || '.', 'read');
|
||||||
|
|
||||||
@@ -263,6 +295,10 @@ class FindFiles extends Tool {
|
|||||||
required: ['pattern'],
|
required: ['pattern'],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
render({ pattern, path: dir }) {
|
||||||
|
return dir ? `${pattern} in ${dir}` : pattern;
|
||||||
|
}
|
||||||
|
|
||||||
run({ pattern, path: dir = '.', limit = 200 }) {
|
run({ pattern, path: dir = '.', limit = 200 }) {
|
||||||
const abs = this.resolve(dir, 'read');
|
const abs = this.resolve(dir, 'read');
|
||||||
const match = globToRegExp(pattern);
|
const match = globToRegExp(pattern);
|
||||||
@@ -307,6 +343,10 @@ class SearchFiles extends Tool {
|
|||||||
required: ['pattern'],
|
required: ['pattern'],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
render({ pattern, path: dir = '.', glob = '**/*' }) {
|
||||||
|
return `${pattern} in ${dir} (${glob})`;
|
||||||
|
}
|
||||||
|
|
||||||
run({
|
run({
|
||||||
pattern,
|
pattern,
|
||||||
path: dir = '.',
|
path: dir = '.',
|
||||||
@@ -390,6 +430,10 @@ class WriteFile extends Tool {
|
|||||||
required: ['path', 'content'],
|
required: ['path', 'content'],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
render({ path: file }) {
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
run({ path: file, content }) {
|
run({ path: file, content }) {
|
||||||
const abs = this.resolve(file, 'write');
|
const abs = this.resolve(file, 'write');
|
||||||
fs.mkdirSync(path.dirname(abs), { recursive: true });
|
fs.mkdirSync(path.dirname(abs), { recursive: true });
|
||||||
@@ -415,6 +459,10 @@ class UpdateFile extends Tool {
|
|||||||
required: ['path', 'search'],
|
required: ['path', 'search'],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
render({ path: file, search }) {
|
||||||
|
return `${file}: ${oneLine(search)}`;
|
||||||
|
}
|
||||||
|
|
||||||
run({ path: file, search, replace = '', global, regex }) {
|
run({ path: file, search, replace = '', global, regex }) {
|
||||||
const abs = this.resolve(file, 'write');
|
const abs = this.resolve(file, 'write');
|
||||||
const content = fs.readFileSync(abs, 'utf8');
|
const content = fs.readFileSync(abs, 'utf8');
|
||||||
@@ -450,6 +498,10 @@ class MultiEdit extends Tool {
|
|||||||
required: ['path', 'edits'],
|
required: ['path', 'edits'],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
render({ path: file, edits }) {
|
||||||
|
return `${file} (${Array.isArray(edits) ? edits.length : 0} edits)`;
|
||||||
|
}
|
||||||
|
|
||||||
run({ path: file, edits }) {
|
run({ path: file, edits }) {
|
||||||
const abs = this.resolve(file, 'write');
|
const abs = this.resolve(file, 'write');
|
||||||
const original = fs.readFileSync(abs, 'utf8');
|
const original = fs.readFileSync(abs, 'utf8');
|
||||||
@@ -504,12 +556,13 @@ function selectDefaults(selection) {
|
|||||||
* to the resolved scope so it only receives the call arguments.
|
* to the resolved scope so it only receives the call arguments.
|
||||||
*/
|
*/
|
||||||
function wrap(tool, root) {
|
function wrap(tool, root) {
|
||||||
const { name, description, parameters, scope, handler } = tool;
|
const { name, description, parameters, scope, handler, render } = tool;
|
||||||
return {
|
return {
|
||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
parameters,
|
parameters,
|
||||||
handler: (args) => handler(args ?? {}, { root, scope }),
|
handler: (args) => handler(args ?? {}, { root, scope }),
|
||||||
|
render: typeof render === 'function' ? render : defaultRender,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user