ollama tests
This commit is contained in:
parent
9b96450054
commit
561ec84eef
2
packages/kbot/dist-in/ai-tools/constants.d.ts
vendored
Normal file
2
packages/kbot/dist-in/ai-tools/constants.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
export declare const LOGGER_NAME = "llm-tools-cli";
|
||||
export declare const EXCLUDE_GLOB: string[];
|
||||
11
packages/kbot/dist-in/ai-tools/constants.js
Normal file
11
packages/kbot/dist-in/ai-tools/constants.js
Normal file
@ -0,0 +1,11 @@
|
||||
export const LOGGER_NAME = 'llm-tools-cli';
|
||||
export const EXCLUDE_GLOB = [
|
||||
"**/node_modules/**",
|
||||
"**/dist/**",
|
||||
"**/build/**",
|
||||
"**/coverage/**",
|
||||
"*.log",
|
||||
".kbot",
|
||||
".git"
|
||||
];
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uc3RhbnRzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2FpLXRvb2xzL2NvbnN0YW50cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxNQUFNLENBQUMsTUFBTSxXQUFXLEdBQUcsZUFBZSxDQUFBO0FBRTFDLE1BQU0sQ0FBQyxNQUFNLFlBQVksR0FBRztJQUN4QixvQkFBb0I7SUFDcEIsWUFBWTtJQUNaLGFBQWE7SUFDYixnQkFBZ0I7SUFDaEIsT0FBTztJQUNQLE9BQU87SUFDUCxNQUFNO0NBQ1QsQ0FBQSJ9
|
||||
38
packages/kbot/dist-in/ai-tools/index.d.ts
vendored
Normal file
38
packages/kbot/dist-in/ai-tools/index.d.ts
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
import { z } from 'zod';
|
||||
import { ISettingsParam, Logger } from "tslog";
|
||||
import * as winston from 'winston';
|
||||
import { IKBotTask } from './types.js';
|
||||
export declare let logger: Logger<unknown>;
|
||||
export declare const TLogLevelNameSchema: z.ZodEnum<{
|
||||
silly: "silly";
|
||||
trace: "trace";
|
||||
debug: "debug";
|
||||
info: "info";
|
||||
warn: "warn";
|
||||
error: "error";
|
||||
fatal: "fatal";
|
||||
}>;
|
||||
export type LogLevel = z.infer<typeof TLogLevelNameSchema>;
|
||||
export declare enum LogLevelEx {
|
||||
silly = 0,
|
||||
trace = 1,
|
||||
debug = 2,
|
||||
info = 3,
|
||||
warn = 4,
|
||||
error = 5,
|
||||
fatal = 6
|
||||
}
|
||||
export declare enum ELogTargets {
|
||||
Console = 1,
|
||||
FileText = 2,
|
||||
FileJson = 4,
|
||||
Seq = 8
|
||||
}
|
||||
export declare function createLogger(name: string, options?: ISettingsParam<any>): Logger<unknown>;
|
||||
export declare const defaultLogger: Logger<unknown>;
|
||||
export declare const winstonLogger: (name: string, file: string, targets?: ELogTargets) => winston.Logger;
|
||||
export declare const createFileLogger: (logger: Logger<unknown>, level: number, file: string) => Logger<unknown>;
|
||||
export declare const toolLoggerTS: (name: any, options: IKBotTask) => Logger<unknown>;
|
||||
export declare const toolLogger: (name: any, options?: IKBotTask) => winston.Logger;
|
||||
export * from './types.js';
|
||||
export * from './types_kbot.js';
|
||||
163
packages/kbot/dist-in/ai-tools/index.js
Normal file
163
packages/kbot/dist-in/ai-tools/index.js
Normal file
File diff suppressed because one or more lines are too long
3
packages/kbot/dist-in/ai-tools/lib/tools/fs.d.ts
vendored
Normal file
3
packages/kbot/dist-in/ai-tools/lib/tools/fs.d.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
import { IKBotTask } from '../../types.js';
|
||||
export declare const decode_base64: (base64: string) => string;
|
||||
export declare const tools: (target: string, options: IKBotTask) => Array<any>;
|
||||
432
packages/kbot/dist-in/ai-tools/lib/tools/fs.js
Normal file
432
packages/kbot/dist-in/ai-tools/lib/tools/fs.js
Normal file
File diff suppressed because one or more lines are too long
2
packages/kbot/dist-in/ai-tools/lib/tools/git.d.ts
vendored
Normal file
2
packages/kbot/dist-in/ai-tools/lib/tools/git.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
import { IKBotTask } from '../../types.js';
|
||||
export declare const tools: (target: string, options: IKBotTask) => Array<any>;
|
||||
156
packages/kbot/dist-in/ai-tools/lib/tools/git.js
Normal file
156
packages/kbot/dist-in/ai-tools/lib/tools/git.js
Normal file
File diff suppressed because one or more lines are too long
2
packages/kbot/dist-in/ai-tools/lib/tools/interact.d.ts
vendored
Normal file
2
packages/kbot/dist-in/ai-tools/lib/tools/interact.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
import { IKBotTask } from '../../types.js';
|
||||
export declare const tools: (target: string, options: IKBotTask) => Array<any>;
|
||||
85
packages/kbot/dist-in/ai-tools/lib/tools/interact.js
Normal file
85
packages/kbot/dist-in/ai-tools/lib/tools/interact.js
Normal file
@ -0,0 +1,85 @@
|
||||
import { toolLogger } from '../../index.js';
|
||||
import { input, select } from '@inquirer/prompts';
|
||||
export const tools = (target, options) => {
|
||||
const logger = toolLogger('interact', options);
|
||||
return [
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'ask_question',
|
||||
description: 'Ask user a simple question and get response',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
question: {
|
||||
type: 'string',
|
||||
description: 'Question to ask the user'
|
||||
},
|
||||
default: {
|
||||
type: 'string',
|
||||
description: 'Default answer',
|
||||
optional: true
|
||||
}
|
||||
},
|
||||
required: ['question']
|
||||
},
|
||||
function: async (params) => {
|
||||
try {
|
||||
const answer = await input({
|
||||
message: params.question,
|
||||
default: params.default
|
||||
});
|
||||
return { response: answer };
|
||||
}
|
||||
catch (error) {
|
||||
logger.error('Error asking question:', error.message);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
parse: JSON.parse
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'choose_option',
|
||||
description: 'Ask user to choose from multiple options',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
message: {
|
||||
type: 'string',
|
||||
description: 'Message to show the user'
|
||||
},
|
||||
choices: {
|
||||
type: 'array',
|
||||
items: { type: 'string' },
|
||||
description: 'List of choices'
|
||||
},
|
||||
multiple: {
|
||||
type: 'boolean',
|
||||
description: 'Allow multiple selections',
|
||||
optional: true
|
||||
}
|
||||
},
|
||||
required: ['message', 'choices']
|
||||
},
|
||||
function: async (params) => {
|
||||
try {
|
||||
const answer = await select({
|
||||
message: params.message,
|
||||
choices: params.choices
|
||||
});
|
||||
return { response: answer };
|
||||
}
|
||||
catch (error) {
|
||||
logger.error('Error in choice selection:', error.message);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
parse: JSON.parse
|
||||
}
|
||||
}
|
||||
];
|
||||
};
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJhY3QuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvYWktdG9vbHMvbGliL3Rvb2xzL2ludGVyYWN0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUVBLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQTtBQUUzQyxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxNQUFNLG1CQUFtQixDQUFBO0FBQ2pELE1BQU0sQ0FBQyxNQUFNLEtBQUssR0FBRyxDQUFDLE1BQWMsRUFBRSxPQUFrQixFQUFjLEVBQUU7SUFDcEUsTUFBTSxNQUFNLEdBQUcsVUFBVSxDQUFDLFVBQVUsRUFBRSxPQUFPLENBQUMsQ0FBQTtJQUM5QyxPQUFPO1FBQ0g7WUFDSSxJQUFJLEVBQUUsVUFBVTtZQUNoQixRQUFRLEVBQUU7Z0JBQ04sSUFBSSxFQUFFLGNBQWM7Z0JBQ3BCLFdBQVcsRUFBRSw2Q0FBNkM7Z0JBQzFELFVBQVUsRUFBRTtvQkFDUixJQUFJLEVBQUUsUUFBUTtvQkFDZCxVQUFVLEVBQUU7d0JBQ1IsUUFBUSxFQUFFOzRCQUNOLElBQUksRUFBRSxRQUFROzRCQUNkLFdBQVcsRUFBRSwwQkFBMEI7eUJBQzFDO3dCQUNELE9BQU8sRUFBRTs0QkFDTCxJQUFJLEVBQUUsUUFBUTs0QkFDZCxXQUFXLEVBQUUsZ0JBQWdCOzRCQUM3QixRQUFRLEVBQUUsSUFBSTt5QkFDakI7cUJBQ0o7b0JBQ0QsUUFBUSxFQUFFLENBQUMsVUFBVSxDQUFDO2lCQUN6QjtnQkFDRCxRQUFRLEVBQUUsS0FBSyxFQUFFLE1BQVcsRUFBRSxFQUFFO29CQUM1QixJQUFJLENBQUM7d0JBQ0QsTUFBTSxNQUFNLEdBQUcsTUFBTSxLQUFLLENBQ3RCOzRCQUNJLE9BQU8sRUFBRSxNQUFNLENBQUMsUUFBUTs0QkFDeEIsT0FBTyxFQUFFLE1BQU0sQ0FBQyxPQUFPO3lCQUMxQixDQUNKLENBQUE7d0JBQ0QsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLEVBQUUsQ0FBQTtvQkFDL0IsQ0FBQztvQkFBQyxPQUFPLEtBQVUsRUFBRSxDQUFDO3dCQUNsQixNQUFNLENBQUMsS0FBSyxDQUFDLHdCQUF3QixFQUFFLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQzt3QkFDdEQsT0FBTyxJQUFJLENBQUM7b0JBQ2hCLENBQUM7Z0JBQ0wsQ0FBQztnQkFDRCxLQUFLLEVBQUUsSUFBSSxDQUFDLEtBQUs7YUFDcEI7U0FDeUI7UUFDOUI7WUFDSSxJQUFJLEVBQUUsVUFBVTtZQUNoQixRQUFRLEVBQUU7Z0JBQ04sSUFBSSxFQUFFLGVBQWU7Z0JBQ3JCLFdBQVcsRUFBRSwwQ0FBMEM7Z0JBQ3ZELFVBQVUsRUFBRTtvQkFDUixJQUFJLEVBQUUsUUFBUTtvQkFDZCxVQUFVLEVBQUU7d0JBQ1IsT0FBTyxFQUFFOzRCQUNMLElBQUksRUFBRSxRQUFROzRCQUNkLFdBQVcsRUFBRSwwQkFBMEI7eUJBQzFDO3dCQUNELE9BQU8sRUFBRTs0QkFDTCxJQUFJLEVBQUUsT0FBTzs0QkFDYixLQUFLLEVBQUUsRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFOzRCQUN6QixXQUFXLEVBQUUsaUJBQWlCO3lCQUNqQzt3QkFDRCxRQUFRLEVBQUU7NEJBQ04sSUFBSSxFQUFFLFNBQVM7NEJBQ2YsV0FBVyxFQUFFLDJCQUEyQjs0QkFDeEMsUUFBUSxFQUFFLElBQUk7eUJBQ2pCO3FCQUNKO29CQUNELFFBQVEsRUFBRSxDQUFDLFNBQVMsRUFBRSxTQUFTLENBQUM7aUJBQ25DO2dCQUNELFFBQVEsRUFBRSxLQUFLLEVBQUUsTUFBVyxFQUFFLEVBQUU7b0JBQzVCLElBQUksQ0FBQzt3QkFDRCxNQUFNLE1BQU0sR0FBRyxNQUFNLE1BQU0sQ0FDdkI7NEJBQ0ksT0FBTyxFQUFFLE1BQU0sQ0FBQyxPQUFPOzRCQUN2QixPQUFPLEVBQUUsTUFBTSxDQUFDLE9BQU87eUJBQzFCLENBQ0osQ0FBQzt3QkFDRixPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sRUFBRSxDQUFBO29CQUMvQixDQUFDO29CQUFDLE9BQU8sS0FBVSxFQUFFLENBQUM7d0JBQ2xCLE1BQU0sQ0FBQyxLQUFLLENBQUMsNEJBQTRCLEVBQUUsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDO3dCQUMxRCxPQUFPLElBQUksQ0FBQztvQkFDaEIsQ0FBQztnQkFDTCxDQUFDO2dCQUNELEtBQUssRUFBRSxJQUFJLENBQUMsS0FBSzthQUNwQjtTQUN5QjtLQUNqQyxDQUFBO0FBQ0wsQ0FBQyxDQUFBIn0=
|
||||
4
packages/kbot/dist-in/ai-tools/lib/tools/keyv.d.ts
vendored
Normal file
4
packages/kbot/dist-in/ai-tools/lib/tools/keyv.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
import Keyv from 'keyv';
|
||||
export declare const store: (storePath: string, ns?: string, opts?: any) => Keyv<any>;
|
||||
export declare const get: (key: string, storePath: string, ns?: string, opts?: any) => Promise<any>;
|
||||
export declare const set: (key: string, value: any, storePath: string, ns?: string, opts?: any) => Promise<boolean>;
|
||||
20
packages/kbot/dist-in/ai-tools/lib/tools/keyv.js
Normal file
20
packages/kbot/dist-in/ai-tools/lib/tools/keyv.js
Normal file
@ -0,0 +1,20 @@
|
||||
import * as path from 'path';
|
||||
import Keyv from 'keyv';
|
||||
import { KeyvFile } from 'keyv-file';
|
||||
import { resolve } from '@polymech/commons';
|
||||
export const store = (storePath, ns = 'ns-unknown', opts = {}) => {
|
||||
const keyvFile = new KeyvFile({
|
||||
filename: path.resolve(resolve(storePath)),
|
||||
writeDelay: 100, // ms
|
||||
});
|
||||
return new Keyv({ store: keyvFile, namespace: ns, ...opts });
|
||||
};
|
||||
export const get = async (key, storePath, ns = 'ns-unknown', opts = {}) => {
|
||||
const keyv = store(storePath, ns, opts);
|
||||
return await keyv.get(key);
|
||||
};
|
||||
export const set = async (key, value, storePath, ns = 'ns-unknown', opts = {}) => {
|
||||
const keyv = store(storePath, ns, opts);
|
||||
return await keyv.set(key, value);
|
||||
};
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoia2V5di5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9haS10b29scy9saWIvdG9vbHMva2V5di50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssSUFBSSxNQUFNLE1BQU0sQ0FBQTtBQUM1QixPQUFPLElBQUksTUFBTSxNQUFNLENBQUE7QUFDdkIsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLFdBQVcsQ0FBQTtBQUVwQyxPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sbUJBQW1CLENBQUE7QUFFM0MsTUFBTSxDQUFDLE1BQU0sS0FBSyxHQUFHLENBQUMsU0FBaUIsRUFBRSxLQUFhLFlBQVksRUFBRSxPQUFZLEVBQUUsRUFBRSxFQUFFO0lBQ2xGLE1BQU0sUUFBUSxHQUFHLElBQUksUUFBUSxDQUFDO1FBQzFCLFFBQVEsRUFBRSxJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUMxQyxVQUFVLEVBQUUsR0FBRyxFQUFFLEtBQUs7S0FDekIsQ0FBQyxDQUFBO0lBQ0YsT0FBTyxJQUFJLElBQUksQ0FBQyxFQUFFLEtBQUssRUFBRSxRQUFRLEVBQUUsU0FBUyxFQUFFLEVBQUUsRUFBRSxHQUFHLElBQUksRUFBRSxDQUFDLENBQUE7QUFDaEUsQ0FBQyxDQUFBO0FBQ0QsTUFBTSxDQUFDLE1BQU0sR0FBRyxHQUFHLEtBQUssRUFBRSxHQUFXLEVBQUUsU0FBaUIsRUFBRSxLQUFhLFlBQVksRUFBRSxPQUFZLEVBQUUsRUFBRSxFQUFFO0lBQ25HLE1BQU0sSUFBSSxHQUFHLEtBQUssQ0FBQyxTQUFTLEVBQUUsRUFBRSxFQUFFLElBQUksQ0FBQyxDQUFBO0lBQ3ZDLE9BQU8sTUFBTSxJQUFJLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFBO0FBQzlCLENBQUMsQ0FBQTtBQUNELE1BQU0sQ0FBQyxNQUFNLEdBQUcsR0FBRyxLQUFLLEVBQUUsR0FBVyxFQUFFLEtBQVUsRUFBRSxTQUFpQixFQUFFLEtBQWEsWUFBWSxFQUFFLE9BQVksRUFBRSxFQUFFLEVBQUU7SUFDL0csTUFBTSxJQUFJLEdBQUcsS0FBSyxDQUFDLFNBQVMsRUFBRSxFQUFFLEVBQUUsSUFBSSxDQUFDLENBQUE7SUFDdkMsT0FBTyxNQUFNLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQyxDQUFBO0FBQ3JDLENBQUMsQ0FBQSJ9
|
||||
2
packages/kbot/dist-in/ai-tools/lib/tools/memory.d.ts
vendored
Normal file
2
packages/kbot/dist-in/ai-tools/lib/tools/memory.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
import { IKBotTask } from '../../types.js';
|
||||
export declare const tools: (target: string, options: IKBotTask) => Array<any>;
|
||||
354
packages/kbot/dist-in/ai-tools/lib/tools/memory.js
Normal file
354
packages/kbot/dist-in/ai-tools/lib/tools/memory.js
Normal file
File diff suppressed because one or more lines are too long
2
packages/kbot/dist-in/ai-tools/lib/tools/npm.d.ts
vendored
Normal file
2
packages/kbot/dist-in/ai-tools/lib/tools/npm.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
import { IKBotTask } from '../../types.js';
|
||||
export declare const tools: (target: string, options: IKBotTask) => Array<any>;
|
||||
150
packages/kbot/dist-in/ai-tools/lib/tools/npm.js
Normal file
150
packages/kbot/dist-in/ai-tools/lib/tools/npm.js
Normal file
File diff suppressed because one or more lines are too long
16
packages/kbot/dist-in/ai-tools/lib/tools/process.d.ts
vendored
Normal file
16
packages/kbot/dist-in/ai-tools/lib/tools/process.d.ts
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
export declare enum STATUS {
|
||||
OK = 0,
|
||||
ERROR = 1,
|
||||
PENDING = 2
|
||||
}
|
||||
export declare class Process {
|
||||
binary: string;
|
||||
cwd: string;
|
||||
args: string;
|
||||
buffer: string[];
|
||||
constructor(options?: any);
|
||||
exec(command: string, args?: string[]): Promise<any>;
|
||||
}
|
||||
export declare class Helper {
|
||||
static run(cwd: any, cmd: string, args: string[], buffer?: string[], debug_stream?: boolean): Promise<any>;
|
||||
}
|
||||
126
packages/kbot/dist-in/ai-tools/lib/tools/process.js
Normal file
126
packages/kbot/dist-in/ai-tools/lib/tools/process.js
Normal file
@ -0,0 +1,126 @@
|
||||
import { logger } from '../../index.js';
|
||||
import { spawn } from 'child_process';
|
||||
export var STATUS;
|
||||
(function (STATUS) {
|
||||
STATUS[STATUS["OK"] = 0] = "OK";
|
||||
STATUS[STATUS["ERROR"] = 1] = "ERROR";
|
||||
STATUS[STATUS["PENDING"] = 2] = "PENDING";
|
||||
})(STATUS || (STATUS = {}));
|
||||
const fatalHandler = (message, fn) => {
|
||||
if (message.startsWith('fatal:')) {
|
||||
fn('\t\ ' + message);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const defaultFilter = (message) => {
|
||||
return message.length > 0 &&
|
||||
message !== '\n' &&
|
||||
message !== '\r' &&
|
||||
message !== '\r\n' &&
|
||||
!message.startsWith('Debugger attached') &&
|
||||
!message.includes('NODE_TLS_REJECT_UNAUTHORIZED') &&
|
||||
!message.includes('Waiting for the debugger to disconnect');
|
||||
};
|
||||
const subscribe = (signal, collector = () => { }) => {
|
||||
if (!signal || !signal.on) {
|
||||
return;
|
||||
}
|
||||
signal.on('message', (message) => logger.debug('message', message));
|
||||
signal.on('error', (error) => logger.error('std-error', error));
|
||||
signal.on('data', (data) => {
|
||||
/*
|
||||
const msg = data.toString().replace(ansiRegex(), "")
|
||||
if (!defaultFilter(msg)) {
|
||||
return
|
||||
}
|
||||
collector(msg)*/
|
||||
process.stdout.write(data);
|
||||
});
|
||||
};
|
||||
const merge = (buffer, data) => buffer.concat(data);
|
||||
const hook = (child, resolve, reject, cmd, buffer = []) => {
|
||||
const collector = (data) => { buffer.push(data); };
|
||||
//subscribe(child.stderr, collector)
|
||||
//process.stdin.pipe(child.stdin)
|
||||
debugger;
|
||||
child.on('exit', (code, signal) => {
|
||||
debugger;
|
||||
if (code) {
|
||||
resolve({
|
||||
code: STATUS.ERROR,
|
||||
command: cmd,
|
||||
error: code,
|
||||
messages: buffer
|
||||
});
|
||||
}
|
||||
else {
|
||||
resolve({
|
||||
code: STATUS.OK,
|
||||
command: cmd,
|
||||
messages: buffer
|
||||
});
|
||||
}
|
||||
});
|
||||
return child;
|
||||
};
|
||||
export class Process {
|
||||
binary = '';
|
||||
cwd = '';
|
||||
args = '';
|
||||
buffer = [];
|
||||
constructor(options = {}) {
|
||||
this.binary = options.binary || this.binary;
|
||||
this.cwd = options.cwd || process.cwd();
|
||||
this.buffer = options.buffer || [];
|
||||
}
|
||||
async exec(command, args = []) {
|
||||
args = [command].concat(args);
|
||||
try {
|
||||
let cmd = `${this.binary} ${args.join(' ')}`;
|
||||
/*
|
||||
const p = new Promise<any>((resolve, reject) => {
|
||||
const p = exec(cmd, {
|
||||
cwd: this.cwd
|
||||
})
|
||||
return hook(p, resolve, reject, this.binary + ' ' + args.join(' '), this.buffer)
|
||||
})
|
||||
return p
|
||||
*/
|
||||
try {
|
||||
//stdio: ['pipe', 'pipe', 'pipe'],
|
||||
debugger;
|
||||
const p = new Promise((resolve, reject) => {
|
||||
const cp = spawn(cmd, args, {
|
||||
cwd: this.cwd,
|
||||
shell: true,
|
||||
stdio: 'inherit',
|
||||
env: {
|
||||
...process.env
|
||||
},
|
||||
});
|
||||
return hook(cp, resolve, reject, cmd, this.buffer);
|
||||
});
|
||||
return p;
|
||||
}
|
||||
catch (e) {
|
||||
logger.error('Error executing command', e);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
logger.error('Error executing command', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
export class Helper {
|
||||
static async run(cwd, cmd, args, buffer = [], debug_stream = false) {
|
||||
debug_stream && logger.info(`Run ${cmd} in ${cwd}`, args);
|
||||
const gitProcess = new Process({
|
||||
cwd,
|
||||
binary: cmd,
|
||||
buffer
|
||||
});
|
||||
return gitProcess.exec('', args);
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvY2Vzcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9haS10b29scy9saWIvdG9vbHMvcHJvY2Vzcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sZ0JBQWdCLENBQUE7QUFFdkMsT0FBTyxFQUFnQixLQUFLLEVBQUUsTUFBTSxlQUFlLENBQUE7QUFFbkQsTUFBTSxDQUFOLElBQVksTUFJWDtBQUpELFdBQVksTUFBTTtJQUNkLCtCQUFFLENBQUE7SUFDRixxQ0FBSyxDQUFBO0lBQ0wseUNBQU8sQ0FBQTtBQUNYLENBQUMsRUFKVyxNQUFNLEtBQU4sTUFBTSxRQUlqQjtBQUVELE1BQU0sWUFBWSxHQUFHLENBQUMsT0FBZSxFQUFFLEVBQXlCLEVBQVcsRUFBRTtJQUN6RSxJQUFJLE9BQU8sQ0FBQyxVQUFVLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQztRQUMvQixFQUFFLENBQUMsTUFBTSxHQUFHLE9BQU8sQ0FBQyxDQUFBO1FBQ3BCLE9BQU8sSUFBSSxDQUFDO0lBQ2hCLENBQUM7SUFDRCxPQUFPLEtBQUssQ0FBQTtBQUNoQixDQUFDLENBQUE7QUFFRCxNQUFNLGFBQWEsR0FBRyxDQUFDLE9BQWUsRUFBVyxFQUFFO0lBQy9DLE9BQU8sT0FBTyxDQUFDLE1BQU0sR0FBRyxDQUFDO1FBQ3JCLE9BQU8sS0FBSyxJQUFJO1FBQ2hCLE9BQU8sS0FBSyxJQUFJO1FBQ2hCLE9BQU8sS0FBSyxNQUFNO1FBQ2xCLENBQUMsT0FBTyxDQUFDLFVBQVUsQ0FBQyxtQkFBbUIsQ0FBQztRQUN4QyxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsOEJBQThCLENBQUM7UUFDakQsQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLHdDQUF3QyxDQUFDLENBQUE7QUFDbkUsQ0FBQyxDQUFBO0FBRUQsTUFBTSxTQUFTLEdBQUcsQ0FBQyxNQUF1QixFQUFFLFlBQWlDLEdBQUcsRUFBRSxHQUFHLENB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
|
||||
2
packages/kbot/dist-in/ai-tools/lib/tools/search.d.ts
vendored
Normal file
2
packages/kbot/dist-in/ai-tools/lib/tools/search.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
import { IKBotTask } from '../../types.js';
|
||||
export declare const tools: (target: string, options: IKBotTask) => Array<any>;
|
||||
90
packages/kbot/dist-in/ai-tools/lib/tools/search.js
Normal file
90
packages/kbot/dist-in/ai-tools/lib/tools/search.js
Normal file
@ -0,0 +1,90 @@
|
||||
import { isArray } from '@polymech/core/primitives';
|
||||
import { CONFIG_DEFAULT } from '@polymech/commons';
|
||||
import { toolLogger } from '../../index.js';
|
||||
export const tools = (target, options) => {
|
||||
const logger = toolLogger('search', options);
|
||||
return [
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'google',
|
||||
description: 'Searches Google for the given query',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
query: { type: 'string' }
|
||||
},
|
||||
required: ['query']
|
||||
},
|
||||
function: async (params) => {
|
||||
const { query } = params;
|
||||
const config = CONFIG_DEFAULT();
|
||||
let apiKey = config?.google?.api_key;
|
||||
let cse = config?.google?.cse;
|
||||
if (!config || !apiKey || !cse) {
|
||||
logger.debug("Config not found in $HOME/.osr/config.json. " +
|
||||
"Optionally, export OSR_CONFIG with the path to the configuration file " +
|
||||
"");
|
||||
return undefined;
|
||||
}
|
||||
const res = await fetch(`https://www.googleapis.com/customsearch/v1?key=${apiKey}&cx=${cse}&q=${encodeURIComponent(query)}`);
|
||||
const data = await res.json();
|
||||
let results = data.items?.map((item) => ({
|
||||
title: item.title,
|
||||
link: item.link,
|
||||
snippet: item.snippet,
|
||||
...item
|
||||
})) ?? [];
|
||||
return JSON.stringify(results);
|
||||
},
|
||||
parse: JSON.parse
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'serpapi',
|
||||
description: 'Searches Serpapi (finds locations (engine:google_local), places on the map (engine:google_maps) ) for the given query',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
query: { type: 'string' },
|
||||
engine: { type: 'string', default: 'google' },
|
||||
},
|
||||
required: ['query']
|
||||
},
|
||||
function: async (params) => {
|
||||
const { query, engine } = params;
|
||||
const config = CONFIG_DEFAULT();
|
||||
let apiKey = config?.serpapi?.key || config?.serpapi?.api_key;
|
||||
if (!config || !apiKey) {
|
||||
logger.debug("Config not found in $HOME/.osr/config.json. " +
|
||||
"Optionally, export OSR_CONFIG with the path to the configuration file " +
|
||||
"");
|
||||
return undefined;
|
||||
}
|
||||
const url = `https://serpapi.com/search?api_key=${apiKey}&engine=${engine || 'google'}&q=${encodeURIComponent(query)}&google_domain=google.com`;
|
||||
const res = await fetch(url);
|
||||
logger.debug(`Searching ${url}`);
|
||||
if (!res.ok) {
|
||||
throw new Error(`HTTP error! status: ${res.status}`);
|
||||
}
|
||||
const data = await res.json();
|
||||
let items = data.organic_results || data.local_results || data.place_results || data.places || data.maps_results || [];
|
||||
if (items && !isArray(items)) {
|
||||
items = [items];
|
||||
}
|
||||
let results = items.map((item) => ({
|
||||
title: item.title,
|
||||
link: item.link,
|
||||
snippet: item.snippet,
|
||||
...item
|
||||
})) ?? [];
|
||||
return JSON.stringify(results);
|
||||
},
|
||||
parse: JSON.parse
|
||||
}
|
||||
}
|
||||
];
|
||||
};
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VhcmNoLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2FpLXRvb2xzL2xpYi90b29scy9zZWFyY2gudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBRUEsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLDJCQUEyQixDQUFBO0FBQ25ELE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQTtBQUNsRCxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sZ0JBQWdCLENBQUE7QUFHM0MsTUFBTSxDQUFDLE1BQU0sS0FBSyxHQUFHLENBQUMsTUFBYyxFQUFFLE9BQWtCLEVBQWMsRUFBRTtJQUNwRSxNQUFNLE1BQU0sR0FBRyxVQUFVLENBQUMsUUFBUSxFQUFFLE9BQU8sQ0FBQyxDQUFBO0lBQzVDLE9BQU87UUFDSDtZQUNJLElBQUksRUFBRSxVQUFVO1lBQ2hCLFFBQVEsRUFBRTtnQkFDTixJQUFJLEVBQUUsUUFBUTtnQkFDZCxXQUFXLEVBQUUscUNBQXFDO2dCQUNsRCxVQUFVLEVBQUU7b0JBQ1IsSUFBSSxFQUFFLFFBQVE7b0JBQ2QsVUFBVSxFQUFFO3dCQUNSLEtBQUssRUFBRSxFQUFFLElBQUksRUFBRSxRQUFRLEVBQUU7cUJBQzVCO29CQUNELFFBQVEsRUFBRSxDQUFDLE9BQU8sQ0FBQztpQkFDdEI7Z0JBQ0QsUUFBUSxFQUFFLEtBQUssRUFBRSxNQUFXLEVBQUUsRUFBRTtvQkFDNUIsTUFBTSxFQUFFLEtBQUssRUFBRSxHQUFHLE1BQU0sQ0FBQTtvQkFDeEIsTUFBTSxNQUFNLEdBQUcsY0FBYyxFQUFTLENBQUE7b0JBQ3RDLElBQUksTUFBTSxHQUFHLE1BQU0sRUFBRSxNQUFNLEVBQUUsT0FBTyxDQUFBO29CQUNwQyxJQUFJLEdBQUcsR0FBRyxNQUFNLEVBQUUsTUFBTSxFQUFFLEdBQUcsQ0FBQTtvQkFDN0IsSUFBSSxDQUFDLE1BQU0sSUFBSSxDQUFDLE1BQU0sSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO3dC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
|
||||
2
packages/kbot/dist-in/ai-tools/lib/tools/terminal.d.ts
vendored
Normal file
2
packages/kbot/dist-in/ai-tools/lib/tools/terminal.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
import { IKBotTask } from '../../types.js';
|
||||
export declare const tools: (target: string, options: IKBotTask) => Array<any>;
|
||||
116
packages/kbot/dist-in/ai-tools/lib/tools/terminal.js
Normal file
116
packages/kbot/dist-in/ai-tools/lib/tools/terminal.js
Normal file
@ -0,0 +1,116 @@
|
||||
import * as path from 'path';
|
||||
import { spawn } from 'child_process';
|
||||
import { toolLogger } from '../../index.js';
|
||||
import { Helper } from './process.js';
|
||||
export const tools = (target, options) => {
|
||||
const logger = toolLogger('terminal', options);
|
||||
return [
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'execute_command',
|
||||
description: 'Execute a terminal command and capture output',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
command: {
|
||||
type: 'string',
|
||||
description: 'Command to execute'
|
||||
},
|
||||
args: {
|
||||
type: 'array',
|
||||
items: { type: 'string' },
|
||||
description: 'Command arguments',
|
||||
optional: true
|
||||
},
|
||||
cwd: {
|
||||
type: 'string',
|
||||
description: 'Working directory for command execution',
|
||||
optional: true
|
||||
},
|
||||
background: {
|
||||
type: 'boolean',
|
||||
description: 'Run command in background (non-blocking)',
|
||||
optional: true,
|
||||
default: false
|
||||
},
|
||||
window: {
|
||||
type: 'boolean',
|
||||
description: 'Open command in new terminal window',
|
||||
optional: true,
|
||||
default: false
|
||||
},
|
||||
detached: {
|
||||
type: 'boolean',
|
||||
description: 'Run process detached from parent',
|
||||
optional: true,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
required: ['command']
|
||||
},
|
||||
function: async (params) => {
|
||||
try {
|
||||
debugger;
|
||||
const cwd = params.cwd ? path.join(target, params.cwd) : target;
|
||||
const args = params.args || [];
|
||||
logger.debug(`Tool::Terminal : ExecuteCommand Running '${params.command}' in ${cwd}`, params);
|
||||
if (params.detached) {
|
||||
const isWindows = process.platform === 'win32';
|
||||
if (isWindows) {
|
||||
spawn('cmd', ['/c', 'start', 'cmd', '/k', params.command, ...args], {
|
||||
cwd: cwd,
|
||||
detached: true,
|
||||
stdio: 'ignore'
|
||||
});
|
||||
}
|
||||
else {
|
||||
// For macOS/Linux
|
||||
spawn('x-terminal-emulator', ['-e', `${params.command} ${args.join(' ')}`], {
|
||||
cwd: cwd,
|
||||
detached: true,
|
||||
stdio: 'ignore'
|
||||
});
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
output: 'Command launched in new window',
|
||||
error: null
|
||||
};
|
||||
}
|
||||
if (params.background || params.detached) {
|
||||
const child = spawn(params.command, args, {
|
||||
cwd: cwd,
|
||||
detached: params.detached === true,
|
||||
stdio: 'ignore'
|
||||
});
|
||||
if (params.detached) {
|
||||
child.unref();
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
output: `Process started with PID: ${child.pid}`,
|
||||
error: null
|
||||
};
|
||||
}
|
||||
const cmd = `${params.command} ${args.join(' ')}`.trim();
|
||||
logger.debug(`Tool::ExecuteCommand Running '${cmd}' in ${cwd}`);
|
||||
const collector = [];
|
||||
const ret = await Helper.run(cwd, cmd, [], collector, true);
|
||||
return ret;
|
||||
}
|
||||
catch (error) {
|
||||
logger.error('Error executing command', error);
|
||||
return {
|
||||
success: false,
|
||||
output: null,
|
||||
error: error.message
|
||||
};
|
||||
}
|
||||
},
|
||||
parse: JSON.parse
|
||||
}
|
||||
}
|
||||
];
|
||||
};
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVybWluYWwuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvYWktdG9vbHMvbGliL3Rvb2xzL3Rlcm1pbmFsLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFBO0FBRTVCLE9BQU8sRUFBRSxLQUFLLEVBQUUsTUFBTSxlQUFlLENBQUE7QUFDckMsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGdCQUFnQixDQUFBO0FBRTNDLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxjQUFjLENBQUE7QUFFckMsTUFBTSxDQUFDLE1BQU0sS0FBSyxHQUFHLENBQUMsTUFBYyxFQUFFLE9BQWtCLEVBQWMsRUFBRTtJQUNwRSxNQUFNLE1BQU0sR0FBRyxVQUFVLENBQUMsVUFBVSxFQUFFLE9BQU8sQ0FBQyxDQUFBO0lBQzlDLE9BQU87UUFDSDtZQUNJLElBQUksRUFBRSxVQUFVO1lBQ2hCLFFBQVEsRUFBRTtnQkFDTixJQUFJLEVBQUUsaUJBQWlCO2dCQUN2QixXQUFXLEVBQUUsK0NBQStDO2dCQUM1RCxVQUFVLEVBQUU7b0JBQ1IsSUFBSSxFQUFFLFFBQVE7b0JBQ2QsVUFBVSxFQUFFO3dCQUNSLE9BQU8sRUFBRTs0QkFDTCxJQUFJLEVBQUUsUUFBUTs0QkFDZCxXQUFXLEVBQUUsb0JBQW9CO3lCQUNwQzt3QkFDRCxJQUFJLEVBQUU7NEJBQ0YsSUFBSSxFQUFFLE9BQU87NEJBQ2IsS0FBSyxFQUFFLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRTs0QkFDekIsV0FBVyxFQUFFLG1CQUFtQjs0QkFDaEMsUUFBUSxFQUFFLElBQUk7eUJBQ2pCO3dCQUNELEdBQUcsRUFBRTs0QkFDRCxJQUFJLEVBQUUsUUFBUTs0QkFDZCxXQUFXLEVBQUUseUNBQXlDOzRCQUN0RCxRQUFRLEVBQUUsSUFBSTt5QkFDakI7d0JBQ0QsVUFBVSxFQUFFOzRCQUNSLElBQUksRUFBRSxTQUFTOzRCQUNmLFdBQVcs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
|
||||
10
packages/kbot/dist-in/ai-tools/lib/tools/tools.d.ts
vendored
Normal file
10
packages/kbot/dist-in/ai-tools/lib/tools/tools.d.ts
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
export declare const tools: {
|
||||
fs: (target: string, options: import("../../types.js").IKBotTask) => Array<any>;
|
||||
npm: (target: string, options: import("../../types.js").IKBotTask) => Array<any>;
|
||||
git: (target: string, options: import("../../types.js").IKBotTask) => Array<any>;
|
||||
terminal: (target: string, options: import("../../types.js").IKBotTask) => Array<any>;
|
||||
interact: (target: string, options: import("../../types.js").IKBotTask) => Array<any>;
|
||||
user: (target: string, options: import("../../types.js").IKBotTask) => Array<any>;
|
||||
search: (target: string, options: import("../../types.js").IKBotTask) => Array<any>;
|
||||
memory: (target: string, options: import("../../types.js").IKBotTask) => Array<any>;
|
||||
};
|
||||
23
packages/kbot/dist-in/ai-tools/lib/tools/tools.js
Normal file
23
packages/kbot/dist-in/ai-tools/lib/tools/tools.js
Normal file
@ -0,0 +1,23 @@
|
||||
import { tools as fsTools } from './fs.js';
|
||||
import { tools as npmTools } from './npm.js';
|
||||
import { tools as gitTools } from './git.js';
|
||||
import { tools as terminalTools } from './terminal.js';
|
||||
import { tools as interactTools } from './interact.js';
|
||||
import { tools as userTools } from './user.js';
|
||||
import { tools as search } from './search.js';
|
||||
//import { tools as webTools } from './web.js'
|
||||
import { tools as memoryTools } from './memory.js';
|
||||
// import { tools as emailTools } from './email'
|
||||
export const tools = {
|
||||
fs: fsTools,
|
||||
npm: npmTools,
|
||||
git: gitTools,
|
||||
terminal: terminalTools,
|
||||
interact: interactTools,
|
||||
user: userTools,
|
||||
search: search,
|
||||
// web: webTools,
|
||||
memory: memoryTools
|
||||
// email: emailTools
|
||||
};
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9vbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvYWktdG9vbHMvbGliL3Rvb2xzL3Rvb2xzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxLQUFLLElBQUksT0FBTyxFQUFFLE1BQU0sU0FBUyxDQUFBO0FBQzFDLE9BQU8sRUFBRSxLQUFLLElBQUksUUFBUSxFQUFFLE1BQU0sVUFBVSxDQUFBO0FBQzVDLE9BQU8sRUFBRSxLQUFLLElBQUksUUFBUSxFQUFFLE1BQU0sVUFBVSxDQUFBO0FBQzVDLE9BQU8sRUFBRSxLQUFLLElBQUksYUFBYSxFQUFFLE1BQU0sZUFBZSxDQUFBO0FBQ3RELE9BQU8sRUFBRSxLQUFLLElBQUksYUFBYSxFQUFFLE1BQU0sZUFBZSxDQUFBO0FBQ3RELE9BQU8sRUFBRSxLQUFLLElBQUksU0FBUyxFQUFFLE1BQU0sV0FBVyxDQUFBO0FBQzlDLE9BQU8sRUFBRSxLQUFLLElBQUksTUFBTSxFQUFFLE1BQU0sYUFBYSxDQUFBO0FBQzdDLDhDQUE4QztBQUM5QyxPQUFPLEVBQUUsS0FBSyxJQUFJLFdBQVcsRUFBRSxNQUFNLGFBQWEsQ0FBQTtBQUNsRCxnREFBZ0Q7QUFFaEQsTUFBTSxDQUFDLE1BQU0sS0FBSyxHQUFHO0lBQ2pCLEVBQUUsRUFBRSxPQUFPO0lBQ1gsR0FBRyxFQUFFLFFBQVE7SUFDYixHQUFHLEVBQUUsUUFBUTtJQUNiLFFBQVEsRUFBRSxhQUFhO0lBQ3ZCLFFBQVEsRUFBRSxhQUFhO0lBQ3ZCLElBQUksRUFBRSxTQUFTO0lBQ2YsTUFBTSxFQUFFLE1BQU07SUFDZCxpQkFBaUI7SUFDakIsTUFBTSxFQUFFLFdBQVc7SUFDbkIsb0JBQW9CO0NBQ3ZCLENBQUEifQ==
|
||||
4
packages/kbot/dist-in/ai-tools/lib/tools/user.d.ts
vendored
Normal file
4
packages/kbot/dist-in/ai-tools/lib/tools/user.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
import { IKBotTask } from '../../types.js';
|
||||
export declare const mime: (file?: string) => any;
|
||||
export declare const fileToBase64: (filePath: string) => string | null;
|
||||
export declare const tools: (target: string, options: IKBotTask) => Array<any>;
|
||||
78
packages/kbot/dist-in/ai-tools/lib/tools/user.js
Normal file
78
packages/kbot/dist-in/ai-tools/lib/tools/user.js
Normal file
@ -0,0 +1,78 @@
|
||||
import { parse, join } from 'path';
|
||||
import * as fs from 'fs';
|
||||
import { lookup } from 'mime-types';
|
||||
import { toolLogger } from '../../index.js';
|
||||
export const mime = (file = '') => parse(file).ext ? lookup(file) : null;
|
||||
//const screenshot = require('screenshot-desktop')
|
||||
export const fileToBase64 = (filePath) => {
|
||||
try {
|
||||
const fileBuffer = fs.readFileSync(filePath);
|
||||
const mimeType = lookup(filePath);
|
||||
if (!mimeType) {
|
||||
throw new Error('Unable to determine MIME type.');
|
||||
}
|
||||
const base64Data = fileBuffer.toString('base64');
|
||||
return `data:${mimeType};base64,${base64Data}`;
|
||||
}
|
||||
catch (error) {
|
||||
console.error('fileToBase64 : Error reading file:', error);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
export const tools = (target, options) => {
|
||||
const logger = toolLogger('user', options);
|
||||
return [
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'capture_screen',
|
||||
description: 'Capture a screenshot and store it as file (jpg). Returns the path to the file',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
file: { type: 'string' }
|
||||
},
|
||||
required: ['file']
|
||||
},
|
||||
function: async (params) => {
|
||||
try {
|
||||
const outputPath = join(target, params.file);
|
||||
const takeScreenshot = async () => {
|
||||
/*
|
||||
return new Promise((resolve, reject) => {
|
||||
screenshot({ format: 'jpg' }).then((img) => {
|
||||
write(outputPath, img)
|
||||
resolve({ success: true, path: outputPath})
|
||||
}).catch(reject)
|
||||
})
|
||||
*/
|
||||
};
|
||||
const { path } = await takeScreenshot();
|
||||
return {
|
||||
"role": "user",
|
||||
"content": [
|
||||
/*
|
||||
{
|
||||
type: "image_url",
|
||||
image_url: {
|
||||
url: fileToBase64( path),
|
||||
}
|
||||
}
|
||||
*/
|
||||
]
|
||||
};
|
||||
}
|
||||
catch (error) {
|
||||
logger.error('Error capturing screenshot:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.message
|
||||
};
|
||||
}
|
||||
},
|
||||
parse: JSON.parse
|
||||
}
|
||||
}
|
||||
];
|
||||
};
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXNlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9haS10b29scy9saWIvdG9vbHMvdXNlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEVBQUUsS0FBSyxFQUFFLElBQUksRUFBRSxNQUFNLE1BQU0sQ0FBQTtBQUdsQyxPQUFPLEtBQUssRUFBRSxNQUFNLElBQUksQ0FBQTtBQUN4QixPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sWUFBWSxDQUFBO0FBRW5DLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQTtBQUUzQyxNQUFNLENBQUMsTUFBTSxJQUFJLEdBQUcsQ0FBQyxPQUFlLEVBQUUsRUFBRSxFQUFFLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUE7QUFFaEYsa0RBQWtEO0FBRWxELE1BQU0sQ0FBQyxNQUFNLFlBQVksR0FBRyxDQUFDLFFBQWdCLEVBQWlCLEVBQUU7SUFDNUQsSUFBSSxDQUFDO1FBQ0QsTUFBTSxVQUFVLEdBQUcsRUFBRSxDQUFDLFlBQVksQ0FBQyxRQUFRLENBQUMsQ0FBQTtRQUM1QyxNQUFNLFFBQVEsR0FBRyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUE7UUFDakMsSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFDO1lBQ1osTUFBTSxJQUFJLEtBQUssQ0FBQyxnQ0FBZ0MsQ0FBQyxDQUFBO1FBQ3JELENBQUM7UUFDRCxNQUFNLFVBQVUsR0FBRyxVQUFVLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxDQUFBO1FBQ2hELE9BQU8sUUFBUSxRQUFRLFdBQVcsVUFBVSxFQUFFLENBQUE7SUFDbEQsQ0FBQztJQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7UUFDYixPQUFPLENBQUMsS0FBSyxDQUFDLG9DQUFvQyxFQUFFLEtBQUssQ0FBQyxDQUFBO1FBQzFELE9BQU8sSUFBSSxDQUFBO0lBQ2YsQ0FBQztBQUNMLENBQUMsQ0FBQTtBQUNELE1BQU0sQ0FBQyxNQUFNLEtBQUssR0FBRyxDQUFDLE1BQWMsRUFBRSxPQUFrQixFQUFjLEVBQUU7SUFDcEUsTUFBTSxNQUFNLEdBQUcsVUFBVSxDQUFDLE1BQU0sRUFBRSxPQUFPLENBQUMsQ0FBQTtJQUMxQyxPQUFPO1FBQ0g7WUFDSSxJQUFJLEVBQUUsVUFBVTtZQUNoQixRQUFRLEVBQUU7Z0JBQ04sSUFBSSxFQUFFLGdCQUFnQjtnQkFDdEIsV0FBVyxFQUFFLCtFQUErRTtnQkFDNUYsVUFBVSxFQUFFO29CQUNSLElBQUksRUFBRSxRQUFRO29CQUNkLFVBQVUsRUFBRTt3QkFDUixJQUFJLEVBQUUsRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFO3FCQUMzQjtvQkFDRCxRQUFRLEVBQUUsQ0FBQyxNQUFNLENBQUM7aUJBQ3JCO2dCQUNELFFBQVEsRUFBRSxLQUFLLEVBQUUsTUFBVyxFQUFFLEVBQUU7b0JBQzVCLElBQUksQ0FBQzt3QkFDRCxNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsTUFBTSxFQUFFLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQTt3QkFDNUMsTUFBTSxjQUFjLEdBQUcsS0FBSyxJQUFtQixFQUFFOzRCQUM3Qzs7Ozs7Ozs4QkFPRTt3QkFDTixDQUFDLENBQUE7d0JBQ0QsTUFBTSxFQUFFLElBQUksRUFBRSxHQUFHLE1BQU0sY0FBYyxFQUFFLENBQUE7d0JBQ3ZDLE9BQU87NEJBQ0gsTUFBTSxFQUFFLE1BQU07NEJBQ2QsU0FBUyxFQUNQOzRCQUNFOzs7Ozs7OzhCQU9FOzZCQUNIO3lCQUNKLENBQUE7b0JBQ1AsQ0FBQztvQkFBQyxPQUFPLEtBQVUsRUFBRSxDQUFDO3dCQUNsQixNQUFNLENBQUMsS0FBSyxDQUFDLDZCQUE2QixFQUFFLEtBQUssQ0FBQyxDQUFDO3dCQUNuRCxPQUFPOzRCQUNILE9BQU8sRUFBRSxLQUFLOzRCQUNkLEtBQUssRUFBRSxLQUFLLENBQUMsT0FBTzt5QkFDdkIsQ0FBQztvQkFDTixDQUFDO2dCQUNMLENBQUM7Z0JBQ0QsS0FBSyxFQUFFLElBQUksQ0FBQyxLQUFLO2FBQ3BCO1NBQ3lCO0tBQ2pDLENBQUM7QUFDTixDQUFDLENBQUMifQ==
|
||||
24
packages/kbot/dist-in/ai-tools/types.d.ts
vendored
Normal file
24
packages/kbot/dist-in/ai-tools/types.d.ts
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
import { ChatCompletion, ChatCompletionMessage, ChatCompletionMessageParam } from 'openai/resources';
|
||||
import { IKBotOptions } from './types_kbot.js';
|
||||
import OpenAI from 'openai';
|
||||
import { Logger, ILogObj } from 'tslog';
|
||||
import { RunnableFunctionWithParse } from 'openai/lib/RunnableFunction';
|
||||
export type onToolBefore = (ctx: RunnableFunctionWithParse<any>, args: any) => Promise<any>;
|
||||
export type onToolAfter = (ctx: RunnableFunctionWithParse<any>, args: any, result?: any) => Promise<any>;
|
||||
export interface ICollector {
|
||||
onMessage: (message: ChatCompletionMessageParam) => void;
|
||||
onToolCall: (tool: ChatCompletionMessage.FunctionCall) => void;
|
||||
onFunctionCallResult: (content: string) => void;
|
||||
onChatCompletion: (completion: ChatCompletion) => void;
|
||||
onContent: (content: string) => void;
|
||||
onTool: (category: string, name: string, args: any, result?: any) => void;
|
||||
onToolBefore: onToolBefore;
|
||||
onToolAfter: onToolAfter;
|
||||
}
|
||||
export interface IKBotTask extends IKBotOptions {
|
||||
client?: OpenAI;
|
||||
collector?: ICollector;
|
||||
onRun?: (ctx: IKBotTask) => Promise<IKBotTask>;
|
||||
logger?: Logger<ILogObj>;
|
||||
customTools?: any[];
|
||||
}
|
||||
2
packages/kbot/dist-in/ai-tools/types.js
Normal file
2
packages/kbot/dist-in/ai-tools/types.js
Normal file
@ -0,0 +1,2 @@
|
||||
export {};
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYWktdG9vbHMvdHlwZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9
|
||||
569
packages/kbot/dist-in/ai-tools/types_kbot.d.ts
vendored
Normal file
569
packages/kbot/dist-in/ai-tools/types_kbot.d.ts
vendored
Normal file
@ -0,0 +1,569 @@
|
||||
export interface IKBotOptions {
|
||||
/** Target directory */
|
||||
path?: string;
|
||||
/** The prompt. Supports file paths and environment variables. */
|
||||
prompt?: string | undefined;
|
||||
/** Optional output path for modified files (Tool mode only) */
|
||||
output?: string | undefined;
|
||||
/** Optional destination path for the result, will substitute ${MODEL_NAME} and ${ROUTER} in the path. Optional, used for "completion" mode */
|
||||
dst?: string | undefined;
|
||||
/** How to handle output if --dst file already exists: "concat" (append) or "merge" (try to merge structures if possible, otherwise append). Only used if --dst is specified. */
|
||||
append?: ("concat" | "merge" | "replace") | undefined;
|
||||
/** Specify how to wrap the output, "meta (file name, absolute path, cwd)" or "none". */
|
||||
wrap?: "meta" | "none";
|
||||
/** Iterate over items, supported: GLOB | Path to JSON File | array of strings (comma separated). To test different models, use --each="gpt-3.5-turbo,gpt-4o", the actual string will exposed as variable `ITEM`, eg: --dst="${ITEM}-output.md" */
|
||||
each?: string | undefined;
|
||||
/** Disable tools categories, eg: --disable=fs,git,interact,terminal,search,web,email,user */
|
||||
disable?: string[];
|
||||
/** List of specific tools to disable */
|
||||
disableTools?: string[];
|
||||
/** List of tools to use. Can be built-in tool names or paths to custom tool files. Default: fs,git,interact,terminal,search,web,email,user */
|
||||
tools?: (string[] | string);
|
||||
/** Comma separated glob patterns or paths, eg --include=src/*.tsx,src/*.ts --include=package.json */
|
||||
include?: string[] | undefined;
|
||||
/** Comma separated glob patterns or paths, eg --exclude=src/*.tsx,src/*.ts --exclude=package.json */
|
||||
exclude?: string[] | undefined;
|
||||
/** Specify a glob extension behavior. Available presets: match-cpp. Also accepts a custom glob pattern with variables like ${SRC_DIR}, ${SRC_NAME}, ${SRC_EXT}. E.g., "match-cpp" or "${SRC_DIR}/${SRC_NAME}*.cpp" */
|
||||
globExtension?: (("match-cpp") | string) | undefined;
|
||||
/** Explicit API key to use */
|
||||
api_key?: string | undefined;
|
||||
/** AI model to use for processing. Available models:
|
||||
[35m[1m[22m[39m
|
||||
[35m[1m OpenRouter models:[22m[39m
|
||||
[35m[1m[22m[39m
|
||||
ai21/jamba-large-1.7 | paid
|
||||
aion-labs/aion-1.0 | paid
|
||||
aion-labs/aion-1.0-mini | paid
|
||||
aion-labs/aion-2.0 | paid
|
||||
aion-labs/aion-rp-llama-3.1-8b | paid
|
||||
alfredpros/codellama-7b-instruct-solidity | paid
|
||||
allenai/molmo-2-8b | paid
|
||||
allenai/olmo-2-0325-32b-instruct | paid
|
||||
allenai/olmo-3-32b-think | paid
|
||||
allenai/olmo-3-7b-instruct | paid
|
||||
allenai/olmo-3-7b-think | paid
|
||||
allenai/olmo-3.1-32b-instruct | paid
|
||||
allenai/olmo-3.1-32b-think | paid
|
||||
amazon/nova-2-lite-v1 | paid
|
||||
amazon/nova-lite-v1 | paid
|
||||
amazon/nova-micro-v1 | paid
|
||||
amazon/nova-premier-v1 | paid
|
||||
amazon/nova-pro-v1 | paid
|
||||
anthropic/claude-3-haiku | paid
|
||||
anthropic/claude-3.5-haiku | paid
|
||||
anthropic/claude-3.5-sonnet | paid
|
||||
anthropic/claude-3.7-sonnet | paid
|
||||
anthropic/claude-3.7-sonnet:thinking | paid
|
||||
anthropic/claude-haiku-4.5 | paid
|
||||
anthropic/claude-opus-4 | paid
|
||||
anthropic/claude-opus-4.1 | paid
|
||||
anthropic/claude-opus-4.5 | paid
|
||||
anthropic/claude-opus-4.6 | paid
|
||||
anthropic/claude-sonnet-4 | paid
|
||||
anthropic/claude-sonnet-4.5 | paid
|
||||
anthropic/claude-sonnet-4.6 | paid
|
||||
arcee-ai/coder-large | paid
|
||||
arcee-ai/maestro-reasoning | paid
|
||||
arcee-ai/spotlight | paid
|
||||
arcee-ai/trinity-large-preview:free | free
|
||||
arcee-ai/trinity-mini | paid
|
||||
arcee-ai/trinity-mini:free | free
|
||||
arcee-ai/virtuoso-large | paid
|
||||
openrouter/auto | paid
|
||||
baidu/ernie-4.5-21b-a3b | paid
|
||||
baidu/ernie-4.5-21b-a3b-thinking | paid
|
||||
baidu/ernie-4.5-300b-a47b | paid
|
||||
baidu/ernie-4.5-vl-28b-a3b | paid
|
||||
baidu/ernie-4.5-vl-424b-a47b | paid
|
||||
openrouter/bodybuilder | paid
|
||||
bytedance-seed/seed-1.6 | paid
|
||||
bytedance-seed/seed-1.6-flash | paid
|
||||
bytedance-seed/seed-2.0-lite | paid
|
||||
bytedance-seed/seed-2.0-mini | paid
|
||||
bytedance/ui-tars-1.5-7b | paid
|
||||
cohere/command-a | paid
|
||||
cohere/command-r-08-2024 | paid
|
||||
cohere/command-r-plus-08-2024 | paid
|
||||
cohere/command-r7b-12-2024 | paid
|
||||
deepcogito/cogito-v2.1-671b | paid
|
||||
deepseek/deepseek-chat | paid
|
||||
deepseek/deepseek-chat-v3-0324 | paid
|
||||
deepseek/deepseek-chat-v3.1 | paid
|
||||
deepseek/deepseek-v3.1-terminus | paid
|
||||
deepseek/deepseek-v3.2 | paid
|
||||
deepseek/deepseek-v3.2-exp | paid
|
||||
deepseek/deepseek-v3.2-speciale | paid
|
||||
deepseek/deepseek-r1 | paid
|
||||
deepseek/deepseek-r1-0528 | paid
|
||||
deepseek/deepseek-r1-distill-llama-70b | paid
|
||||
deepseek/deepseek-r1-distill-qwen-32b | paid
|
||||
eleutherai/llemma_7b | paid
|
||||
essentialai/rnj-1-instruct | paid
|
||||
openrouter/free | paid
|
||||
alpindale/goliath-120b | paid
|
||||
google/gemini-2.0-flash-001 | paid
|
||||
google/gemini-2.0-flash-lite-001 | paid
|
||||
google/gemini-2.5-flash | paid
|
||||
google/gemini-2.5-flash-lite | paid
|
||||
google/gemini-2.5-flash-lite-preview-09-2025 | paid
|
||||
google/gemini-2.5-pro | paid
|
||||
google/gemini-2.5-pro-preview-05-06 | paid
|
||||
google/gemini-2.5-pro-preview | paid
|
||||
google/gemini-3-flash-preview | paid
|
||||
google/gemini-3-pro-preview | paid
|
||||
google/gemini-3.1-flash-lite-preview | paid
|
||||
google/gemini-3.1-pro-preview | paid
|
||||
google/gemini-3.1-pro-preview-customtools | paid
|
||||
google/gemma-2-27b-it | paid
|
||||
google/gemma-2-9b-it | paid
|
||||
google/gemma-3-12b-it | paid
|
||||
google/gemma-3-12b-it:free | free
|
||||
google/gemma-3-27b-it | paid
|
||||
google/gemma-3-27b-it:free | free
|
||||
google/gemma-3-4b-it | paid
|
||||
google/gemma-3-4b-it:free | free
|
||||
google/gemma-3n-e2b-it:free | free
|
||||
google/gemma-3n-e4b-it | paid
|
||||
google/gemma-3n-e4b-it:free | free
|
||||
google/gemini-2.5-flash-image | paid
|
||||
google/gemini-3.1-flash-image-preview | paid
|
||||
google/gemini-3-pro-image-preview | paid
|
||||
ibm-granite/granite-4.0-h-micro | paid
|
||||
inception/mercury | paid
|
||||
inception/mercury-2 | paid
|
||||
inception/mercury-coder | paid
|
||||
inflection/inflection-3-pi | paid
|
||||
inflection/inflection-3-productivity | paid
|
||||
kwaipilot/kat-coder-pro | paid
|
||||
liquid/lfm-2.2-6b | paid
|
||||
liquid/lfm-2-24b-a2b | paid
|
||||
liquid/lfm2-8b-a1b | paid
|
||||
liquid/lfm-2.5-1.2b-instruct:free | free
|
||||
liquid/lfm-2.5-1.2b-thinking:free | free
|
||||
meta-llama/llama-guard-3-8b | paid
|
||||
anthracite-org/magnum-v4-72b | paid
|
||||
mancer/weaver | paid
|
||||
meituan/longcat-flash-chat | paid
|
||||
meta-llama/llama-3-70b-instruct | paid
|
||||
meta-llama/llama-3-8b-instruct | paid
|
||||
meta-llama/llama-3.1-405b | paid
|
||||
meta-llama/llama-3.1-70b-instruct | paid
|
||||
meta-llama/llama-3.1-8b-instruct | paid
|
||||
meta-llama/llama-3.2-11b-vision-instruct | paid
|
||||
meta-llama/llama-3.2-1b-instruct | paid
|
||||
meta-llama/llama-3.2-3b-instruct | paid
|
||||
meta-llama/llama-3.2-3b-instruct:free | free
|
||||
meta-llama/llama-3.3-70b-instruct | paid
|
||||
meta-llama/llama-3.3-70b-instruct:free | free
|
||||
meta-llama/llama-4-maverick | paid
|
||||
meta-llama/llama-4-scout | paid
|
||||
meta-llama/llama-guard-4-12b | paid
|
||||
microsoft/phi-4 | paid
|
||||
minimax/minimax-m1 | paid
|
||||
minimax/minimax-m2 | paid
|
||||
minimax/minimax-m2-her | paid
|
||||
minimax/minimax-m2.1 | paid
|
||||
minimax/minimax-m2.5 | paid
|
||||
minimax/minimax-m2.5:free | free
|
||||
minimax/minimax-m2.7 | paid
|
||||
minimax/minimax-01 | paid
|
||||
mistralai/mistral-large | paid
|
||||
mistralai/mistral-large-2407 | paid
|
||||
mistralai/mistral-large-2411 | paid
|
||||
mistralai/codestral-2508 | paid
|
||||
mistralai/devstral-2512 | paid
|
||||
mistralai/devstral-medium | paid
|
||||
mistralai/devstral-small | paid
|
||||
mistralai/ministral-14b-2512 | paid
|
||||
mistralai/ministral-3b-2512 | paid
|
||||
mistralai/ministral-8b-2512 | paid
|
||||
mistralai/mistral-7b-instruct-v0.1 | paid
|
||||
mistralai/mistral-large-2512 | paid
|
||||
mistralai/mistral-medium-3 | paid
|
||||
mistralai/mistral-medium-3.1 | paid
|
||||
mistralai/mistral-nemo | paid
|
||||
mistralai/mistral-small-24b-instruct-2501 | paid
|
||||
mistralai/mistral-small-3.1-24b-instruct | paid
|
||||
mistralai/mistral-small-3.1-24b-instruct:free | free
|
||||
mistralai/mistral-small-3.2-24b-instruct | paid
|
||||
mistralai/mistral-small-2603 | paid
|
||||
mistralai/mistral-small-creative | paid
|
||||
mistralai/mixtral-8x22b-instruct | paid
|
||||
mistralai/mixtral-8x7b-instruct | paid
|
||||
mistralai/pixtral-large-2411 | paid
|
||||
mistralai/mistral-saba | paid
|
||||
mistralai/voxtral-small-24b-2507 | paid
|
||||
moonshotai/kimi-k2 | paid
|
||||
moonshotai/kimi-k2-0905 | paid
|
||||
moonshotai/kimi-k2-thinking | paid
|
||||
moonshotai/kimi-k2.5 | paid
|
||||
morph/morph-v3-fast | paid
|
||||
morph/morph-v3-large | paid
|
||||
gryphe/mythomax-l2-13b | paid
|
||||
nex-agi/deepseek-v3.1-nex-n1 | paid
|
||||
nousresearch/hermes-3-llama-3.1-405b | paid
|
||||
nousresearch/hermes-3-llama-3.1-405b:free | free
|
||||
nousresearch/hermes-3-llama-3.1-70b | paid
|
||||
nousresearch/hermes-4-405b | paid
|
||||
nousresearch/hermes-4-70b | paid
|
||||
nousresearch/hermes-2-pro-llama-3-8b | paid
|
||||
nvidia/llama-3.1-nemotron-70b-instruct | paid
|
||||
nvidia/llama-3.3-nemotron-super-49b-v1.5 | paid
|
||||
nvidia/nemotron-3-nano-30b-a3b | paid
|
||||
nvidia/nemotron-3-nano-30b-a3b:free | free
|
||||
nvidia/nemotron-3-super-120b-a12b | paid
|
||||
nvidia/nemotron-3-super-120b-a12b:free | free
|
||||
nvidia/nemotron-nano-12b-v2-vl | paid
|
||||
nvidia/nemotron-nano-12b-v2-vl:free | free
|
||||
nvidia/nemotron-nano-9b-v2 | paid
|
||||
nvidia/nemotron-nano-9b-v2:free | free
|
||||
openai/gpt-audio | paid
|
||||
openai/gpt-audio-mini | paid
|
||||
openai/gpt-3.5-turbo | paid
|
||||
openai/gpt-3.5-turbo-0613 | paid
|
||||
openai/gpt-3.5-turbo-16k | paid
|
||||
openai/gpt-3.5-turbo-instruct | paid
|
||||
openai/gpt-4 | paid
|
||||
openai/gpt-4-0314 | paid
|
||||
openai/gpt-4-turbo | paid
|
||||
openai/gpt-4-1106-preview | paid
|
||||
openai/gpt-4-turbo-preview | paid
|
||||
openai/gpt-4.1 | paid
|
||||
openai/gpt-4.1-mini | paid
|
||||
openai/gpt-4.1-nano | paid
|
||||
openai/gpt-4o | paid
|
||||
openai/gpt-4o-2024-05-13 | paid
|
||||
openai/gpt-4o-2024-08-06 | paid
|
||||
openai/gpt-4o-2024-11-20 | paid
|
||||
openai/gpt-4o:extended | paid
|
||||
openai/gpt-4o-audio-preview | paid
|
||||
openai/gpt-4o-search-preview | paid
|
||||
openai/gpt-4o-mini | paid
|
||||
openai/gpt-4o-mini-2024-07-18 | paid
|
||||
openai/gpt-4o-mini-search-preview | paid
|
||||
openai/gpt-5 | paid
|
||||
openai/gpt-5-chat | paid
|
||||
openai/gpt-5-codex | paid
|
||||
openai/gpt-5-image | paid
|
||||
openai/gpt-5-image-mini | paid
|
||||
openai/gpt-5-mini | paid
|
||||
openai/gpt-5-nano | paid
|
||||
openai/gpt-5-pro | paid
|
||||
openai/gpt-5.1 | paid
|
||||
openai/gpt-5.1-chat | paid
|
||||
openai/gpt-5.1-codex | paid
|
||||
openai/gpt-5.1-codex-max | paid
|
||||
openai/gpt-5.1-codex-mini | paid
|
||||
openai/gpt-5.2 | paid
|
||||
openai/gpt-5.2-chat | paid
|
||||
openai/gpt-5.2-pro | paid
|
||||
openai/gpt-5.2-codex | paid
|
||||
openai/gpt-5.3-chat | paid
|
||||
openai/gpt-5.3-codex | paid
|
||||
openai/gpt-5.4 | paid
|
||||
openai/gpt-5.4-mini | paid
|
||||
openai/gpt-5.4-nano | paid
|
||||
openai/gpt-5.4-pro | paid
|
||||
openai/gpt-oss-120b | paid
|
||||
openai/gpt-oss-120b:free | free
|
||||
openai/gpt-oss-20b | paid
|
||||
openai/gpt-oss-20b:free | free
|
||||
openai/gpt-oss-safeguard-20b | paid
|
||||
openai/o1 | paid
|
||||
openai/o1-pro | paid
|
||||
openai/o3 | paid
|
||||
openai/o3-deep-research | paid
|
||||
openai/o3-mini | paid
|
||||
openai/o3-mini-high | paid
|
||||
openai/o3-pro | paid
|
||||
openai/o4-mini | paid
|
||||
openai/o4-mini-deep-research | paid
|
||||
openai/o4-mini-high | paid
|
||||
perplexity/sonar | paid
|
||||
perplexity/sonar-deep-research | paid
|
||||
perplexity/sonar-pro | paid
|
||||
perplexity/sonar-pro-search | paid
|
||||
perplexity/sonar-reasoning-pro | paid
|
||||
prime-intellect/intellect-3 | paid
|
||||
qwen/qwen-plus-2025-07-28 | paid
|
||||
qwen/qwen-plus-2025-07-28:thinking | paid
|
||||
qwen/qwen-vl-max | paid
|
||||
qwen/qwen-vl-plus | paid
|
||||
qwen/qwen-max | paid
|
||||
qwen/qwen-plus | paid
|
||||
qwen/qwen-turbo | paid
|
||||
qwen/qwen-2.5-7b-instruct | paid
|
||||
qwen/qwen2.5-coder-7b-instruct | paid
|
||||
qwen/qwen2.5-vl-32b-instruct | paid
|
||||
qwen/qwen2.5-vl-72b-instruct | paid
|
||||
qwen/qwen-2.5-vl-7b-instruct | paid
|
||||
qwen/qwen3-14b | paid
|
||||
qwen/qwen3-235b-a22b | paid
|
||||
qwen/qwen3-235b-a22b-2507 | paid
|
||||
qwen/qwen3-235b-a22b-thinking-2507 | paid
|
||||
qwen/qwen3-30b-a3b | paid
|
||||
qwen/qwen3-30b-a3b-instruct-2507 | paid
|
||||
qwen/qwen3-30b-a3b-thinking-2507 | paid
|
||||
qwen/qwen3-32b | paid
|
||||
qwen/qwen3-4b:free | free
|
||||
qwen/qwen3-8b | paid
|
||||
qwen/qwen3-coder-30b-a3b-instruct | paid
|
||||
qwen/qwen3-coder | paid
|
||||
qwen/qwen3-coder:free | free
|
||||
qwen/qwen3-coder-flash | paid
|
||||
qwen/qwen3-coder-next | paid
|
||||
qwen/qwen3-coder-plus | paid
|
||||
qwen/qwen3-max | paid
|
||||
qwen/qwen3-max-thinking | paid
|
||||
qwen/qwen3-next-80b-a3b-instruct | paid
|
||||
qwen/qwen3-next-80b-a3b-instruct:free | free
|
||||
qwen/qwen3-next-80b-a3b-thinking | paid
|
||||
qwen/qwen3-vl-235b-a22b-instruct | paid
|
||||
qwen/qwen3-vl-235b-a22b-thinking | paid
|
||||
qwen/qwen3-vl-30b-a3b-instruct | paid
|
||||
qwen/qwen3-vl-30b-a3b-thinking | paid
|
||||
qwen/qwen3-vl-32b-instruct | paid
|
||||
qwen/qwen3-vl-8b-instruct | paid
|
||||
qwen/qwen3-vl-8b-thinking | paid
|
||||
qwen/qwen3.5-397b-a17b | paid
|
||||
qwen/qwen3.5-plus-02-15 | paid
|
||||
qwen/qwen3.5-122b-a10b | paid
|
||||
qwen/qwen3.5-27b | paid
|
||||
qwen/qwen3.5-35b-a3b | paid
|
||||
qwen/qwen3.5-9b | paid
|
||||
qwen/qwen3.5-flash-02-23 | paid
|
||||
qwen/qwq-32b | paid
|
||||
qwen/qwen-2.5-72b-instruct | paid
|
||||
qwen/qwen-2.5-coder-32b-instruct | paid
|
||||
relace/relace-apply-3 | paid
|
||||
relace/relace-search | paid
|
||||
undi95/remm-slerp-l2-13b | paid
|
||||
sao10k/l3-lunaris-8b | paid
|
||||
sao10k/l3-euryale-70b | paid
|
||||
sao10k/l3.1-70b-hanami-x1 | paid
|
||||
sao10k/l3.1-euryale-70b | paid
|
||||
sao10k/l3.3-euryale-70b | paid
|
||||
stepfun/step-3.5-flash | paid
|
||||
stepfun/step-3.5-flash:free | free
|
||||
switchpoint/router | paid
|
||||
tencent/hunyuan-a13b-instruct | paid
|
||||
thedrummer/cydonia-24b-v4.1 | paid
|
||||
thedrummer/rocinante-12b | paid
|
||||
thedrummer/skyfall-36b-v2 | paid
|
||||
thedrummer/unslopnemo-12b | paid
|
||||
tngtech/deepseek-r1t2-chimera | paid
|
||||
alibaba/tongyi-deepresearch-30b-a3b | paid
|
||||
upstage/solar-pro-3 | paid
|
||||
cognitivecomputations/dolphin-mistral-24b-venice-edition:free | free
|
||||
microsoft/wizardlm-2-8x22b | paid
|
||||
writer/palmyra-x5 | paid
|
||||
x-ai/grok-3 | paid
|
||||
x-ai/grok-3-beta | paid
|
||||
x-ai/grok-3-mini | paid
|
||||
x-ai/grok-3-mini-beta | paid
|
||||
x-ai/grok-4 | paid
|
||||
x-ai/grok-4-fast | paid
|
||||
x-ai/grok-4.1-fast | paid
|
||||
x-ai/grok-4.20-beta | paid
|
||||
x-ai/grok-4.20-multi-agent-beta | paid
|
||||
x-ai/grok-code-fast-1 | paid
|
||||
xiaomi/mimo-v2-flash | paid
|
||||
xiaomi/mimo-v2-omni | paid
|
||||
xiaomi/mimo-v2-pro | paid
|
||||
z-ai/glm-4-32b | paid
|
||||
z-ai/glm-4.5 | paid
|
||||
z-ai/glm-4.5-air | paid
|
||||
z-ai/glm-4.5-air:free | free
|
||||
z-ai/glm-4.5v | paid
|
||||
z-ai/glm-4.6 | paid
|
||||
z-ai/glm-4.6v | paid
|
||||
z-ai/glm-4.7 | paid
|
||||
z-ai/glm-4.7-flash | paid
|
||||
z-ai/glm-5 | paid
|
||||
z-ai/glm-5-turbo | paid
|
||||
[35m[1m[22m[39m
|
||||
[35m[1m OpenAI models:[22m[39m
|
||||
[35m[1m[22m[39m
|
||||
babbage-002
|
||||
chatgpt-image-latest
|
||||
dall-e-2
|
||||
dall-e-3
|
||||
davinci-002
|
||||
gpt-3.5-turbo
|
||||
gpt-3.5-turbo-0125
|
||||
gpt-3.5-turbo-1106
|
||||
gpt-3.5-turbo-16k
|
||||
gpt-3.5-turbo-instruct
|
||||
gpt-3.5-turbo-instruct-0914
|
||||
gpt-4
|
||||
gpt-4-0125-preview
|
||||
gpt-4-0613
|
||||
gpt-4-1106-preview
|
||||
gpt-4-turbo
|
||||
gpt-4-turbo-2024-04-09
|
||||
gpt-4-turbo-preview
|
||||
gpt-4.1
|
||||
gpt-4.1-2025-04-14
|
||||
gpt-4.1-mini
|
||||
gpt-4.1-mini-2025-04-14
|
||||
gpt-4.1-nano
|
||||
gpt-4.1-nano-2025-04-14
|
||||
gpt-4o
|
||||
gpt-4o-2024-05-13
|
||||
gpt-4o-2024-08-06
|
||||
gpt-4o-2024-11-20
|
||||
gpt-4o-audio-preview
|
||||
gpt-4o-audio-preview-2024-12-17
|
||||
gpt-4o-audio-preview-2025-06-03
|
||||
gpt-4o-mini
|
||||
gpt-4o-mini-2024-07-18
|
||||
gpt-4o-mini-audio-preview
|
||||
gpt-4o-mini-audio-preview-2024-12-17
|
||||
gpt-4o-mini-realtime-preview
|
||||
gpt-4o-mini-realtime-preview-2024-12-17
|
||||
gpt-4o-mini-search-preview
|
||||
gpt-4o-mini-search-preview-2025-03-11
|
||||
gpt-4o-mini-transcribe
|
||||
gpt-4o-mini-transcribe-2025-03-20
|
||||
gpt-4o-mini-transcribe-2025-12-15
|
||||
gpt-4o-mini-tts
|
||||
gpt-4o-mini-tts-2025-03-20
|
||||
gpt-4o-mini-tts-2025-12-15
|
||||
gpt-4o-realtime-preview
|
||||
gpt-4o-realtime-preview-2024-12-17
|
||||
gpt-4o-realtime-preview-2025-06-03
|
||||
gpt-4o-search-preview
|
||||
gpt-4o-search-preview-2025-03-11
|
||||
gpt-4o-transcribe
|
||||
gpt-4o-transcribe-diarize
|
||||
gpt-5
|
||||
gpt-5-2025-08-07
|
||||
gpt-5-chat-latest
|
||||
gpt-5-codex
|
||||
gpt-5-mini
|
||||
gpt-5-mini-2025-08-07
|
||||
gpt-5-nano
|
||||
gpt-5-nano-2025-08-07
|
||||
gpt-5-pro
|
||||
gpt-5-pro-2025-10-06
|
||||
gpt-5-search-api
|
||||
gpt-5-search-api-2025-10-14
|
||||
gpt-5.1
|
||||
gpt-5.1-2025-11-13
|
||||
gpt-5.1-chat-latest
|
||||
gpt-5.1-codex
|
||||
gpt-5.1-codex-max
|
||||
gpt-5.1-codex-mini
|
||||
gpt-5.2
|
||||
gpt-5.2-2025-12-11
|
||||
gpt-5.2-chat-latest
|
||||
gpt-5.2-codex
|
||||
gpt-5.2-pro
|
||||
gpt-5.2-pro-2025-12-11
|
||||
gpt-5.3-chat-latest
|
||||
gpt-5.3-codex
|
||||
gpt-5.4
|
||||
gpt-5.4-2026-03-05
|
||||
gpt-5.4-mini
|
||||
gpt-5.4-mini-2026-03-17
|
||||
gpt-5.4-nano
|
||||
gpt-5.4-nano-2026-03-17
|
||||
gpt-5.4-pro
|
||||
gpt-5.4-pro-2026-03-05
|
||||
gpt-audio
|
||||
gpt-audio-1.5
|
||||
gpt-audio-2025-08-28
|
||||
gpt-audio-mini
|
||||
gpt-audio-mini-2025-10-06
|
||||
gpt-audio-mini-2025-12-15
|
||||
gpt-image-1
|
||||
gpt-image-1-mini
|
||||
gpt-image-1.5
|
||||
gpt-realtime
|
||||
gpt-realtime-1.5
|
||||
gpt-realtime-2025-08-28
|
||||
gpt-realtime-mini
|
||||
gpt-realtime-mini-2025-10-06
|
||||
gpt-realtime-mini-2025-12-15
|
||||
o1
|
||||
o1-2024-12-17
|
||||
o1-pro
|
||||
o1-pro-2025-03-19
|
||||
o3
|
||||
o3-2025-04-16
|
||||
o3-mini
|
||||
o3-mini-2025-01-31
|
||||
o4-mini
|
||||
o4-mini-2025-04-16
|
||||
o4-mini-deep-research
|
||||
o4-mini-deep-research-2025-06-26
|
||||
omni-moderation-2024-09-26
|
||||
omni-moderation-latest
|
||||
sora-2
|
||||
sora-2-pro
|
||||
text-embedding-3-large
|
||||
text-embedding-3-small
|
||||
text-embedding-ada-002
|
||||
tts-1
|
||||
tts-1-1106
|
||||
tts-1-hd
|
||||
tts-1-hd-1106
|
||||
whisper-1
|
||||
-----
|
||||
|
||||
[35m[1m[22m[39m
|
||||
[35m[1m Deepseek models:[22m[39m
|
||||
[35m[1m[22m[39m
|
||||
deepseek-chat
|
||||
deepseek-reasoner
|
||||
-----
|
||||
*/
|
||||
model?: string | undefined;
|
||||
/** Router to use: openai, openrouter or deepseek */
|
||||
router?: string;
|
||||
/** Chat completion mode:
|
||||
completion, tools, assistant.
|
||||
[32m[1mcompletion[22m[39m: no support for tools, please use --dst parameter to save the output.
|
||||
[32m[1mtools[22m[39m: allows for tools to be used, eg 'save to ./output.md'. Not all models support this mode.
|
||||
[32m[1mresponses[22m[39m: allows for responses to be used, eg 'save to ./output.md'. Not all models support this mode.
|
||||
[32m[1massistant[22m[39m: : allows documents (PDF, DOCX, ...) to be added but dont support tools. Use --dst to save the output. Supported files :
|
||||
[32m[1mcustom[22m[39m: custom mode
|
||||
*/
|
||||
mode?: "completion" | "tools" | "assistant" | "responses" | "custom";
|
||||
/** Logging level for the application */
|
||||
logLevel?: number;
|
||||
/** Path to profile for variables. Supports environment variables. */
|
||||
profile?: string | undefined;
|
||||
/** Base URL for the API, set via --router or directly */
|
||||
baseURL?: string | undefined;
|
||||
/** Path to JSON configuration file (API keys). Supports environment variables. */
|
||||
config?: string | undefined;
|
||||
/** Create a script */
|
||||
dump?: string | undefined;
|
||||
/** Path to preferences file, eg: location, your email address, gender, etc. Supports environment variables. */
|
||||
preferences?: string;
|
||||
/** Logging directory */
|
||||
logs?: string;
|
||||
/** Enable streaming (verbose LLM output) */
|
||||
stream?: boolean;
|
||||
/** Use alternate tokenizer & instead of $ */
|
||||
alt?: boolean;
|
||||
/** Environment (in profile) */
|
||||
env?: string;
|
||||
variables?: {
|
||||
[x: string]: string;
|
||||
};
|
||||
/** List of filters to apply to the output.
|
||||
Used only in completion mode and a given output file specified with --dst.
|
||||
It unwraps by default any code or data in Markdown.
|
||||
Choices:
|
||||
JSON,JSONUnescape,JSONPretty,AlphaSort,code,JSONParse,trim,markdown
|
||||
*/
|
||||
filters?: (string | ("JSON" | "JSONUnescape" | "JSONPretty" | "AlphaSort" | "code" | "JSONParse" | "trim" | "markdown")[] | string[] | ((...args_0: unknown[]) => unknown)[]);
|
||||
/** JSONPath query to be used to transform input objects */
|
||||
query?: (string | null);
|
||||
/** Dry run - only write out parameters without making API calls */
|
||||
dry?: (boolean | string);
|
||||
/** Format for structured outputs. Can be a Zod schema, a Zod schema string, a JSON schema string, or a path to a JSON file. */
|
||||
format?: (string | any) | undefined;
|
||||
}
|
||||
2
packages/kbot/dist-in/ai-tools/types_kbot.js
Normal file
2
packages/kbot/dist-in/ai-tools/types_kbot.js
Normal file
@ -0,0 +1,2 @@
|
||||
export {};
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXNfa2JvdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9haS10b29scy90eXBlc19rYm90LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiIifQ==
|
||||
20
packages/kbot/dist/package.json
vendored
Normal file
20
packages/kbot/dist/package.json
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "@plastichub/kbot",
|
||||
"version": "1.3.0",
|
||||
"main": "main_node.js",
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"bin": {
|
||||
"kbot": "./main_node.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"node-emoji": "^2.2.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"puppeteer": "^23.11.1"
|
||||
}
|
||||
}
|
||||
0
packages/kbot/src/ai-tools/.gitignore
vendored
Normal file
0
packages/kbot/src/ai-tools/.gitignore
vendored
Normal file
27
packages/kbot/src/ai-tools/commands/index.ts
Normal file
27
packages/kbot/src/ai-tools/commands/index.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import type { Argv } from 'yargs'
|
||||
import { types } from './types.js'
|
||||
import { list, options as listOptions } from './list.js'
|
||||
import { invoke, invokeOptions } from './invoke.js'
|
||||
import { CONFIG_DEFAULT } from '@polymech/commons'
|
||||
import { logger } from '../index.js'
|
||||
|
||||
export const commands = (yargs: Argv) => {
|
||||
return yargs
|
||||
.command('types', 'Generate TypeScript interfaces from Zod schemas', {}, types)
|
||||
.command('list', 'List all available tools and their descriptions', listOptions, list)
|
||||
.command('invoke', 'Invoke a specific tool function', invokeOptions, invoke)
|
||||
.option('env_key', {
|
||||
type: 'string',
|
||||
description: 'Environment configuration key'
|
||||
})
|
||||
.middleware([(argv) => {
|
||||
const config = CONFIG_DEFAULT(argv.env_key) as any;
|
||||
if (!config) {
|
||||
logger.warn('No config found!');
|
||||
return;
|
||||
}
|
||||
return config;
|
||||
}])
|
||||
.strict()
|
||||
.help();
|
||||
};
|
||||
55
packages/kbot/src/ai-tools/commands/invoke.ts
Normal file
55
packages/kbot/src/ai-tools/commands/invoke.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import { tools } from '../lib/tools/tools.js'
|
||||
import { logger } from '../index.js'
|
||||
import { InvokeToolSchema } from '../zod_schemas.js'
|
||||
import { toYargs } from '@polymech/commons/schemas'
|
||||
import { sync as write } from '@polymech/fs/write'
|
||||
import type { Argv } from 'yargs'
|
||||
import * as path from 'path'
|
||||
|
||||
const options = (yargs: Argv) => toYargs(yargs, InvokeToolSchema);
|
||||
|
||||
export const invoke = async (argv: any) => {
|
||||
try {
|
||||
const { tools: toolCategory, function: funcName, target, params, output } = argv;
|
||||
|
||||
// Get tool category
|
||||
const toolSet = tools[toolCategory];
|
||||
if (!toolSet) {
|
||||
logger.error(`Tool category '${toolCategory}' not found`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize tools with target directory
|
||||
const toolList = toolSet(target);
|
||||
|
||||
// Find specific function
|
||||
const tool = toolList.find(t => t.function.name === funcName);
|
||||
if (!tool) {
|
||||
logger.error(`Function '${funcName}' not found in ${toolCategory} tools`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse parameters if provided
|
||||
const parameters = params ? JSON.parse(params) : {};
|
||||
|
||||
// Execute tool function
|
||||
logger.info(`Invoking ${toolCategory}::${funcName}`);
|
||||
const result = await tool.function.function(parameters);
|
||||
|
||||
// Handle output
|
||||
if (output) {
|
||||
const outputPath = path.isAbsolute(output) ? output : path.join(process.cwd(), output);
|
||||
logger.info(`Writing output to ${outputPath}`);
|
||||
write(outputPath, JSON.stringify(result, null, 2));
|
||||
} else {
|
||||
logger.info('Result:', result);
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
logger.error('Error invoking tool:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export { options as invokeOptions };
|
||||
75
packages/kbot/src/ai-tools/commands/list.ts
Normal file
75
packages/kbot/src/ai-tools/commands/list.ts
Normal file
@ -0,0 +1,75 @@
|
||||
import type { Argv } from 'yargs'
|
||||
import { tools } from '../lib/tools/tools.js'
|
||||
import { logger } from '../index.js'
|
||||
import { ListCommandSchema } from '../zod_schemas.js'
|
||||
import { sync as write } from '@polymech/fs/write'
|
||||
import { toYargs } from '@polymech/commons/schemas'
|
||||
|
||||
export const options = (yargs: Argv) => toYargs(yargs, ListCommandSchema)
|
||||
|
||||
interface FSParameters {
|
||||
type: string;
|
||||
properties: Record<string, any>;
|
||||
required: string[];
|
||||
}
|
||||
interface FSDefinition {
|
||||
name: string;
|
||||
description: string;
|
||||
category: string;
|
||||
parameters: FSParameters;
|
||||
}
|
||||
interface FSData {
|
||||
fs: FSDefinition[];
|
||||
}
|
||||
|
||||
export const signature = (definition: FSDefinition): string => {
|
||||
const { properties } = definition.parameters;
|
||||
const requiredKeys = definition.parameters.required || [];
|
||||
const params = Object.entries(properties).map(([key, val]) => {
|
||||
const isRequired = requiredKeys.includes(key);
|
||||
const isOptional = !!val.optional || !isRequired;
|
||||
return isOptional ? `?${key}` : key;
|
||||
});
|
||||
return `(${params.join(", ")})`;
|
||||
}
|
||||
|
||||
export function format(category: string, data: any): string {
|
||||
const lines: string[] = [`## ${category}\n`];
|
||||
data.forEach(definition => {
|
||||
const functionName = definition.name
|
||||
const args = `${signature(definition)}`
|
||||
const summary = definition.description
|
||||
lines.push(`- ${functionName}${args}: ${summary}`)
|
||||
})
|
||||
return lines.join("\n")
|
||||
}
|
||||
|
||||
export const list = async (argv: any, options?: any) => {
|
||||
const getCategorizedTools = (category, options) => {
|
||||
const toolsArray = tools[category](process.cwd(), options);
|
||||
return toolsArray.map(tool => ({
|
||||
name: tool.function.name,
|
||||
description: tool.function.description,
|
||||
category,
|
||||
parameters: tool.function.parameters
|
||||
}));
|
||||
}
|
||||
const toolsList = {
|
||||
email: getCategorizedTools('email', options),
|
||||
search: getCategorizedTools('search', options),
|
||||
interact: getCategorizedTools('email', options),
|
||||
fs: getCategorizedTools('fs', options),
|
||||
npm: getCategorizedTools('npm', options),
|
||||
git: getCategorizedTools('git', options),
|
||||
terminal: getCategorizedTools('terminal', options)
|
||||
}
|
||||
//write(argv.output + '.json', Object.keys(toolsList).map((k,v)=>format(k,v as any)).join('\n') );
|
||||
|
||||
const shortDescription = Object.keys(toolsList).map((value:string) => {
|
||||
return format(value,toolsList[value])
|
||||
}).join('\n\n');
|
||||
if (argv.output) {
|
||||
write(argv.output, JSON.stringify(toolsList, null, 2))
|
||||
write(argv.output + '.md', shortDescription)
|
||||
}
|
||||
}
|
||||
29
packages/kbot/src/ai-tools/commands/types.ts
Normal file
29
packages/kbot/src/ai-tools/commands/types.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { generate_interfaces } from '@polymech/commons'
|
||||
|
||||
import {
|
||||
FileListingOptionsSchema,
|
||||
FileRemovalOptionsSchema,
|
||||
GitCommitSchema,
|
||||
GitRevertSchema,
|
||||
GitSwitchVersionSchema,
|
||||
InvokeToolSchema,
|
||||
ToolListingOptionsSchema,
|
||||
TerminalCommandSchema,
|
||||
ListCommandSchema,
|
||||
NpmRunSchema
|
||||
} from '../zod_schemas.js'
|
||||
|
||||
export const types = async () => {
|
||||
return generate_interfaces([
|
||||
FileListingOptionsSchema,
|
||||
FileRemovalOptionsSchema,
|
||||
GitCommitSchema,
|
||||
GitRevertSchema,
|
||||
GitSwitchVersionSchema,
|
||||
InvokeToolSchema,
|
||||
ToolListingOptionsSchema,
|
||||
TerminalCommandSchema,
|
||||
ListCommandSchema,
|
||||
NpmRunSchema
|
||||
], 'src/zod_types.ts')
|
||||
}
|
||||
11
packages/kbot/src/ai-tools/constants.ts
Normal file
11
packages/kbot/src/ai-tools/constants.ts
Normal file
@ -0,0 +1,11 @@
|
||||
export const LOGGER_NAME = 'llm-tools-cli'
|
||||
|
||||
export const EXCLUDE_GLOB = [
|
||||
"**/node_modules/**",
|
||||
"**/dist/**",
|
||||
"**/build/**",
|
||||
"**/coverage/**",
|
||||
"*.log",
|
||||
".kbot",
|
||||
".git"
|
||||
]
|
||||
186
packages/kbot/src/ai-tools/index.ts
Normal file
186
packages/kbot/src/ai-tools/index.ts
Normal file
@ -0,0 +1,186 @@
|
||||
import * as path from 'path'
|
||||
import { z } from 'zod'
|
||||
|
||||
import { ISettingsParam, Logger } from "tslog"
|
||||
|
||||
import * as winston from 'winston'
|
||||
import TransportStream from 'winston-transport'
|
||||
import { SeqTransport} from '@datalust/winston-seq'
|
||||
import { createStream } from "rotating-file-stream"
|
||||
|
||||
import { CONFIG_DEFAULT } from '@polymech/commons'
|
||||
import { sync as read } from '@polymech/fs/read'
|
||||
import { sync as write } from '@polymech/fs/write'
|
||||
import { sync as exists } from '@polymech/fs/exists'
|
||||
|
||||
import { IKBotTask } from './types.js'
|
||||
|
||||
export let logger: Logger<unknown> = createLogger('osr-ai-tools')
|
||||
export const TLogLevelNameSchema = z.enum(["silly", "trace", "debug", "info", "warn", "error", "fatal"])
|
||||
export type LogLevel = z.infer<typeof TLogLevelNameSchema>
|
||||
export enum LogLevelEx {
|
||||
silly,
|
||||
trace,
|
||||
debug,
|
||||
info,
|
||||
warn,
|
||||
error,
|
||||
fatal
|
||||
}
|
||||
export enum ELogTargets {
|
||||
Console = 1 << 0,
|
||||
FileText = 1 << 1,
|
||||
FileJson = 1 << 2,
|
||||
Seq = 1 << 3
|
||||
}
|
||||
export function createLogger(name: string, options?: ISettingsParam<any>) {
|
||||
return new Logger<unknown>({
|
||||
name,
|
||||
type: 'pretty',
|
||||
...options,
|
||||
})
|
||||
}
|
||||
export const defaultLogger = createLogger('DefaultLogger', {
|
||||
minLevel: LogLevelEx.info
|
||||
})
|
||||
|
||||
class JsonArrayFileTransport extends TransportStream {
|
||||
filename: string;
|
||||
constructor(opts) {
|
||||
super(opts);
|
||||
opts.filename = opts.filename
|
||||
this.filename = opts.filename || 'logs.json';
|
||||
setImmediate(() => this.emit('opened'))
|
||||
}
|
||||
log(info: any, next: () => void): void {
|
||||
setImmediate(() => this.emit('logged', info))
|
||||
const { level, message, exception, stack, ...props } = info;
|
||||
const fileExists = exists(this.filename)
|
||||
const existingLogs = fileExists
|
||||
? read(this.filename, 'json') as []
|
||||
: [];
|
||||
|
||||
const entry = {
|
||||
level: info.level,
|
||||
message: info.message,
|
||||
timestamp: new Date().toISOString(),
|
||||
...info
|
||||
};
|
||||
existingLogs.push(entry)
|
||||
write(this.filename, existingLogs)
|
||||
next()
|
||||
}
|
||||
close(): void {
|
||||
setImmediate(() => this.emit('closed'))
|
||||
}
|
||||
flush(): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve(true)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
class TSLogTransport extends TransportStream {
|
||||
constructor(opts) {
|
||||
super(opts);
|
||||
setImmediate(() => this.emit('opened'))
|
||||
}
|
||||
log(info: any, next: () => void): void {
|
||||
setImmediate(() => this.emit('logged', info))
|
||||
const { level, message, exception, stack, ...props } = info;
|
||||
defaultLogger.info(info)
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
export const winstonLogger = (name: string, file: string, targets: ELogTargets = ELogTargets.Console | ELogTargets.FileJson) => {
|
||||
const logger = winston.createLogger({
|
||||
defaultMeta: { service: name },
|
||||
level: 'debug',
|
||||
transports: []
|
||||
})
|
||||
if (targets & ELogTargets.Console) {
|
||||
//logger.add(new TSLogTransport({}))
|
||||
logger.add(new winston.transports.Console({
|
||||
format: winston.format.combine(
|
||||
winston.format.timestamp({ format: 'MM/DD/YYYY hh:mm:ss.SSS' }),
|
||||
///winston.format.json(),
|
||||
winston.format.colorize(),
|
||||
winston.format.printf(info => {
|
||||
let message = null
|
||||
try {
|
||||
message = JSON.stringify(info.message)
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
return `[${info.level}] [${name}] | message: ${message.substring(0, 200)}`
|
||||
}))
|
||||
}))
|
||||
}
|
||||
|
||||
if (targets & ELogTargets.FileText) {
|
||||
logger.add(new winston.transports.File({
|
||||
format: winston.format.combine(
|
||||
winston.format.timestamp(),
|
||||
winston.format.timestamp({ format: 'MM/DD/YYYY hh:mm:ss.SSS' }),
|
||||
winston.format.json(),
|
||||
winston.format.printf(info => {
|
||||
return JSON.stringify(info, null, 2);
|
||||
})),
|
||||
dirname: path.parse(file).dir,
|
||||
filename: path.parse(file).base
|
||||
}))
|
||||
}
|
||||
|
||||
if (targets & ELogTargets.FileJson) {
|
||||
logger.add(new JsonArrayFileTransport({
|
||||
filename: file
|
||||
}))
|
||||
}
|
||||
|
||||
if (targets & ELogTargets.Seq) {
|
||||
const config = CONFIG_DEFAULT() as any
|
||||
if (config.seq) {
|
||||
logger.add(new SeqTransport({
|
||||
...config.seq,
|
||||
onError: (e => { })
|
||||
}))
|
||||
}
|
||||
}
|
||||
return logger
|
||||
}
|
||||
|
||||
export const createFileLogger = (logger: Logger<unknown>, level: number, file: string): Logger<unknown> => {
|
||||
const rfs = createStream(file,
|
||||
{
|
||||
size: "10M", // rotate every 10 MegaBytes written
|
||||
interval: "1d", // rotate daily
|
||||
compress: "gzip", // compress rotated files
|
||||
});
|
||||
|
||||
const log = new Logger({
|
||||
type: "json",
|
||||
attachedTransports: [
|
||||
(logObj) => {
|
||||
rfs.write(JSON.stringify(logObj) + "\n");
|
||||
},
|
||||
],
|
||||
});
|
||||
return log
|
||||
}
|
||||
|
||||
export const toolLoggerTS = (name, options: IKBotTask) => {
|
||||
let log = createLogger(name)
|
||||
//log.settings.minLevel = options.logLevel
|
||||
log = createFileLogger(log,options.logLevel, path.join(options.logs,`tools-${name}.json`))
|
||||
return log
|
||||
}
|
||||
|
||||
export const toolLogger = (name, options: IKBotTask = { logs: process.cwd() } as IKBotTask ) => {
|
||||
const logPath = path.resolve(path.join(options.logs || './',`tools-${name}.json`))
|
||||
const log = winstonLogger(name, logPath, ELogTargets.Console)
|
||||
return log
|
||||
}
|
||||
|
||||
export * from './types.js'
|
||||
export * from './types_kbot.js'
|
||||
55
packages/kbot/src/ai-tools/invoke.ts
Normal file
55
packages/kbot/src/ai-tools/invoke.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import { tools } from '../lib/tools/tools.js'
|
||||
import { logger } from '../index.js'
|
||||
import { InvokeToolSchema } from '../zod_schemas.js'
|
||||
import { toYargs } from '@polymech/commons/schemas'
|
||||
import { sync as write } from '@polymech/fs/write'
|
||||
import type { Argv } from 'yargs'
|
||||
import * as path from 'path'
|
||||
|
||||
const options = (yargs: Argv) => toYargs(yargs, InvokeToolSchema);
|
||||
|
||||
export const invoke = async (argv: any) => {
|
||||
try {
|
||||
const { tools: toolCategory, function: funcName, target, params, output } = argv;
|
||||
|
||||
// Get tool category
|
||||
const toolSet = tools[toolCategory];
|
||||
if (!toolSet) {
|
||||
logger.error(`Tool category '${toolCategory}' not found`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize tools with target directory
|
||||
const toolList = toolSet(target);
|
||||
|
||||
// Find specific function
|
||||
const tool = toolList.find(t => t.function.name === funcName);
|
||||
if (!tool) {
|
||||
logger.error(`Function '${funcName}' not found in ${toolCategory} tools`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse parameters if provided
|
||||
const parameters = params ? JSON.parse(params) : {};
|
||||
|
||||
// Execute tool function
|
||||
logger.info(`Invoking ${toolCategory}::${funcName}`);
|
||||
const result = await tool.function.function(parameters);
|
||||
|
||||
// Handle output
|
||||
if (output) {
|
||||
const outputPath = path.isAbsolute(output) ? output : path.join(process.cwd(), output);
|
||||
logger.info(`Writing output to ${outputPath}`);
|
||||
write(outputPath, JSON.stringify(result, null, 2));
|
||||
} else {
|
||||
logger.info('Result:', result);
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
logger.error('Error invoking tool:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export { options as invokeOptions };
|
||||
435
packages/kbot/src/ai-tools/lib/tools/fs.ts
Normal file
435
packages/kbot/src/ai-tools/lib/tools/fs.ts
Normal file
@ -0,0 +1,435 @@
|
||||
import * as path from 'path'
|
||||
import { RunnableToolFunction } from 'openai/lib/RunnableFunction'
|
||||
import { sync as rm } from '@polymech/fs/remove'
|
||||
import { isString } from '@polymech/core/primitives'
|
||||
import { sync as write } from '@polymech/fs/write'
|
||||
import { sync as read } from '@polymech/fs/read'
|
||||
import { sync as rename } from '@polymech/fs/rename'
|
||||
import { sync as exists } from '@polymech/fs/exists'
|
||||
import { sanitize } from "@polymech/fs/utils"
|
||||
import { filesEx } from '@polymech/commons'
|
||||
|
||||
import { toolLogger } from '../../index.js'
|
||||
import { IKBotTask } from '../../types.js'
|
||||
import { EXCLUDE_GLOB } from '../../constants.js'
|
||||
|
||||
import { glob } from 'glob'
|
||||
|
||||
const isBase64 = (str: string): boolean => {
|
||||
// 1. Quick checks for length & allowed characters:
|
||||
// - Must be multiple of 4 in length
|
||||
// - Must match Base64 charset (A-Z, a-z, 0-9, +, /) plus optional "=" padding
|
||||
if (!str || str.length % 4 !== 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const base64Regex = /^[A-Za-z0-9+/]+={0,2}$/;
|
||||
if (!base64Regex.test(str)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. Attempt decode–re-encode to confirm validity:
|
||||
try {
|
||||
const decoded = atob(str); // Decode from Base64
|
||||
const reencoded = btoa(decoded); // Re-encode to Base64
|
||||
|
||||
// Compare the re-encoded string to original
|
||||
return reencoded === str;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export const decode_base64 = (base64: string): string => {
|
||||
try {
|
||||
if(!isBase64(base64)) {
|
||||
return base64
|
||||
}
|
||||
return Buffer.from(base64, 'base64').toString('utf-8');
|
||||
} catch (error) {
|
||||
throw new Error('Failed to decode base64 string');
|
||||
}
|
||||
};
|
||||
|
||||
// Helper function for smart Base64 decoding
|
||||
const decodeContentSmart = (content: string, logger: any, identifier: string): string => {
|
||||
if (!content || typeof content !== 'string') {
|
||||
return content; // Return original content if null, undefined, or not a string
|
||||
}
|
||||
|
||||
const lines = content.split(/\r?\n/);
|
||||
const processedLines = lines.map(line => {
|
||||
const trimmedLine = line.trim();
|
||||
if (!trimmedLine) {
|
||||
return ''; // Preserve empty lines between potential blocks but decode the blocks themselves
|
||||
}
|
||||
|
||||
try {
|
||||
// Attempt to decode Base64
|
||||
const decodedLine = Buffer.from(trimmedLine, 'base64').toString('utf-8');
|
||||
// Validate if it was actually Base64 by re-encoding
|
||||
const reEncodedLine = Buffer.from(decodedLine, 'utf-8').toString('base64');
|
||||
|
||||
// Revised Validation Check:
|
||||
// Compare original trimmed line with re-encoded line.
|
||||
// Allow for potential padding differences by checking both exact match and no-pad match.
|
||||
const originalNoPad = trimmedLine.replace(/={1,2}$/, '');
|
||||
const reEncodedNoPad = reEncodedLine.replace(/={1,2}$/, '');
|
||||
|
||||
if (reEncodedLine === trimmedLine || reEncodedNoPad === originalNoPad) {
|
||||
logger.debug(`Successfully decoded Base64 line for ${identifier}`);
|
||||
return decodedLine;
|
||||
}
|
||||
// If validation fails, treat as plain text
|
||||
logger.debug(`Re-encoding mismatch for ${identifier}. Original: '${trimmedLine}', Re-encoded: '${reEncodedLine}', using original trimmed line.`);
|
||||
return trimmedLine;
|
||||
} catch (decodeError) {
|
||||
// If decoding throws an error, assume it's plain text
|
||||
// Use debug level as this is expected for non-base64 lines
|
||||
logger.debug(`Base64 decoding failed for line in ${identifier}, assuming plain text. Line: ${trimmedLine}`);
|
||||
return trimmedLine; // Return original trimmed line
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Join the processed lines back together
|
||||
return processedLines.join('\n');
|
||||
};
|
||||
|
||||
export const tools = (target: string, options: IKBotTask): Array<any> => {
|
||||
const logger = toolLogger('fs', options)
|
||||
|
||||
return [
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'list_files',
|
||||
description: 'List all files in a directory',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
directory: { type: 'string' },
|
||||
pattern: { type: 'string', optional: true }
|
||||
},
|
||||
required: ['directory']
|
||||
},
|
||||
function: async (params: any) => {
|
||||
try {
|
||||
const directory = path.join(target, sanitize(params.directory));
|
||||
if (!exists(directory)) {
|
||||
logger.debug(`Tool::ListFiles Directory ${directory} does not exist`);
|
||||
return []
|
||||
}
|
||||
let pattern = params.pattern || '**/*';
|
||||
logger.debug(`Tool::ListFiles Listing files in ${directory} with pattern ${pattern}`);
|
||||
pattern = [
|
||||
...EXCLUDE_GLOB,
|
||||
pattern
|
||||
]
|
||||
const ret = await glob(pattern, {
|
||||
cwd: directory,
|
||||
absolute: false,
|
||||
ignore: EXCLUDE_GLOB
|
||||
});
|
||||
return ret
|
||||
} catch (error) {
|
||||
logger.error('Error listing files', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
parse: JSON.parse
|
||||
}
|
||||
} as RunnableToolFunction<any>,
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'read_files',
|
||||
description: 'Reads files in a directory with a given pattern',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
directory: { type: 'string' },
|
||||
pattern: { type: 'string', optional: true }
|
||||
},
|
||||
required: ['directory']
|
||||
},
|
||||
function: async (params: any) => {
|
||||
try {
|
||||
const pattern = params.pattern || '**/*';
|
||||
let entries = filesEx(target, pattern);
|
||||
let ret = entries.map((entry) => {
|
||||
try {
|
||||
let content = read(entry);
|
||||
return {
|
||||
path: path.relative(target, entry).replace(/\\/g, '/'),
|
||||
content: content.toString()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Error reading file ${entry}:`, error)
|
||||
return null
|
||||
}
|
||||
})
|
||||
ret = ret.filter((entry) => (entry !== null && entry.content))
|
||||
logger.debug(`Tool::ReadFiles Reading files in ${target} with pattern ${pattern} : ${ret.length} files`, ret.map((entry) => entry.path));
|
||||
return ret
|
||||
} catch (error) {
|
||||
logger.error('Error listing files', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
parse: JSON.parse
|
||||
}
|
||||
} as RunnableToolFunction<any>,
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'remove_file',
|
||||
description: 'Remove a file at given path',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
path: { type: 'string' }
|
||||
},
|
||||
required: ['path']
|
||||
},
|
||||
function: async (params: any) => {
|
||||
try {
|
||||
const filePath = path.join(target, sanitize(params.path));
|
||||
logger.debug(`Tool::RemoveFile Removing file ${filePath}`);
|
||||
rm(filePath);
|
||||
return true;
|
||||
} catch (error) {
|
||||
logger.error('Error removing file', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
parse: JSON.parse
|
||||
}
|
||||
} as RunnableToolFunction<any>,
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'rename_file',
|
||||
description: 'Rename or move a file or directory',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
src: { type: 'string' },
|
||||
dst: { type: 'string' }
|
||||
},
|
||||
required: ['path']
|
||||
},
|
||||
function: async (params: any) => {
|
||||
try {
|
||||
const src = path.join(target, sanitize(params.src))
|
||||
const dst = path.join(target, sanitize(params.dst))
|
||||
logger.debug(`Tool::Rename file ${src} to ${dst}`)
|
||||
rename(src, dst)
|
||||
rm(src)
|
||||
return true
|
||||
} catch (error) {
|
||||
logger.error('Error removing file', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
parse: JSON.parse
|
||||
}
|
||||
} as RunnableToolFunction<any>,
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: "modify_project_files",
|
||||
description: "Create or modify existing project files in one shot, preferably used for creating project structure)",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
files: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "object",
|
||||
properties: {
|
||||
path: { type: "string" },
|
||||
content: { type: "string", description: "new file content (Part of JSON payload)" }
|
||||
},
|
||||
required: ["path", "content"]
|
||||
}
|
||||
}
|
||||
},
|
||||
required: ["files"],
|
||||
},
|
||||
function: async (ret) => {
|
||||
try {
|
||||
if (!target) {
|
||||
logger.error(`Tool::FS:modify_project_files : Root path required`)
|
||||
return
|
||||
}
|
||||
let { files } = ret as any
|
||||
if (isString(files)) {
|
||||
try {
|
||||
files = JSON.parse(files)
|
||||
} catch (error: any) {
|
||||
logger.error(`Tool::modify_project_files : Structure Error parsing files`, error, ret)
|
||||
// Consider writing the raw input for debugging if JSON parsing fails
|
||||
// write(path.join(target, 'tools-output-error.json'), files)
|
||||
return error.message
|
||||
}
|
||||
}
|
||||
for (const file of files) {
|
||||
const sanitizedPath = sanitize(file.path);
|
||||
const filePath = path.join(target, sanitizedPath);
|
||||
logger.debug(`Tool:modify_project_files writing file ${filePath}`)
|
||||
try {
|
||||
// const contentToWrite = decodeContentSmart(file.content, logger, sanitizedPath);
|
||||
try {
|
||||
await write(filePath, file.content)
|
||||
} catch (writeError) {
|
||||
logger.error(`Tool:modify_project_files Error writing file ${filePath}`, writeError)
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Tool:modify_project_files Error processing file content for ${filePath}`, error)
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Error creating project structure`, error)
|
||||
}
|
||||
},
|
||||
|
||||
parse: JSON.parse,
|
||||
},
|
||||
} as RunnableToolFunction<{ id: string }>,
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: "write_file",
|
||||
description: "Writes to a file, given a path and content (Part of JSON payload). No directory or file exists check needed!",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
file: {
|
||||
type: "object",
|
||||
properties: {
|
||||
path: { type: "string" },
|
||||
content: { type: "string", description: "new file content (Part of JSON payload)" }
|
||||
}
|
||||
}
|
||||
},
|
||||
required: ["file"],
|
||||
},
|
||||
function: async (params) => {
|
||||
let fileInfo;
|
||||
try {
|
||||
if (isString(params)) {
|
||||
try {
|
||||
params = JSON.parse(params)
|
||||
} catch (error: any) {
|
||||
logger.error(`Tool::write_file : Structure Error parsing JSON`, error, params)
|
||||
return error.message
|
||||
}
|
||||
}
|
||||
|
||||
fileInfo = (params as any).file; // Keep fileInfo accessible
|
||||
|
||||
if (!target || !fileInfo || !fileInfo.path || typeof fileInfo.content === 'undefined') {
|
||||
logger.error(`Tool::write_file : Path/Target/Content are required`, fileInfo)
|
||||
return false; // Indicate failure
|
||||
}
|
||||
|
||||
const sanitizedPath = sanitize(fileInfo.path);
|
||||
const filePath = path.join(target, sanitizedPath)
|
||||
logger.debug(`Tool::write_file Writing file ${filePath}`)
|
||||
try {
|
||||
// Use the smart decoding helper function
|
||||
// const contentToWrite = decodeContentSmart(fileInfo.content, logger, sanitizedPath);
|
||||
await write(filePath, fileInfo.content)
|
||||
return true
|
||||
} catch (error) {
|
||||
// Log error related to processing or writing the file
|
||||
logger.error(`Tool:write_file Error processing or writing file ${sanitizedPath}`, error)
|
||||
return false // Indicate failure
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Tool:write_file Error writing file ${fileInfo?.path ? sanitize(fileInfo.path) : 'unknown'}`, error)
|
||||
return false // Indicate failure
|
||||
}
|
||||
},
|
||||
parse: JSON.parse,
|
||||
},
|
||||
} as RunnableToolFunction<{ id: string }>,
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: "file_exists",
|
||||
description: "check if a file or folder exists",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
file: {
|
||||
type: "object",
|
||||
properties: {
|
||||
path: { type: "string" }
|
||||
}
|
||||
}
|
||||
},
|
||||
required: ["file"],
|
||||
},
|
||||
function: async (ret) => {
|
||||
try {
|
||||
if (isString(ret)) {
|
||||
try {
|
||||
ret = JSON.parse(ret)
|
||||
} catch (error: any) {
|
||||
logger.error(`Tool::file_exists : Structure Error parsing files`, error, ret)
|
||||
return error.message
|
||||
}
|
||||
}
|
||||
const { file } = ret as any
|
||||
if (!target || !file.path) {
|
||||
logger.error(`Tool::file_exists : Path is required`, ret)
|
||||
return
|
||||
}
|
||||
const sanitizedPath = sanitize(file.path);
|
||||
const filePath = path.join(target, sanitizedPath)
|
||||
const res = exists(filePath)
|
||||
logger.debug(`Tool::file_exists ${filePath} exists: ${res}`)
|
||||
return res ? true : false
|
||||
} catch (error) {
|
||||
logger.error(`Tool:file_exists error`, error)
|
||||
return false
|
||||
}
|
||||
},
|
||||
parse: JSON.parse,
|
||||
},
|
||||
} as RunnableToolFunction<{ id: string }>,
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: "read_file",
|
||||
description: "read a file, at given a path",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
file: {
|
||||
type: "object",
|
||||
properties: {
|
||||
path: { type: "string" }
|
||||
}
|
||||
}
|
||||
},
|
||||
required: ["file"],
|
||||
},
|
||||
function: async (ret) => {
|
||||
try {
|
||||
const { file } = ret as any
|
||||
const sanitizedPath = sanitize(file.path);
|
||||
const filePath = path.join(target, sanitizedPath)
|
||||
logger.debug(`Tool::ReadFile Reading file ${filePath}`)
|
||||
return read(filePath, 'string')
|
||||
} catch (error) {
|
||||
logger.error(`Error reading file`, error)
|
||||
}
|
||||
},
|
||||
parse: JSON.parse
|
||||
}
|
||||
} as RunnableToolFunction<{ id: string }>
|
||||
]
|
||||
};
|
||||
157
packages/kbot/src/ai-tools/lib/tools/git.ts
Normal file
157
packages/kbot/src/ai-tools/lib/tools/git.ts
Normal file
@ -0,0 +1,157 @@
|
||||
import * as path from 'path'
|
||||
import { RunnableToolFunction } from 'openai/lib/RunnableFunction'
|
||||
|
||||
import { simpleGit } from 'simple-git'
|
||||
|
||||
import { sync as exists } from '@polymech/fs/exists'
|
||||
import { substitute } from '@polymech/commons'
|
||||
|
||||
import { logger } from '../../index.js'
|
||||
import { toolLogger } from '../../index.js'
|
||||
import { IKBotTask } from '../../types.js'
|
||||
|
||||
import { findUpSync } from 'find-up'
|
||||
|
||||
const commitFiles = async (filePaths: string[], commitMessage: string, targetDirectory: string, variables: Record<string, string> = {}) => {
|
||||
try {
|
||||
if (!filePaths || !filePaths.length) {
|
||||
logger.warn(`No files to commit`)
|
||||
return
|
||||
}
|
||||
if (!exists(path.join(targetDirectory, '.git'))) {
|
||||
try {
|
||||
logger.info(`Initializing repository at ${targetDirectory}`)
|
||||
await initRepository(targetDirectory)
|
||||
} catch (e: any) {
|
||||
logger.error(`Error initializing repository at ${targetDirectory} `, e.message, filePaths)
|
||||
}
|
||||
}
|
||||
|
||||
const git: any = simpleGit(targetDirectory);
|
||||
try {
|
||||
await git.add(filePaths);
|
||||
} catch (e: any) {
|
||||
logger.error('Error adding files:', e.message, filePaths);
|
||||
}
|
||||
await git.commit(commitMessage)
|
||||
try {
|
||||
await git.raw(['branch', '-M', 'master']);
|
||||
const repo = substitute(false, "${GIT_REPO}/${GIT_USER}/${REPO_NAME}.git", {
|
||||
REPO_NAME: path.basename(targetDirectory),
|
||||
...variables
|
||||
})
|
||||
await git.raw(['remote', 'add', 'origin', repo])
|
||||
await git.push(['--set-upstream', 'origin', 'master'])
|
||||
return true
|
||||
} catch (e: any) {
|
||||
if (e.message.includes('remote origin already exists')) {
|
||||
await git.push(['--set-upstream', 'origin', 'master']);
|
||||
} else {
|
||||
logger.error('Tools::GIT : Error pushing files:', e.message, filePaths);
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
logger.error('Error committing files:', error.message, filePaths);
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const initRepository = async (targetDirectory: string, variables: Record<string, string> = {}): Promise<boolean> => {
|
||||
try {
|
||||
const git: any = simpleGit(targetDirectory);
|
||||
if (!exists(path.join(targetDirectory, '.git'))) {
|
||||
await git.init();
|
||||
logger.info('Git repository initialized successfully!');
|
||||
return true;
|
||||
}
|
||||
logger.info('Git repository already exists');
|
||||
return false
|
||||
} catch (error: any) {
|
||||
logger.error('Error initializing git repository:', error.message);
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export const tools = (target: string, options: IKBotTask): Array<any> => {
|
||||
const logger = toolLogger('git', options)
|
||||
if (!target) {
|
||||
logger.warn(`Tools:GIT : Target is required`)
|
||||
return []
|
||||
}
|
||||
if (!exists(target)) {
|
||||
logger.warn(`Tools:GIT : Project path doesnt exists ${target}`)
|
||||
return []
|
||||
}
|
||||
return [
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: "init_repository",
|
||||
description: "Initialize a new git repository",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {},
|
||||
required: []
|
||||
},
|
||||
function: async (params) => {
|
||||
logger.info(`Tool::init_repository Init Repository in ${target}`)
|
||||
const gitDir = findUpSync('.git',{ type: 'directory', cwd: target})
|
||||
if(gitDir && exists(gitDir)){
|
||||
logger.info(`Repository already exists at ${gitDir}`)
|
||||
return true
|
||||
}
|
||||
try {
|
||||
const ret = await initRepository(target, options.variables)
|
||||
return true
|
||||
} catch (error) {
|
||||
logger.error(`Error initializing repository`, error)
|
||||
return false;
|
||||
}
|
||||
},
|
||||
parse: JSON.parse,
|
||||
}
|
||||
} as RunnableToolFunction<{ id: string }>,
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: "commit_files_git",
|
||||
description: "Commit files using git",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
files: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string"
|
||||
}
|
||||
},
|
||||
message: {
|
||||
type: "string"
|
||||
}
|
||||
},
|
||||
required: ["files"],
|
||||
},
|
||||
function: async (ret) => {
|
||||
debugger
|
||||
try {
|
||||
const { files, message } = ret as any
|
||||
logger.info(`Tool::GIT Commit files ${files} in ${target}`)
|
||||
if (!target) {
|
||||
logger.error(`Tool::Git Commit : Target is required`)
|
||||
return
|
||||
}
|
||||
if (!exists(target)) {
|
||||
logger.error(`Project doesnt path exists ${target}`)
|
||||
return
|
||||
}
|
||||
await commitFiles(files, message, target, options.variables)
|
||||
} catch (error: any) {
|
||||
logger.error(`Error committing dependencies : ${error.message}`)
|
||||
}
|
||||
return true
|
||||
},
|
||||
parse: JSON.parse,
|
||||
},
|
||||
} as RunnableToolFunction<{ id: string }>
|
||||
]
|
||||
}
|
||||
30
packages/kbot/src/ai-tools/lib/tools/index.ts
Normal file
30
packages/kbot/src/ai-tools/lib/tools/index.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { RunnableToolFunctionWithParse } from 'openai/lib/RunnableFunction'
|
||||
import { JSONSchema } from 'openai/lib/jsonschema'
|
||||
import { ZodSchema, z } from 'zod'
|
||||
|
||||
// see https://github.com/openai/openai-node/blob/master/examples/tool-call-helpers-zod.ts
|
||||
export const zodFunction =<T extends object>({
|
||||
function: fn,
|
||||
schema,
|
||||
description = '',
|
||||
name,
|
||||
}: {
|
||||
function: (args: T) => Promise<object>
|
||||
schema: ZodSchema<T>
|
||||
description?: string
|
||||
name?: string
|
||||
}): RunnableToolFunctionWithParse<T> => {
|
||||
return {
|
||||
type: 'function',
|
||||
function: {
|
||||
function: fn,
|
||||
name: name ?? fn.name,
|
||||
description: description,
|
||||
parameters: z.toJSONSchema(schema) as JSONSchema,
|
||||
parse(input: string): T {
|
||||
const obj = JSON.parse(input)
|
||||
return schema.parse(obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
89
packages/kbot/src/ai-tools/lib/tools/interact.ts
Normal file
89
packages/kbot/src/ai-tools/lib/tools/interact.ts
Normal file
@ -0,0 +1,89 @@
|
||||
import * as path from 'path'
|
||||
import { RunnableToolFunction } from 'openai/lib/RunnableFunction'
|
||||
import { toolLogger } from '../../index.js'
|
||||
import { IKBotTask } from '../../types.js'
|
||||
import { input, select } from '@inquirer/prompts'
|
||||
export const tools = (target: string, options: IKBotTask): Array<any> => {
|
||||
const logger = toolLogger('interact', options)
|
||||
return [
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'ask_question',
|
||||
description: 'Ask user a simple question and get response',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
question: {
|
||||
type: 'string',
|
||||
description: 'Question to ask the user'
|
||||
},
|
||||
default: {
|
||||
type: 'string',
|
||||
description: 'Default answer',
|
||||
optional: true
|
||||
}
|
||||
},
|
||||
required: ['question']
|
||||
},
|
||||
function: async (params: any) => {
|
||||
try {
|
||||
const answer = await input(
|
||||
{
|
||||
message: params.question,
|
||||
default: params.default
|
||||
}
|
||||
)
|
||||
return { response: answer }
|
||||
} catch (error: any) {
|
||||
logger.error('Error asking question:', error.message);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
parse: JSON.parse
|
||||
}
|
||||
} as RunnableToolFunction<any>,
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'choose_option',
|
||||
description: 'Ask user to choose from multiple options',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
message: {
|
||||
type: 'string',
|
||||
description: 'Message to show the user'
|
||||
},
|
||||
choices: {
|
||||
type: 'array',
|
||||
items: { type: 'string' },
|
||||
description: 'List of choices'
|
||||
},
|
||||
multiple: {
|
||||
type: 'boolean',
|
||||
description: 'Allow multiple selections',
|
||||
optional: true
|
||||
}
|
||||
},
|
||||
required: ['message', 'choices']
|
||||
},
|
||||
function: async (params: any) => {
|
||||
try {
|
||||
const answer = await select(
|
||||
{
|
||||
message: params.message,
|
||||
choices: params.choices
|
||||
}
|
||||
);
|
||||
return { response: answer }
|
||||
} catch (error: any) {
|
||||
logger.error('Error in choice selection:', error.message);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
parse: JSON.parse
|
||||
}
|
||||
} as RunnableToolFunction<any>
|
||||
]
|
||||
}
|
||||
21
packages/kbot/src/ai-tools/lib/tools/keyv.ts
Normal file
21
packages/kbot/src/ai-tools/lib/tools/keyv.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import * as path from 'path'
|
||||
import Keyv from 'keyv'
|
||||
import { KeyvFile } from 'keyv-file'
|
||||
|
||||
import { resolve } from '@polymech/commons'
|
||||
|
||||
export const store = (storePath: string, ns: string = 'ns-unknown', opts: any = {}) => {
|
||||
const keyvFile = new KeyvFile({
|
||||
filename: path.resolve(resolve(storePath)),
|
||||
writeDelay: 100, // ms
|
||||
})
|
||||
return new Keyv({ store: keyvFile, namespace: ns, ...opts })
|
||||
}
|
||||
export const get = async (key: string, storePath: string, ns: string = 'ns-unknown', opts: any = {}) => {
|
||||
const keyv = store(storePath, ns, opts)
|
||||
return await keyv.get(key)
|
||||
}
|
||||
export const set = async (key: string, value: any, storePath: string, ns: string = 'ns-unknown', opts: any = {}) => {
|
||||
const keyv = store(storePath, ns, opts)
|
||||
return await keyv.set(key, value)
|
||||
}
|
||||
379
packages/kbot/src/ai-tools/lib/tools/memory.ts
Normal file
379
packages/kbot/src/ai-tools/lib/tools/memory.ts
Normal file
@ -0,0 +1,379 @@
|
||||
import * as path from 'path'
|
||||
import { RunnableToolFunction } from 'openai/lib/RunnableFunction'
|
||||
import { isString } from '@polymech/core/primitives'
|
||||
|
||||
import { toolLogger } from '../../index.js'
|
||||
import { IKBotTask } from '../../types.js'
|
||||
|
||||
import { store, get, set } from './keyv.js'
|
||||
|
||||
// Helper function to get storage path
|
||||
const getStoragePath = (options: IKBotTask): string => {
|
||||
// For now, use default path. Later this can be configured via options
|
||||
return path.join(process.cwd(), 'memory.json');
|
||||
};
|
||||
|
||||
// Default collection name when none provided
|
||||
const DEFAULT_COLLECTION = 'no-collection';
|
||||
|
||||
// Supported data formats
|
||||
type DataFormat = 'text' | 'json' | 'binary';
|
||||
|
||||
// Memory entry structure
|
||||
interface MemoryEntry {
|
||||
value: string;
|
||||
meta: {
|
||||
type: DataFormat;
|
||||
created: string;
|
||||
updated: string;
|
||||
};
|
||||
}
|
||||
|
||||
// Helper function to process value based on format
|
||||
const processValueForStorage = (value: string, format: DataFormat): string => {
|
||||
switch (format) {
|
||||
case 'json':
|
||||
try {
|
||||
// Validate JSON by parsing and re-stringifying
|
||||
JSON.parse(value);
|
||||
return value;
|
||||
} catch (error) {
|
||||
throw new Error('Invalid JSON format provided');
|
||||
}
|
||||
case 'binary':
|
||||
// For binary, we expect base64 encoded data
|
||||
try {
|
||||
// Validate base64
|
||||
Buffer.from(value, 'base64');
|
||||
return value;
|
||||
} catch (error) {
|
||||
throw new Error('Invalid base64 format for binary data');
|
||||
}
|
||||
case 'text':
|
||||
default:
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
// Helper function to create memory entry
|
||||
const createMemoryEntry = (value: string, format: DataFormat): MemoryEntry => {
|
||||
const now = new Date().toISOString();
|
||||
return {
|
||||
value: processValueForStorage(value, format),
|
||||
meta: {
|
||||
type: format,
|
||||
created: now,
|
||||
updated: now
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const tools = (target: string, options: IKBotTask): Array<any> => {
|
||||
const logger = toolLogger('memory', options)
|
||||
const storagePath = getStoragePath(options);
|
||||
|
||||
return [
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'memorize',
|
||||
description: `Store information in memory as a key-value collection with format support. Supports text, JSON, and binary (base64) formats.
|
||||
|
||||
Returns: {
|
||||
success: boolean,
|
||||
message: string,
|
||||
meta: {
|
||||
type: "text" | "json" | "binary",
|
||||
created: string (ISO timestamp),
|
||||
updated: string (ISO timestamp)
|
||||
}
|
||||
}`,
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
collection: {
|
||||
type: 'string',
|
||||
description: 'Collection name to organize related data (defaults to "no-collection" if not provided). Acts like a namespace.',
|
||||
optional: true
|
||||
},
|
||||
key: {
|
||||
type: 'string',
|
||||
description: 'Unique identifier for the data within the collection. Must be a string.'
|
||||
},
|
||||
value: {
|
||||
type: 'string',
|
||||
description: 'The data to store. For format="text": any string. For format="json": valid JSON string. For format="binary": base64 encoded data.'
|
||||
},
|
||||
format: {
|
||||
type: 'string',
|
||||
description: 'Data format type. "text" for plain text (default), "json" for JSON data (validates structure), "binary" for base64 encoded binary data.',
|
||||
enum: ['text', 'json', 'binary'],
|
||||
optional: true
|
||||
}
|
||||
},
|
||||
required: ['key', 'value']
|
||||
},
|
||||
function: async (params: any) => {
|
||||
try {
|
||||
const { collection = DEFAULT_COLLECTION, key, value, format = 'text' } = params;
|
||||
logger.debug(`Tool::Memorize Storing ${key} in collection ${collection} as ${format}`);
|
||||
|
||||
const memoryEntry = createMemoryEntry(value, format as DataFormat);
|
||||
await set(`${collection}:${key}`, memoryEntry, storagePath, collection);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `Stored ${key} in collection ${collection} as ${format}`,
|
||||
meta: memoryEntry.meta
|
||||
};
|
||||
} catch (error) {
|
||||
logger.error('Error storing memory', error);
|
||||
return {
|
||||
success: false,
|
||||
message: error instanceof Error ? error.message : 'Unknown error occurred'
|
||||
};
|
||||
}
|
||||
},
|
||||
parse: JSON.parse
|
||||
}
|
||||
} as RunnableToolFunction<any>,
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'recall',
|
||||
description: `Retrieve stored information from memory by collection and key, including format metadata.
|
||||
|
||||
Returns: {
|
||||
success: boolean,
|
||||
value?: string (the stored data),
|
||||
meta?: {
|
||||
type: "text" | "json" | "binary",
|
||||
created: string (ISO timestamp),
|
||||
updated: string (ISO timestamp)
|
||||
},
|
||||
key: string,
|
||||
collection: string,
|
||||
message?: string (error message if success=false)
|
||||
}`,
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
collection: {
|
||||
type: 'string',
|
||||
description: 'Collection name to retrieve from (defaults to "no-collection" if not provided). Must match the collection used when storing.',
|
||||
optional: true
|
||||
},
|
||||
key: {
|
||||
type: 'string',
|
||||
description: 'The unique identifier of the data to retrieve. Must match the key used when storing.'
|
||||
}
|
||||
},
|
||||
required: ['key']
|
||||
},
|
||||
function: async (params: any) => {
|
||||
try {
|
||||
const { collection = DEFAULT_COLLECTION, key } = params;
|
||||
logger.debug(`Tool::Recall Retrieving ${key} from collection ${collection}`);
|
||||
|
||||
const storedData = await get(`${collection}:${key}`, storagePath, collection);
|
||||
if (storedData === undefined) {
|
||||
return { success: false, message: `Key ${key} not found in collection ${collection}` };
|
||||
}
|
||||
|
||||
// Handle both old format (plain string) and new format (MemoryEntry)
|
||||
let memoryEntry: MemoryEntry;
|
||||
if (typeof storedData === 'string') {
|
||||
// Legacy format - convert to new format
|
||||
memoryEntry = {
|
||||
value: storedData,
|
||||
meta: {
|
||||
type: 'text',
|
||||
created: new Date().toISOString(),
|
||||
updated: new Date().toISOString()
|
||||
}
|
||||
};
|
||||
} else {
|
||||
memoryEntry = storedData as MemoryEntry;
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
value: memoryEntry.value,
|
||||
meta: memoryEntry.meta,
|
||||
key,
|
||||
collection
|
||||
};
|
||||
} catch (error) {
|
||||
logger.error('Error retrieving memory', error);
|
||||
return {
|
||||
success: false,
|
||||
message: error instanceof Error ? error.message : 'Unknown error occurred'
|
||||
};
|
||||
}
|
||||
},
|
||||
parse: JSON.parse
|
||||
}
|
||||
} as RunnableToolFunction<any>,
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'forget',
|
||||
description: `Remove a specific key from memory collection.
|
||||
|
||||
Returns: {
|
||||
success: boolean,
|
||||
message: string (confirmation or error message)
|
||||
}`,
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
collection: {
|
||||
type: 'string',
|
||||
description: 'Collection name to remove from (defaults to "no-collection" if not provided). Must match the collection where the key was stored.',
|
||||
optional: true
|
||||
},
|
||||
key: {
|
||||
type: 'string',
|
||||
description: 'The unique identifier of the data to remove. Must match exactly the key used when storing.'
|
||||
}
|
||||
},
|
||||
required: ['key']
|
||||
},
|
||||
function: async (params: any) => {
|
||||
try {
|
||||
const { collection = DEFAULT_COLLECTION, key } = params;
|
||||
logger.debug(`Tool::Forget Removing ${key} from collection ${collection}`);
|
||||
|
||||
const keyv = store(storagePath, collection);
|
||||
const deleted = await keyv.delete(`${collection}:${key}`);
|
||||
return { success: deleted, message: deleted ? `Removed ${key} from ${collection}` : `Key ${key} not found in ${collection}` };
|
||||
} catch (error) {
|
||||
logger.error('Error removing from memory', error);
|
||||
return {
|
||||
success: false,
|
||||
message: error instanceof Error ? error.message : 'Unknown error occurred'
|
||||
};
|
||||
}
|
||||
},
|
||||
parse: JSON.parse
|
||||
}
|
||||
} as RunnableToolFunction<any>,
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'list_memories',
|
||||
description: `List all keys in a specific collection using Keyv's iterator method.
|
||||
|
||||
Returns: {
|
||||
success: boolean,
|
||||
collection: string (the collection name),
|
||||
keys: string[] (array of key names in the collection),
|
||||
entries: Array<{
|
||||
key: string,
|
||||
meta?: {
|
||||
type: "text" | "json" | "binary",
|
||||
created: string (ISO timestamp),
|
||||
updated: string (ISO timestamp)
|
||||
}
|
||||
}>,
|
||||
count: number (total number of keys),
|
||||
message?: string (info or error message)
|
||||
}`,
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
collection: {
|
||||
type: 'string',
|
||||
description: 'Collection name to list keys from (defaults to "no-collection" if not provided). Will return all keys stored in this collection namespace.',
|
||||
optional: true
|
||||
}
|
||||
},
|
||||
required: []
|
||||
},
|
||||
function: async (params: any) => {
|
||||
try {
|
||||
const { collection = DEFAULT_COLLECTION } = params;
|
||||
|
||||
logger.debug(`Tool::ListMemories Listing keys in collection ${collection}`);
|
||||
|
||||
// Create a Keyv instance for the specific collection to use iterator
|
||||
const keyv = store(storagePath, collection);
|
||||
const keys: string[] = [];
|
||||
const entries: { key: string; meta?: any }[] = [];
|
||||
|
||||
try {
|
||||
// Check if iterator method exists and use it
|
||||
if (typeof keyv.iterator === 'function') {
|
||||
try {
|
||||
// Try calling iterator without arguments first
|
||||
const iterator = (keyv as any).iterator();
|
||||
for await (const [key, value] of iterator) {
|
||||
// Remove the collection prefix from the key to get the clean key name
|
||||
const cleanKey = key.replace(`${collection}:`, '');
|
||||
keys.push(cleanKey);
|
||||
|
||||
// Try to extract metadata if it's a MemoryEntry
|
||||
let meta = undefined;
|
||||
if (value && typeof value === 'object' && value.meta) {
|
||||
meta = value.meta;
|
||||
}
|
||||
|
||||
entries.push({ key: cleanKey, meta });
|
||||
}
|
||||
} catch (iteratorCallError) {
|
||||
logger.warn(`Tool::ListMemories Iterator call failed:`, iteratorCallError);
|
||||
// Fall through to the not available case
|
||||
return {
|
||||
success: true,
|
||||
collection,
|
||||
keys: [],
|
||||
entries: [],
|
||||
count: 0,
|
||||
message: 'Iterator call failed. Unable to list keys.'
|
||||
};
|
||||
}
|
||||
} else {
|
||||
// Iterator not available, provide helpful message
|
||||
logger.warn(`Tool::ListMemories Iterator method not available for Keyv instance`);
|
||||
return {
|
||||
success: true,
|
||||
collection,
|
||||
keys: [],
|
||||
entries: [],
|
||||
count: 0,
|
||||
message: 'Iterator method not available in this Keyv version. Use individual key operations instead.'
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
collection,
|
||||
keys,
|
||||
entries,
|
||||
count: keys.length
|
||||
};
|
||||
} catch (iteratorError) {
|
||||
// If iterator fails, fall back to returning basic info
|
||||
logger.warn(`Tool::ListMemories Iterator failed for collection ${collection}:`, iteratorError);
|
||||
return {
|
||||
success: true,
|
||||
collection,
|
||||
keys: [],
|
||||
entries: [],
|
||||
count: 0,
|
||||
message: 'Iterator failed or collection is empty'
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Error listing memories', error);
|
||||
return {
|
||||
success: false,
|
||||
message: error instanceof Error ? error.message : 'Unknown error occurred'
|
||||
};
|
||||
}
|
||||
},
|
||||
parse: JSON.parse
|
||||
}
|
||||
} as RunnableToolFunction<any>
|
||||
]
|
||||
};
|
||||
151
packages/kbot/src/ai-tools/lib/tools/npm.ts
Normal file
151
packages/kbot/src/ai-tools/lib/tools/npm.ts
Normal file
@ -0,0 +1,151 @@
|
||||
import path from 'path'
|
||||
import { RunnableToolFunction } from 'openai/lib/RunnableFunction'
|
||||
import { exec } from 'child_process'
|
||||
import { promisify } from 'util'
|
||||
import { logger } from '../../index.js'
|
||||
import pMap from "p-map"
|
||||
import { sync as exists } from '@polymech/fs/exists'
|
||||
import { IKBotTask } from '../../types.js'
|
||||
import { toolLogger } from '../../index.js'
|
||||
|
||||
const execAsync = promisify(exec)
|
||||
const install = async (dependency: string, directory: string): Promise<any> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const command = `pnpm add ${dependency} --dir ${directory}`
|
||||
exec(command, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
logger.error(`Error installing ${dependency}:`, error.message)
|
||||
return resolve(false)
|
||||
}
|
||||
logger.info(`Successfully installed "${dependency}" in "${directory}".`)
|
||||
})
|
||||
})
|
||||
}
|
||||
export const tools = (target: string, options: IKBotTask): Array<any> => {
|
||||
const logger = toolLogger('npm', options)
|
||||
return [
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'build_project',
|
||||
description: 'Build project using pnpm build command',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {},
|
||||
required: []
|
||||
},
|
||||
function: async () => {
|
||||
try {
|
||||
logger.debug(`Tool::BuildProject Building project at ${target}`);
|
||||
const { stdout, stderr } = await execAsync('pnpm build', {
|
||||
cwd: target
|
||||
});
|
||||
return {
|
||||
success: !stderr,
|
||||
output: stdout,
|
||||
error: stderr || null
|
||||
};
|
||||
} catch (error: any) {
|
||||
logger.error('Error building project', error);
|
||||
return {
|
||||
success: false,
|
||||
output: null,
|
||||
error: error.message
|
||||
};
|
||||
}
|
||||
},
|
||||
parse: JSON.parse
|
||||
}
|
||||
} as RunnableToolFunction<any>,
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'run_npm',
|
||||
description: 'Run an npm/pnpm command',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
command: { type: 'string', description: 'Command to run (e.g. install, test, etc)' },
|
||||
args: {
|
||||
type: 'array',
|
||||
items: { type: 'string' },
|
||||
description: 'Additional arguments for the command',
|
||||
optional: true
|
||||
}
|
||||
},
|
||||
required: ['command']
|
||||
},
|
||||
function: async (params: any) => {
|
||||
try {
|
||||
const args = params.args ? params.args.join(' ') : '';
|
||||
const fullCommand = `pnpm ${params.command} ${args}`.trim();
|
||||
logger.debug(`Tool::RunNpm Running command: ${fullCommand}`);
|
||||
const { stdout, stderr } = await execAsync(fullCommand, {
|
||||
cwd: target
|
||||
});
|
||||
|
||||
return {
|
||||
success: !stderr,
|
||||
output: stdout,
|
||||
error: stderr || null
|
||||
};
|
||||
} catch (error: any) {
|
||||
logger.error('Error running npm command', error);
|
||||
return {
|
||||
success: false,
|
||||
output: null,
|
||||
error: error.message
|
||||
};
|
||||
}
|
||||
},
|
||||
parse: JSON.parse
|
||||
}
|
||||
} as RunnableToolFunction<any>,
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: "install_dependency",
|
||||
description: "Install a dependency using npm",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
dependencies: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
required: ["dependencies"],
|
||||
},
|
||||
function: async (ret) => {
|
||||
try {
|
||||
const { dependencies } = ret as any
|
||||
if (!target) {
|
||||
logger.error(`Tool::NPM Target is required to install dependencies`)
|
||||
return
|
||||
}
|
||||
if (!exists(target)) {
|
||||
logger.error(`Project doesnt path exists ${target}`)
|
||||
return
|
||||
}
|
||||
await pMap(dependencies, (async (dependency: string) => {
|
||||
logger.info(`Installing dependency`, dependency)
|
||||
try {
|
||||
return install(dependency, target)
|
||||
} catch (error) {
|
||||
logger.error(`Error installing dependency ${dependency} `, error)
|
||||
}
|
||||
}), {
|
||||
concurrency: 1
|
||||
})
|
||||
|
||||
} catch (error) {
|
||||
logger.error(`Error installing dependencies`, error)
|
||||
}
|
||||
},
|
||||
parse: JSON.parse,
|
||||
}
|
||||
} as RunnableToolFunction<{ id: string }>
|
||||
]
|
||||
}
|
||||
130
packages/kbot/src/ai-tools/lib/tools/process.ts
Normal file
130
packages/kbot/src/ai-tools/lib/tools/process.ts
Normal file
@ -0,0 +1,130 @@
|
||||
import { logger } from '../../index.js'
|
||||
import * as stream from 'stream'
|
||||
import { ChildProcess, spawn } from 'child_process'
|
||||
|
||||
export enum STATUS {
|
||||
OK,
|
||||
ERROR,
|
||||
PENDING
|
||||
}
|
||||
|
||||
const fatalHandler = (message: string, fn: (msg: string) => void): boolean => {
|
||||
if (message.startsWith('fatal:')) {
|
||||
fn('\t\ ' + message)
|
||||
return true;
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const defaultFilter = (message: string): boolean => {
|
||||
return message.length > 0 &&
|
||||
message !== '\n' &&
|
||||
message !== '\r' &&
|
||||
message !== '\r\n' &&
|
||||
!message.startsWith('Debugger attached') &&
|
||||
!message.includes('NODE_TLS_REJECT_UNAUTHORIZED') &&
|
||||
!message.includes('Waiting for the debugger to disconnect')
|
||||
}
|
||||
|
||||
const subscribe = (signal: stream.Readable, collector: (data: any) => void = () => { }) => {
|
||||
if(!signal || !signal.on) {
|
||||
return
|
||||
}
|
||||
signal.on('message', (message) => logger.debug('message', message))
|
||||
signal.on('error', (error) => logger.error('std-error', error))
|
||||
signal.on('data', (data) => {
|
||||
/*
|
||||
const msg = data.toString().replace(ansiRegex(), "")
|
||||
if (!defaultFilter(msg)) {
|
||||
return
|
||||
}
|
||||
collector(msg)*/
|
||||
process.stdout.write(data)
|
||||
|
||||
})
|
||||
}
|
||||
const merge = (buffer: string[], data: any): string[] => buffer.concat(data);
|
||||
|
||||
const hook = (child: ChildProcess, resolve: any, reject: any, cmd: string, buffer: string[] = []) => {
|
||||
const collector = (data: any) => { buffer.push(data) }
|
||||
//subscribe(child.stderr, collector)
|
||||
//process.stdin.pipe(child.stdin)
|
||||
debugger
|
||||
child.on('exit', (code, signal) => {
|
||||
debugger
|
||||
if (code) {
|
||||
resolve({
|
||||
code: STATUS.ERROR,
|
||||
command: cmd,
|
||||
error: code,
|
||||
messages: buffer
|
||||
})
|
||||
} else {
|
||||
resolve({
|
||||
code: STATUS.OK,
|
||||
command: cmd,
|
||||
messages: buffer
|
||||
})
|
||||
}
|
||||
})
|
||||
return child
|
||||
}
|
||||
|
||||
export class Process {
|
||||
public binary = ''
|
||||
public cwd: string = ''
|
||||
public args: string = ''
|
||||
public buffer: string[] = []
|
||||
constructor(options: any = {}) {
|
||||
this.binary = options.binary || this.binary
|
||||
this.cwd = options.cwd || process.cwd()
|
||||
this.buffer = options.buffer || []
|
||||
}
|
||||
public async exec(command: string, args: string[] = []): Promise<any> {
|
||||
args = [command].concat(args)
|
||||
try {
|
||||
let cmd = `${this.binary} ${args.join(' ')}`
|
||||
/*
|
||||
const p = new Promise<any>((resolve, reject) => {
|
||||
const p = exec(cmd, {
|
||||
cwd: this.cwd
|
||||
})
|
||||
return hook(p, resolve, reject, this.binary + ' ' + args.join(' '), this.buffer)
|
||||
})
|
||||
return p
|
||||
*/
|
||||
try {
|
||||
//stdio: ['pipe', 'pipe', 'pipe'],
|
||||
debugger
|
||||
const p = new Promise<any>((resolve, reject) => {
|
||||
const cp = spawn(cmd, args, {
|
||||
cwd: this.cwd,
|
||||
shell: true,
|
||||
stdio:'inherit',
|
||||
env: {
|
||||
...process.env
|
||||
},
|
||||
})
|
||||
return hook(cp, resolve, reject, cmd, this.buffer)
|
||||
})
|
||||
return p
|
||||
} catch (e) {
|
||||
logger.error('Error executing command', e)
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error('Error executing command', e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class Helper {
|
||||
public static async run(cwd, cmd: string, args: string[], buffer: string[] = [], debug_stream: boolean = false): Promise<any> {
|
||||
debug_stream && logger.info(`Run ${cmd} in ${cwd}`, args)
|
||||
const gitProcess = new Process({
|
||||
cwd,
|
||||
binary: cmd,
|
||||
buffer
|
||||
})
|
||||
return gitProcess.exec('', args)
|
||||
}
|
||||
}
|
||||
102
packages/kbot/src/ai-tools/lib/tools/search.ts
Normal file
102
packages/kbot/src/ai-tools/lib/tools/search.ts
Normal file
@ -0,0 +1,102 @@
|
||||
import * as path from 'path'
|
||||
import { RunnableToolFunction } from 'openai/lib/RunnableFunction'
|
||||
import { isArray } from '@polymech/core/primitives'
|
||||
import { CONFIG_DEFAULT } from '@polymech/commons'
|
||||
import { toolLogger } from '../../index.js'
|
||||
import { IKBotTask } from '../../types.js'
|
||||
|
||||
export const tools = (target: string, options: IKBotTask): Array<any> => {
|
||||
const logger = toolLogger('search', options)
|
||||
return [
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'google',
|
||||
description: 'Searches Google for the given query',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
query: { type: 'string' }
|
||||
},
|
||||
required: ['query']
|
||||
},
|
||||
function: async (params: any) => {
|
||||
const { query } = params
|
||||
const config = CONFIG_DEFAULT() as any
|
||||
let apiKey = config?.google?.api_key
|
||||
let cse = config?.google?.cse
|
||||
if (!config || !apiKey || !cse) {
|
||||
logger.debug(
|
||||
"Config not found in $HOME/.osr/config.json. " +
|
||||
"Optionally, export OSR_CONFIG with the path to the configuration file " +
|
||||
""
|
||||
);
|
||||
return undefined
|
||||
}
|
||||
const res = await fetch(
|
||||
`https://www.googleapis.com/customsearch/v1?key=${apiKey}&cx=${cse}&q=${encodeURIComponent(
|
||||
query
|
||||
)}`
|
||||
)
|
||||
const data = await res.json();
|
||||
let results =
|
||||
data.items?.map((item: { title?: string; link?: string; snippet?: string }) => ({
|
||||
title: item.title,
|
||||
link: item.link,
|
||||
snippet: item.snippet,
|
||||
...item
|
||||
})) ?? [];
|
||||
return JSON.stringify(results)
|
||||
},
|
||||
parse: JSON.parse
|
||||
}
|
||||
} as RunnableToolFunction<any>,
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'serpapi',
|
||||
description: 'Searches Serpapi (finds locations (engine:google_local), places on the map (engine:google_maps) ) for the given query',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
query: { type: 'string' },
|
||||
engine: { type: 'string', default: 'google' },
|
||||
},
|
||||
required: ['query']
|
||||
},
|
||||
function: async (params: any) => {
|
||||
const { query, engine } = params
|
||||
const config = CONFIG_DEFAULT() as any
|
||||
let apiKey = config?.serpapi?.key || config?.serpapi?.api_key
|
||||
if (!config || !apiKey) {
|
||||
logger.debug(
|
||||
"Config not found in $HOME/.osr/config.json. " +
|
||||
"Optionally, export OSR_CONFIG with the path to the configuration file " +
|
||||
""
|
||||
);
|
||||
return undefined
|
||||
}
|
||||
const url = `https://serpapi.com/search?api_key=${apiKey}&engine=${engine || 'google'}&q=${encodeURIComponent(query)}&google_domain=google.com`
|
||||
const res = await fetch(url)
|
||||
logger.debug(`Searching ${url}`)
|
||||
if (!res.ok) {
|
||||
throw new Error(`HTTP error! status: ${res.status}`);
|
||||
}
|
||||
const data = await res.json()
|
||||
let items = data.organic_results || data.local_results || data.place_results || data.places || data.maps_results || []
|
||||
if (items && !isArray(items)) {
|
||||
items = [items]
|
||||
}
|
||||
let results = items.map((item: any) => ({
|
||||
title: item.title,
|
||||
link: item.link,
|
||||
snippet: item.snippet,
|
||||
...item
|
||||
})) ?? []
|
||||
return JSON.stringify(results)
|
||||
},
|
||||
parse: JSON.parse
|
||||
}
|
||||
} as RunnableToolFunction<any>
|
||||
]
|
||||
};
|
||||
119
packages/kbot/src/ai-tools/lib/tools/terminal.ts
Normal file
119
packages/kbot/src/ai-tools/lib/tools/terminal.ts
Normal file
@ -0,0 +1,119 @@
|
||||
import * as path from 'path'
|
||||
import { RunnableToolFunction } from 'openai/lib/RunnableFunction'
|
||||
import { spawn } from 'child_process'
|
||||
import { toolLogger } from '../../index.js'
|
||||
import { IKBotTask } from '../../types.js'
|
||||
import { Helper } from './process.js'
|
||||
|
||||
export const tools = (target: string, options: IKBotTask): Array<any> => {
|
||||
const logger = toolLogger('terminal', options)
|
||||
return [
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'execute_command',
|
||||
description: 'Execute a terminal command and capture output',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
command: {
|
||||
type: 'string',
|
||||
description: 'Command to execute'
|
||||
},
|
||||
args: {
|
||||
type: 'array',
|
||||
items: { type: 'string' },
|
||||
description: 'Command arguments',
|
||||
optional: true
|
||||
},
|
||||
cwd: {
|
||||
type: 'string',
|
||||
description: 'Working directory for command execution',
|
||||
optional: true
|
||||
},
|
||||
background: {
|
||||
type: 'boolean',
|
||||
description: 'Run command in background (non-blocking)',
|
||||
optional: true,
|
||||
default: false
|
||||
},
|
||||
window: {
|
||||
type: 'boolean',
|
||||
description: 'Open command in new terminal window',
|
||||
optional: true,
|
||||
default: false
|
||||
},
|
||||
detached: {
|
||||
type: 'boolean',
|
||||
description: 'Run process detached from parent',
|
||||
optional: true,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
required: ['command']
|
||||
},
|
||||
function: async (params: any) => {
|
||||
try {
|
||||
debugger
|
||||
const cwd = params.cwd ? path.join(target, params.cwd) : target;
|
||||
const args = params.args || [];
|
||||
logger.debug(`Tool::Terminal : ExecuteCommand Running '${params.command}' in ${cwd}`, params)
|
||||
if (params.detached) {
|
||||
const isWindows = process.platform === 'win32';
|
||||
if (isWindows) {
|
||||
spawn('cmd', ['/c', 'start', 'cmd', '/k', params.command, ...args], {
|
||||
cwd: cwd,
|
||||
detached: true,
|
||||
stdio: 'ignore'
|
||||
});
|
||||
} else {
|
||||
// For macOS/Linux
|
||||
spawn('x-terminal-emulator', ['-e', `${params.command} ${args.join(' ')}`], {
|
||||
cwd: cwd,
|
||||
detached: true,
|
||||
stdio: 'ignore'
|
||||
});
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
output: 'Command launched in new window',
|
||||
error: null
|
||||
};
|
||||
}
|
||||
|
||||
if (params.background || params.detached) {
|
||||
const child = spawn(params.command, args, {
|
||||
cwd: cwd,
|
||||
detached: params.detached === true,
|
||||
stdio: 'ignore'
|
||||
});
|
||||
|
||||
if (params.detached) {
|
||||
child.unref();
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: `Process started with PID: ${child.pid}`,
|
||||
error: null
|
||||
};
|
||||
}
|
||||
const cmd = `${params.command} ${args.join(' ')}`.trim();
|
||||
logger.debug(`Tool::ExecuteCommand Running '${cmd}' in ${cwd}`);
|
||||
const collector = []
|
||||
const ret = await Helper.run(cwd, cmd, [], collector, true)
|
||||
return ret
|
||||
} catch (error: any) {
|
||||
logger.error('Error executing command', error);
|
||||
return {
|
||||
success: false,
|
||||
output: null,
|
||||
error: error.message
|
||||
};
|
||||
}
|
||||
},
|
||||
parse: JSON.parse
|
||||
}
|
||||
} as RunnableToolFunction<any>
|
||||
]
|
||||
}
|
||||
23
packages/kbot/src/ai-tools/lib/tools/tools.ts
Normal file
23
packages/kbot/src/ai-tools/lib/tools/tools.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { tools as fsTools } from './fs.js'
|
||||
import { tools as npmTools } from './npm.js'
|
||||
import { tools as gitTools } from './git.js'
|
||||
import { tools as terminalTools } from './terminal.js'
|
||||
import { tools as interactTools } from './interact.js'
|
||||
import { tools as userTools } from './user.js'
|
||||
import { tools as search } from './search.js'
|
||||
//import { tools as webTools } from './web.js'
|
||||
import { tools as memoryTools } from './memory.js'
|
||||
// import { tools as emailTools } from './email'
|
||||
|
||||
export const tools = {
|
||||
fs: fsTools,
|
||||
npm: npmTools,
|
||||
git: gitTools,
|
||||
terminal: terminalTools,
|
||||
interact: interactTools,
|
||||
user: userTools,
|
||||
search: search,
|
||||
// web: webTools,
|
||||
memory: memoryTools
|
||||
// email: emailTools
|
||||
}
|
||||
83
packages/kbot/src/ai-tools/lib/tools/user.ts
Normal file
83
packages/kbot/src/ai-tools/lib/tools/user.ts
Normal file
@ -0,0 +1,83 @@
|
||||
|
||||
import { parse, join } from 'path'
|
||||
import { RunnableToolFunction } from 'openai/lib/RunnableFunction'
|
||||
import { sync as write } from '@polymech/fs/write'
|
||||
import * as fs from 'fs'
|
||||
import { lookup } from 'mime-types'
|
||||
import { IKBotTask } from '../../types.js'
|
||||
import { toolLogger } from '../../index.js'
|
||||
|
||||
export const mime = (file: string = '') => parse(file).ext ? lookup(file) : null
|
||||
|
||||
//const screenshot = require('screenshot-desktop')
|
||||
|
||||
export const fileToBase64 = (filePath: string): string | null => {
|
||||
try {
|
||||
const fileBuffer = fs.readFileSync(filePath)
|
||||
const mimeType = lookup(filePath)
|
||||
if (!mimeType) {
|
||||
throw new Error('Unable to determine MIME type.')
|
||||
}
|
||||
const base64Data = fileBuffer.toString('base64')
|
||||
return `data:${mimeType};base64,${base64Data}`
|
||||
} catch (error) {
|
||||
console.error('fileToBase64 : Error reading file:', error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
export const tools = (target: string, options: IKBotTask): Array<any> => {
|
||||
const logger = toolLogger('user', options)
|
||||
return [
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'capture_screen',
|
||||
description: 'Capture a screenshot and store it as file (jpg). Returns the path to the file',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
file: { type: 'string' }
|
||||
},
|
||||
required: ['file']
|
||||
},
|
||||
function: async (params: any) => {
|
||||
try {
|
||||
const outputPath = join(target, params.file)
|
||||
const takeScreenshot = async () : Promise<any> => {
|
||||
/*
|
||||
return new Promise((resolve, reject) => {
|
||||
screenshot({ format: 'jpg' }).then((img) => {
|
||||
write(outputPath, img)
|
||||
resolve({ success: true, path: outputPath})
|
||||
}).catch(reject)
|
||||
})
|
||||
*/
|
||||
}
|
||||
const { path } = await takeScreenshot()
|
||||
return {
|
||||
"role": "user",
|
||||
"content":
|
||||
[
|
||||
/*
|
||||
{
|
||||
type: "image_url",
|
||||
image_url: {
|
||||
url: fileToBase64( path),
|
||||
}
|
||||
}
|
||||
*/
|
||||
]
|
||||
}
|
||||
} catch (error: any) {
|
||||
logger.error('Error capturing screenshot:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.message
|
||||
};
|
||||
}
|
||||
},
|
||||
parse: JSON.parse
|
||||
}
|
||||
} as RunnableToolFunction<any>
|
||||
];
|
||||
};
|
||||
112
packages/kbot/src/ai-tools/lib/tools/web.ts
Normal file
112
packages/kbot/src/ai-tools/lib/tools/web.ts
Normal file
@ -0,0 +1,112 @@
|
||||
import * as path from 'path'
|
||||
import { RunnableToolFunction } from 'openai/lib/RunnableFunction'
|
||||
|
||||
import puppeteer from 'puppeteer'
|
||||
import TurndownService from 'turndown'
|
||||
|
||||
import { toolLogger } from '../../index.js'
|
||||
import { IKBotTask } from '../../types.js'
|
||||
|
||||
const turndown = new TurndownService()
|
||||
|
||||
export const tools = (target: string, options: IKBotTask): Array<any> => {
|
||||
const logger = toolLogger('web', options)
|
||||
return [
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'browse_page',
|
||||
description: 'Browse a webpage and return its content as markdown, all links, images and pages main image',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
url: {
|
||||
type: 'string',
|
||||
description: 'URL of the webpage to browse'
|
||||
}
|
||||
},
|
||||
required: ['url']
|
||||
},
|
||||
function: async (params: any) => {
|
||||
try {
|
||||
logger.debug(`Tool::BrowsePage Browsing ${params.url}`);
|
||||
const browser = await puppeteer.launch({
|
||||
headless: true,
|
||||
args: ['--no-sandbox', '--disable-setuid-sandbox']
|
||||
})
|
||||
|
||||
try {
|
||||
const page = await browser.newPage()
|
||||
logger.debug(`Tool::Web::BrowsePage Opening page ${params.url}`)
|
||||
await page.goto(params.url, {
|
||||
waitUntil: 'networkidle2'
|
||||
})
|
||||
|
||||
const pageData = await page.evaluate((selector) => {
|
||||
const elementsToRemove = document.querySelectorAll(
|
||||
'script, style, link, meta, noscript, iframe, [style*="display:none"],[style*="display: none"], .hidden'
|
||||
)
|
||||
elementsToRemove.forEach(el => el.remove())
|
||||
|
||||
const links = Array.from(document.querySelectorAll('a'))
|
||||
.map(a => ({
|
||||
text: a.textContent?.trim() || '',
|
||||
href: a.href
|
||||
}))
|
||||
.filter(link => link.href && link.href.startsWith('http'))
|
||||
.slice(0, 20)
|
||||
|
||||
const images = Array.from(document.querySelectorAll('img'))
|
||||
.map(img => ({
|
||||
src: img.src,
|
||||
alt: img.alt || '',
|
||||
width: img.width,
|
||||
height: img.height
|
||||
}))
|
||||
.filter(img => img.src && img.src.startsWith('http'))
|
||||
.slice(0, 20)
|
||||
|
||||
const mainImage = document.querySelector('meta[property="og:image"]')?.getAttribute('content') ||
|
||||
document.querySelector('meta[name="og:image"]')?.getAttribute('content')
|
||||
|
||||
let content
|
||||
const body = document.body
|
||||
content = body ? body.innerHTML : ''
|
||||
return {
|
||||
content,
|
||||
links,
|
||||
images,
|
||||
ogImage: mainImage
|
||||
}
|
||||
}, null)
|
||||
|
||||
const markdown = turndown.turndown(pageData.content)
|
||||
await browser.close()
|
||||
const ret = {
|
||||
success: true,
|
||||
markdown: markdown,
|
||||
links: pageData.links,
|
||||
images: pageData.images,
|
||||
mainImage: pageData.ogImage,
|
||||
url: params.url
|
||||
};
|
||||
return ret
|
||||
} catch (error: any) {
|
||||
logger.debug('Error browsing page:', error.message, error);
|
||||
await browser.close()
|
||||
throw error
|
||||
}
|
||||
} catch (error: any) {
|
||||
logger.debug('Error browsing page:', error.message);
|
||||
return {
|
||||
success: false,
|
||||
error: error.message,
|
||||
url: params.url
|
||||
};
|
||||
}
|
||||
},
|
||||
parse: JSON.parse
|
||||
}
|
||||
} as RunnableToolFunction<any>
|
||||
]
|
||||
}
|
||||
75
packages/kbot/src/ai-tools/list.ts
Normal file
75
packages/kbot/src/ai-tools/list.ts
Normal file
@ -0,0 +1,75 @@
|
||||
import type { Argv } from 'yargs'
|
||||
import { tools } from '../lib/tools/tools.js'
|
||||
import { logger } from '../index.js'
|
||||
import { ListCommandSchema } from '../zod_schemas.js'
|
||||
import { sync as write } from '@polymech/fs/write'
|
||||
import { toYargs } from '@polymech/commons/schemas'
|
||||
|
||||
export const options = (yargs: Argv) => toYargs(yargs, ListCommandSchema)
|
||||
|
||||
interface FSParameters {
|
||||
type: string;
|
||||
properties: Record<string, any>;
|
||||
required: string[];
|
||||
}
|
||||
interface FSDefinition {
|
||||
name: string;
|
||||
description: string;
|
||||
category: string;
|
||||
parameters: FSParameters;
|
||||
}
|
||||
interface FSData {
|
||||
fs: FSDefinition[];
|
||||
}
|
||||
|
||||
export const signature = (definition: FSDefinition): string => {
|
||||
const { properties } = definition.parameters;
|
||||
const requiredKeys = definition.parameters.required || [];
|
||||
const params = Object.entries(properties).map(([key, val]) => {
|
||||
const isRequired = requiredKeys.includes(key);
|
||||
const isOptional = !!val.optional || !isRequired;
|
||||
return isOptional ? `?${key}` : key;
|
||||
});
|
||||
return `(${params.join(", ")})`;
|
||||
}
|
||||
|
||||
export function format(category: string, data: any): string {
|
||||
const lines: string[] = [`## ${category}\n`];
|
||||
data.forEach(definition => {
|
||||
const functionName = definition.name
|
||||
const args = `${signature(definition)}`
|
||||
const summary = definition.description
|
||||
lines.push(`- ${functionName}${args}: ${summary}`)
|
||||
})
|
||||
return lines.join("\n")
|
||||
}
|
||||
|
||||
export const list = async (argv: any, options?: any) => {
|
||||
const getCategorizedTools = (category, options) => {
|
||||
const toolsArray = tools[category](process.cwd(), options);
|
||||
return toolsArray.map(tool => ({
|
||||
name: tool.function.name,
|
||||
description: tool.function.description,
|
||||
category,
|
||||
parameters: tool.function.parameters
|
||||
}));
|
||||
}
|
||||
const toolsList = {
|
||||
email: getCategorizedTools('email', options),
|
||||
search: getCategorizedTools('search', options),
|
||||
interact: getCategorizedTools('email', options),
|
||||
fs: getCategorizedTools('fs', options),
|
||||
npm: getCategorizedTools('npm', options),
|
||||
git: getCategorizedTools('git', options),
|
||||
terminal: getCategorizedTools('terminal', options)
|
||||
}
|
||||
//write(argv.output + '.json', Object.keys(toolsList).map((k,v)=>format(k,v as any)).join('\n') );
|
||||
|
||||
const shortDescription = Object.keys(toolsList).map((value:string) => {
|
||||
return format(value,toolsList[value])
|
||||
}).join('\n\n');
|
||||
if (argv.output) {
|
||||
write(argv.output, JSON.stringify(toolsList, null, 2))
|
||||
write(argv.output + '.md', shortDescription)
|
||||
}
|
||||
}
|
||||
16
packages/kbot/src/ai-tools/main.ts
Normal file
16
packages/kbot/src/ai-tools/main.ts
Normal file
@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env node
|
||||
import { commands } from './commands/index.js'
|
||||
import { logger } from './index.js'
|
||||
import cli from 'yargs'
|
||||
import { hideBin } from 'yargs/helpers'
|
||||
const yargs = cli(hideBin(process.argv))
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
const argv = await commands(yargs).argv
|
||||
} catch (error) {
|
||||
logger.error('Error executing command:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
main()
|
||||
30
packages/kbot/src/ai-tools/types.ts
Normal file
30
packages/kbot/src/ai-tools/types.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { ChatCompletion, ChatCompletionMessage, ChatCompletionMessageParam } from 'openai/resources'
|
||||
import { IKBotOptions } from './types_kbot.js'
|
||||
import OpenAI from 'openai'
|
||||
import { Logger, ILogObj } from 'tslog'
|
||||
|
||||
import { RunnableFunctionWithParse } from 'openai/lib/RunnableFunction'
|
||||
|
||||
export type onToolBefore = (ctx: RunnableFunctionWithParse<any>,args: any) => Promise<any>
|
||||
export type onToolAfter = (ctx: RunnableFunctionWithParse<any>, args: any, result?: any) => Promise<any>
|
||||
|
||||
export interface ICollector {
|
||||
//OpenAI
|
||||
onMessage: (message: ChatCompletionMessageParam) => void
|
||||
onToolCall: (tool: ChatCompletionMessage.FunctionCall) => void,
|
||||
onFunctionCallResult: (content: string) => void,
|
||||
onChatCompletion: (completion: ChatCompletion) => void,
|
||||
onContent: (content:string) => void,
|
||||
// internal
|
||||
onTool: (category: string, name: string, args: any, result?: any) => void
|
||||
onToolBefore: onToolBefore
|
||||
onToolAfter: onToolAfter
|
||||
}
|
||||
export interface IKBotTask extends IKBotOptions
|
||||
{
|
||||
client?: OpenAI
|
||||
collector?: ICollector
|
||||
onRun?:(ctx: IKBotTask) => Promise<IKBotTask>
|
||||
logger?: Logger<ILogObj>
|
||||
customTools?: any[]
|
||||
}
|
||||
569
packages/kbot/src/ai-tools/types_kbot.ts
Normal file
569
packages/kbot/src/ai-tools/types_kbot.ts
Normal file
@ -0,0 +1,569 @@
|
||||
export interface IKBotOptions {
|
||||
/** Target directory */
|
||||
path?: string;
|
||||
/** The prompt. Supports file paths and environment variables. */
|
||||
prompt?: string | undefined;
|
||||
/** Optional output path for modified files (Tool mode only) */
|
||||
output?: string | undefined;
|
||||
/** Optional destination path for the result, will substitute ${MODEL_NAME} and ${ROUTER} in the path. Optional, used for "completion" mode */
|
||||
dst?: string | undefined;
|
||||
/** How to handle output if --dst file already exists: "concat" (append) or "merge" (try to merge structures if possible, otherwise append). Only used if --dst is specified. */
|
||||
append?: ("concat" | "merge" | "replace") | undefined;
|
||||
/** Specify how to wrap the output, "meta (file name, absolute path, cwd)" or "none". */
|
||||
wrap?: "meta" | "none";
|
||||
/** Iterate over items, supported: GLOB | Path to JSON File | array of strings (comma separated). To test different models, use --each="gpt-3.5-turbo,gpt-4o", the actual string will exposed as variable `ITEM`, eg: --dst="${ITEM}-output.md" */
|
||||
each?: string | undefined;
|
||||
/** Disable tools categories, eg: --disable=fs,git,interact,terminal,search,web,email,user */
|
||||
disable?: string[];
|
||||
/** List of specific tools to disable */
|
||||
disableTools?: string[];
|
||||
/** List of tools to use. Can be built-in tool names or paths to custom tool files. Default: fs,git,interact,terminal,search,web,email,user */
|
||||
tools?: (string[] | string);
|
||||
/** Comma separated glob patterns or paths, eg --include=src/*.tsx,src/*.ts --include=package.json */
|
||||
include?: string[] | undefined;
|
||||
/** Comma separated glob patterns or paths, eg --exclude=src/*.tsx,src/*.ts --exclude=package.json */
|
||||
exclude?: string[] | undefined;
|
||||
/** Specify a glob extension behavior. Available presets: match-cpp. Also accepts a custom glob pattern with variables like ${SRC_DIR}, ${SRC_NAME}, ${SRC_EXT}. E.g., "match-cpp" or "${SRC_DIR}/${SRC_NAME}*.cpp" */
|
||||
globExtension?: (("match-cpp") | string) | undefined;
|
||||
/** Explicit API key to use */
|
||||
api_key?: string | undefined;
|
||||
/** AI model to use for processing. Available models:
|
||||
[35m[1m[22m[39m
|
||||
[35m[1m OpenRouter models:[22m[39m
|
||||
[35m[1m[22m[39m
|
||||
ai21/jamba-large-1.7 | paid
|
||||
aion-labs/aion-1.0 | paid
|
||||
aion-labs/aion-1.0-mini | paid
|
||||
aion-labs/aion-2.0 | paid
|
||||
aion-labs/aion-rp-llama-3.1-8b | paid
|
||||
alfredpros/codellama-7b-instruct-solidity | paid
|
||||
allenai/molmo-2-8b | paid
|
||||
allenai/olmo-2-0325-32b-instruct | paid
|
||||
allenai/olmo-3-32b-think | paid
|
||||
allenai/olmo-3-7b-instruct | paid
|
||||
allenai/olmo-3-7b-think | paid
|
||||
allenai/olmo-3.1-32b-instruct | paid
|
||||
allenai/olmo-3.1-32b-think | paid
|
||||
amazon/nova-2-lite-v1 | paid
|
||||
amazon/nova-lite-v1 | paid
|
||||
amazon/nova-micro-v1 | paid
|
||||
amazon/nova-premier-v1 | paid
|
||||
amazon/nova-pro-v1 | paid
|
||||
anthropic/claude-3-haiku | paid
|
||||
anthropic/claude-3.5-haiku | paid
|
||||
anthropic/claude-3.5-sonnet | paid
|
||||
anthropic/claude-3.7-sonnet | paid
|
||||
anthropic/claude-3.7-sonnet:thinking | paid
|
||||
anthropic/claude-haiku-4.5 | paid
|
||||
anthropic/claude-opus-4 | paid
|
||||
anthropic/claude-opus-4.1 | paid
|
||||
anthropic/claude-opus-4.5 | paid
|
||||
anthropic/claude-opus-4.6 | paid
|
||||
anthropic/claude-sonnet-4 | paid
|
||||
anthropic/claude-sonnet-4.5 | paid
|
||||
anthropic/claude-sonnet-4.6 | paid
|
||||
arcee-ai/coder-large | paid
|
||||
arcee-ai/maestro-reasoning | paid
|
||||
arcee-ai/spotlight | paid
|
||||
arcee-ai/trinity-large-preview:free | free
|
||||
arcee-ai/trinity-mini | paid
|
||||
arcee-ai/trinity-mini:free | free
|
||||
arcee-ai/virtuoso-large | paid
|
||||
openrouter/auto | paid
|
||||
baidu/ernie-4.5-21b-a3b | paid
|
||||
baidu/ernie-4.5-21b-a3b-thinking | paid
|
||||
baidu/ernie-4.5-300b-a47b | paid
|
||||
baidu/ernie-4.5-vl-28b-a3b | paid
|
||||
baidu/ernie-4.5-vl-424b-a47b | paid
|
||||
openrouter/bodybuilder | paid
|
||||
bytedance-seed/seed-1.6 | paid
|
||||
bytedance-seed/seed-1.6-flash | paid
|
||||
bytedance-seed/seed-2.0-lite | paid
|
||||
bytedance-seed/seed-2.0-mini | paid
|
||||
bytedance/ui-tars-1.5-7b | paid
|
||||
cohere/command-a | paid
|
||||
cohere/command-r-08-2024 | paid
|
||||
cohere/command-r-plus-08-2024 | paid
|
||||
cohere/command-r7b-12-2024 | paid
|
||||
deepcogito/cogito-v2.1-671b | paid
|
||||
deepseek/deepseek-chat | paid
|
||||
deepseek/deepseek-chat-v3-0324 | paid
|
||||
deepseek/deepseek-chat-v3.1 | paid
|
||||
deepseek/deepseek-v3.1-terminus | paid
|
||||
deepseek/deepseek-v3.2 | paid
|
||||
deepseek/deepseek-v3.2-exp | paid
|
||||
deepseek/deepseek-v3.2-speciale | paid
|
||||
deepseek/deepseek-r1 | paid
|
||||
deepseek/deepseek-r1-0528 | paid
|
||||
deepseek/deepseek-r1-distill-llama-70b | paid
|
||||
deepseek/deepseek-r1-distill-qwen-32b | paid
|
||||
eleutherai/llemma_7b | paid
|
||||
essentialai/rnj-1-instruct | paid
|
||||
openrouter/free | paid
|
||||
alpindale/goliath-120b | paid
|
||||
google/gemini-2.0-flash-001 | paid
|
||||
google/gemini-2.0-flash-lite-001 | paid
|
||||
google/gemini-2.5-flash | paid
|
||||
google/gemini-2.5-flash-lite | paid
|
||||
google/gemini-2.5-flash-lite-preview-09-2025 | paid
|
||||
google/gemini-2.5-pro | paid
|
||||
google/gemini-2.5-pro-preview-05-06 | paid
|
||||
google/gemini-2.5-pro-preview | paid
|
||||
google/gemini-3-flash-preview | paid
|
||||
google/gemini-3-pro-preview | paid
|
||||
google/gemini-3.1-flash-lite-preview | paid
|
||||
google/gemini-3.1-pro-preview | paid
|
||||
google/gemini-3.1-pro-preview-customtools | paid
|
||||
google/gemma-2-27b-it | paid
|
||||
google/gemma-2-9b-it | paid
|
||||
google/gemma-3-12b-it | paid
|
||||
google/gemma-3-12b-it:free | free
|
||||
google/gemma-3-27b-it | paid
|
||||
google/gemma-3-27b-it:free | free
|
||||
google/gemma-3-4b-it | paid
|
||||
google/gemma-3-4b-it:free | free
|
||||
google/gemma-3n-e2b-it:free | free
|
||||
google/gemma-3n-e4b-it | paid
|
||||
google/gemma-3n-e4b-it:free | free
|
||||
google/gemini-2.5-flash-image | paid
|
||||
google/gemini-3.1-flash-image-preview | paid
|
||||
google/gemini-3-pro-image-preview | paid
|
||||
ibm-granite/granite-4.0-h-micro | paid
|
||||
inception/mercury | paid
|
||||
inception/mercury-2 | paid
|
||||
inception/mercury-coder | paid
|
||||
inflection/inflection-3-pi | paid
|
||||
inflection/inflection-3-productivity | paid
|
||||
kwaipilot/kat-coder-pro | paid
|
||||
liquid/lfm-2.2-6b | paid
|
||||
liquid/lfm-2-24b-a2b | paid
|
||||
liquid/lfm2-8b-a1b | paid
|
||||
liquid/lfm-2.5-1.2b-instruct:free | free
|
||||
liquid/lfm-2.5-1.2b-thinking:free | free
|
||||
meta-llama/llama-guard-3-8b | paid
|
||||
anthracite-org/magnum-v4-72b | paid
|
||||
mancer/weaver | paid
|
||||
meituan/longcat-flash-chat | paid
|
||||
meta-llama/llama-3-70b-instruct | paid
|
||||
meta-llama/llama-3-8b-instruct | paid
|
||||
meta-llama/llama-3.1-405b | paid
|
||||
meta-llama/llama-3.1-70b-instruct | paid
|
||||
meta-llama/llama-3.1-8b-instruct | paid
|
||||
meta-llama/llama-3.2-11b-vision-instruct | paid
|
||||
meta-llama/llama-3.2-1b-instruct | paid
|
||||
meta-llama/llama-3.2-3b-instruct | paid
|
||||
meta-llama/llama-3.2-3b-instruct:free | free
|
||||
meta-llama/llama-3.3-70b-instruct | paid
|
||||
meta-llama/llama-3.3-70b-instruct:free | free
|
||||
meta-llama/llama-4-maverick | paid
|
||||
meta-llama/llama-4-scout | paid
|
||||
meta-llama/llama-guard-4-12b | paid
|
||||
microsoft/phi-4 | paid
|
||||
minimax/minimax-m1 | paid
|
||||
minimax/minimax-m2 | paid
|
||||
minimax/minimax-m2-her | paid
|
||||
minimax/minimax-m2.1 | paid
|
||||
minimax/minimax-m2.5 | paid
|
||||
minimax/minimax-m2.5:free | free
|
||||
minimax/minimax-m2.7 | paid
|
||||
minimax/minimax-01 | paid
|
||||
mistralai/mistral-large | paid
|
||||
mistralai/mistral-large-2407 | paid
|
||||
mistralai/mistral-large-2411 | paid
|
||||
mistralai/codestral-2508 | paid
|
||||
mistralai/devstral-2512 | paid
|
||||
mistralai/devstral-medium | paid
|
||||
mistralai/devstral-small | paid
|
||||
mistralai/ministral-14b-2512 | paid
|
||||
mistralai/ministral-3b-2512 | paid
|
||||
mistralai/ministral-8b-2512 | paid
|
||||
mistralai/mistral-7b-instruct-v0.1 | paid
|
||||
mistralai/mistral-large-2512 | paid
|
||||
mistralai/mistral-medium-3 | paid
|
||||
mistralai/mistral-medium-3.1 | paid
|
||||
mistralai/mistral-nemo | paid
|
||||
mistralai/mistral-small-24b-instruct-2501 | paid
|
||||
mistralai/mistral-small-3.1-24b-instruct | paid
|
||||
mistralai/mistral-small-3.1-24b-instruct:free | free
|
||||
mistralai/mistral-small-3.2-24b-instruct | paid
|
||||
mistralai/mistral-small-2603 | paid
|
||||
mistralai/mistral-small-creative | paid
|
||||
mistralai/mixtral-8x22b-instruct | paid
|
||||
mistralai/mixtral-8x7b-instruct | paid
|
||||
mistralai/pixtral-large-2411 | paid
|
||||
mistralai/mistral-saba | paid
|
||||
mistralai/voxtral-small-24b-2507 | paid
|
||||
moonshotai/kimi-k2 | paid
|
||||
moonshotai/kimi-k2-0905 | paid
|
||||
moonshotai/kimi-k2-thinking | paid
|
||||
moonshotai/kimi-k2.5 | paid
|
||||
morph/morph-v3-fast | paid
|
||||
morph/morph-v3-large | paid
|
||||
gryphe/mythomax-l2-13b | paid
|
||||
nex-agi/deepseek-v3.1-nex-n1 | paid
|
||||
nousresearch/hermes-3-llama-3.1-405b | paid
|
||||
nousresearch/hermes-3-llama-3.1-405b:free | free
|
||||
nousresearch/hermes-3-llama-3.1-70b | paid
|
||||
nousresearch/hermes-4-405b | paid
|
||||
nousresearch/hermes-4-70b | paid
|
||||
nousresearch/hermes-2-pro-llama-3-8b | paid
|
||||
nvidia/llama-3.1-nemotron-70b-instruct | paid
|
||||
nvidia/llama-3.3-nemotron-super-49b-v1.5 | paid
|
||||
nvidia/nemotron-3-nano-30b-a3b | paid
|
||||
nvidia/nemotron-3-nano-30b-a3b:free | free
|
||||
nvidia/nemotron-3-super-120b-a12b | paid
|
||||
nvidia/nemotron-3-super-120b-a12b:free | free
|
||||
nvidia/nemotron-nano-12b-v2-vl | paid
|
||||
nvidia/nemotron-nano-12b-v2-vl:free | free
|
||||
nvidia/nemotron-nano-9b-v2 | paid
|
||||
nvidia/nemotron-nano-9b-v2:free | free
|
||||
openai/gpt-audio | paid
|
||||
openai/gpt-audio-mini | paid
|
||||
openai/gpt-3.5-turbo | paid
|
||||
openai/gpt-3.5-turbo-0613 | paid
|
||||
openai/gpt-3.5-turbo-16k | paid
|
||||
openai/gpt-3.5-turbo-instruct | paid
|
||||
openai/gpt-4 | paid
|
||||
openai/gpt-4-0314 | paid
|
||||
openai/gpt-4-turbo | paid
|
||||
openai/gpt-4-1106-preview | paid
|
||||
openai/gpt-4-turbo-preview | paid
|
||||
openai/gpt-4.1 | paid
|
||||
openai/gpt-4.1-mini | paid
|
||||
openai/gpt-4.1-nano | paid
|
||||
openai/gpt-4o | paid
|
||||
openai/gpt-4o-2024-05-13 | paid
|
||||
openai/gpt-4o-2024-08-06 | paid
|
||||
openai/gpt-4o-2024-11-20 | paid
|
||||
openai/gpt-4o:extended | paid
|
||||
openai/gpt-4o-audio-preview | paid
|
||||
openai/gpt-4o-search-preview | paid
|
||||
openai/gpt-4o-mini | paid
|
||||
openai/gpt-4o-mini-2024-07-18 | paid
|
||||
openai/gpt-4o-mini-search-preview | paid
|
||||
openai/gpt-5 | paid
|
||||
openai/gpt-5-chat | paid
|
||||
openai/gpt-5-codex | paid
|
||||
openai/gpt-5-image | paid
|
||||
openai/gpt-5-image-mini | paid
|
||||
openai/gpt-5-mini | paid
|
||||
openai/gpt-5-nano | paid
|
||||
openai/gpt-5-pro | paid
|
||||
openai/gpt-5.1 | paid
|
||||
openai/gpt-5.1-chat | paid
|
||||
openai/gpt-5.1-codex | paid
|
||||
openai/gpt-5.1-codex-max | paid
|
||||
openai/gpt-5.1-codex-mini | paid
|
||||
openai/gpt-5.2 | paid
|
||||
openai/gpt-5.2-chat | paid
|
||||
openai/gpt-5.2-pro | paid
|
||||
openai/gpt-5.2-codex | paid
|
||||
openai/gpt-5.3-chat | paid
|
||||
openai/gpt-5.3-codex | paid
|
||||
openai/gpt-5.4 | paid
|
||||
openai/gpt-5.4-mini | paid
|
||||
openai/gpt-5.4-nano | paid
|
||||
openai/gpt-5.4-pro | paid
|
||||
openai/gpt-oss-120b | paid
|
||||
openai/gpt-oss-120b:free | free
|
||||
openai/gpt-oss-20b | paid
|
||||
openai/gpt-oss-20b:free | free
|
||||
openai/gpt-oss-safeguard-20b | paid
|
||||
openai/o1 | paid
|
||||
openai/o1-pro | paid
|
||||
openai/o3 | paid
|
||||
openai/o3-deep-research | paid
|
||||
openai/o3-mini | paid
|
||||
openai/o3-mini-high | paid
|
||||
openai/o3-pro | paid
|
||||
openai/o4-mini | paid
|
||||
openai/o4-mini-deep-research | paid
|
||||
openai/o4-mini-high | paid
|
||||
perplexity/sonar | paid
|
||||
perplexity/sonar-deep-research | paid
|
||||
perplexity/sonar-pro | paid
|
||||
perplexity/sonar-pro-search | paid
|
||||
perplexity/sonar-reasoning-pro | paid
|
||||
prime-intellect/intellect-3 | paid
|
||||
qwen/qwen-plus-2025-07-28 | paid
|
||||
qwen/qwen-plus-2025-07-28:thinking | paid
|
||||
qwen/qwen-vl-max | paid
|
||||
qwen/qwen-vl-plus | paid
|
||||
qwen/qwen-max | paid
|
||||
qwen/qwen-plus | paid
|
||||
qwen/qwen-turbo | paid
|
||||
qwen/qwen-2.5-7b-instruct | paid
|
||||
qwen/qwen2.5-coder-7b-instruct | paid
|
||||
qwen/qwen2.5-vl-32b-instruct | paid
|
||||
qwen/qwen2.5-vl-72b-instruct | paid
|
||||
qwen/qwen-2.5-vl-7b-instruct | paid
|
||||
qwen/qwen3-14b | paid
|
||||
qwen/qwen3-235b-a22b | paid
|
||||
qwen/qwen3-235b-a22b-2507 | paid
|
||||
qwen/qwen3-235b-a22b-thinking-2507 | paid
|
||||
qwen/qwen3-30b-a3b | paid
|
||||
qwen/qwen3-30b-a3b-instruct-2507 | paid
|
||||
qwen/qwen3-30b-a3b-thinking-2507 | paid
|
||||
qwen/qwen3-32b | paid
|
||||
qwen/qwen3-4b:free | free
|
||||
qwen/qwen3-8b | paid
|
||||
qwen/qwen3-coder-30b-a3b-instruct | paid
|
||||
qwen/qwen3-coder | paid
|
||||
qwen/qwen3-coder:free | free
|
||||
qwen/qwen3-coder-flash | paid
|
||||
qwen/qwen3-coder-next | paid
|
||||
qwen/qwen3-coder-plus | paid
|
||||
qwen/qwen3-max | paid
|
||||
qwen/qwen3-max-thinking | paid
|
||||
qwen/qwen3-next-80b-a3b-instruct | paid
|
||||
qwen/qwen3-next-80b-a3b-instruct:free | free
|
||||
qwen/qwen3-next-80b-a3b-thinking | paid
|
||||
qwen/qwen3-vl-235b-a22b-instruct | paid
|
||||
qwen/qwen3-vl-235b-a22b-thinking | paid
|
||||
qwen/qwen3-vl-30b-a3b-instruct | paid
|
||||
qwen/qwen3-vl-30b-a3b-thinking | paid
|
||||
qwen/qwen3-vl-32b-instruct | paid
|
||||
qwen/qwen3-vl-8b-instruct | paid
|
||||
qwen/qwen3-vl-8b-thinking | paid
|
||||
qwen/qwen3.5-397b-a17b | paid
|
||||
qwen/qwen3.5-plus-02-15 | paid
|
||||
qwen/qwen3.5-122b-a10b | paid
|
||||
qwen/qwen3.5-27b | paid
|
||||
qwen/qwen3.5-35b-a3b | paid
|
||||
qwen/qwen3.5-9b | paid
|
||||
qwen/qwen3.5-flash-02-23 | paid
|
||||
qwen/qwq-32b | paid
|
||||
qwen/qwen-2.5-72b-instruct | paid
|
||||
qwen/qwen-2.5-coder-32b-instruct | paid
|
||||
relace/relace-apply-3 | paid
|
||||
relace/relace-search | paid
|
||||
undi95/remm-slerp-l2-13b | paid
|
||||
sao10k/l3-lunaris-8b | paid
|
||||
sao10k/l3-euryale-70b | paid
|
||||
sao10k/l3.1-70b-hanami-x1 | paid
|
||||
sao10k/l3.1-euryale-70b | paid
|
||||
sao10k/l3.3-euryale-70b | paid
|
||||
stepfun/step-3.5-flash | paid
|
||||
stepfun/step-3.5-flash:free | free
|
||||
switchpoint/router | paid
|
||||
tencent/hunyuan-a13b-instruct | paid
|
||||
thedrummer/cydonia-24b-v4.1 | paid
|
||||
thedrummer/rocinante-12b | paid
|
||||
thedrummer/skyfall-36b-v2 | paid
|
||||
thedrummer/unslopnemo-12b | paid
|
||||
tngtech/deepseek-r1t2-chimera | paid
|
||||
alibaba/tongyi-deepresearch-30b-a3b | paid
|
||||
upstage/solar-pro-3 | paid
|
||||
cognitivecomputations/dolphin-mistral-24b-venice-edition:free | free
|
||||
microsoft/wizardlm-2-8x22b | paid
|
||||
writer/palmyra-x5 | paid
|
||||
x-ai/grok-3 | paid
|
||||
x-ai/grok-3-beta | paid
|
||||
x-ai/grok-3-mini | paid
|
||||
x-ai/grok-3-mini-beta | paid
|
||||
x-ai/grok-4 | paid
|
||||
x-ai/grok-4-fast | paid
|
||||
x-ai/grok-4.1-fast | paid
|
||||
x-ai/grok-4.20-beta | paid
|
||||
x-ai/grok-4.20-multi-agent-beta | paid
|
||||
x-ai/grok-code-fast-1 | paid
|
||||
xiaomi/mimo-v2-flash | paid
|
||||
xiaomi/mimo-v2-omni | paid
|
||||
xiaomi/mimo-v2-pro | paid
|
||||
z-ai/glm-4-32b | paid
|
||||
z-ai/glm-4.5 | paid
|
||||
z-ai/glm-4.5-air | paid
|
||||
z-ai/glm-4.5-air:free | free
|
||||
z-ai/glm-4.5v | paid
|
||||
z-ai/glm-4.6 | paid
|
||||
z-ai/glm-4.6v | paid
|
||||
z-ai/glm-4.7 | paid
|
||||
z-ai/glm-4.7-flash | paid
|
||||
z-ai/glm-5 | paid
|
||||
z-ai/glm-5-turbo | paid
|
||||
[35m[1m[22m[39m
|
||||
[35m[1m OpenAI models:[22m[39m
|
||||
[35m[1m[22m[39m
|
||||
babbage-002
|
||||
chatgpt-image-latest
|
||||
dall-e-2
|
||||
dall-e-3
|
||||
davinci-002
|
||||
gpt-3.5-turbo
|
||||
gpt-3.5-turbo-0125
|
||||
gpt-3.5-turbo-1106
|
||||
gpt-3.5-turbo-16k
|
||||
gpt-3.5-turbo-instruct
|
||||
gpt-3.5-turbo-instruct-0914
|
||||
gpt-4
|
||||
gpt-4-0125-preview
|
||||
gpt-4-0613
|
||||
gpt-4-1106-preview
|
||||
gpt-4-turbo
|
||||
gpt-4-turbo-2024-04-09
|
||||
gpt-4-turbo-preview
|
||||
gpt-4.1
|
||||
gpt-4.1-2025-04-14
|
||||
gpt-4.1-mini
|
||||
gpt-4.1-mini-2025-04-14
|
||||
gpt-4.1-nano
|
||||
gpt-4.1-nano-2025-04-14
|
||||
gpt-4o
|
||||
gpt-4o-2024-05-13
|
||||
gpt-4o-2024-08-06
|
||||
gpt-4o-2024-11-20
|
||||
gpt-4o-audio-preview
|
||||
gpt-4o-audio-preview-2024-12-17
|
||||
gpt-4o-audio-preview-2025-06-03
|
||||
gpt-4o-mini
|
||||
gpt-4o-mini-2024-07-18
|
||||
gpt-4o-mini-audio-preview
|
||||
gpt-4o-mini-audio-preview-2024-12-17
|
||||
gpt-4o-mini-realtime-preview
|
||||
gpt-4o-mini-realtime-preview-2024-12-17
|
||||
gpt-4o-mini-search-preview
|
||||
gpt-4o-mini-search-preview-2025-03-11
|
||||
gpt-4o-mini-transcribe
|
||||
gpt-4o-mini-transcribe-2025-03-20
|
||||
gpt-4o-mini-transcribe-2025-12-15
|
||||
gpt-4o-mini-tts
|
||||
gpt-4o-mini-tts-2025-03-20
|
||||
gpt-4o-mini-tts-2025-12-15
|
||||
gpt-4o-realtime-preview
|
||||
gpt-4o-realtime-preview-2024-12-17
|
||||
gpt-4o-realtime-preview-2025-06-03
|
||||
gpt-4o-search-preview
|
||||
gpt-4o-search-preview-2025-03-11
|
||||
gpt-4o-transcribe
|
||||
gpt-4o-transcribe-diarize
|
||||
gpt-5
|
||||
gpt-5-2025-08-07
|
||||
gpt-5-chat-latest
|
||||
gpt-5-codex
|
||||
gpt-5-mini
|
||||
gpt-5-mini-2025-08-07
|
||||
gpt-5-nano
|
||||
gpt-5-nano-2025-08-07
|
||||
gpt-5-pro
|
||||
gpt-5-pro-2025-10-06
|
||||
gpt-5-search-api
|
||||
gpt-5-search-api-2025-10-14
|
||||
gpt-5.1
|
||||
gpt-5.1-2025-11-13
|
||||
gpt-5.1-chat-latest
|
||||
gpt-5.1-codex
|
||||
gpt-5.1-codex-max
|
||||
gpt-5.1-codex-mini
|
||||
gpt-5.2
|
||||
gpt-5.2-2025-12-11
|
||||
gpt-5.2-chat-latest
|
||||
gpt-5.2-codex
|
||||
gpt-5.2-pro
|
||||
gpt-5.2-pro-2025-12-11
|
||||
gpt-5.3-chat-latest
|
||||
gpt-5.3-codex
|
||||
gpt-5.4
|
||||
gpt-5.4-2026-03-05
|
||||
gpt-5.4-mini
|
||||
gpt-5.4-mini-2026-03-17
|
||||
gpt-5.4-nano
|
||||
gpt-5.4-nano-2026-03-17
|
||||
gpt-5.4-pro
|
||||
gpt-5.4-pro-2026-03-05
|
||||
gpt-audio
|
||||
gpt-audio-1.5
|
||||
gpt-audio-2025-08-28
|
||||
gpt-audio-mini
|
||||
gpt-audio-mini-2025-10-06
|
||||
gpt-audio-mini-2025-12-15
|
||||
gpt-image-1
|
||||
gpt-image-1-mini
|
||||
gpt-image-1.5
|
||||
gpt-realtime
|
||||
gpt-realtime-1.5
|
||||
gpt-realtime-2025-08-28
|
||||
gpt-realtime-mini
|
||||
gpt-realtime-mini-2025-10-06
|
||||
gpt-realtime-mini-2025-12-15
|
||||
o1
|
||||
o1-2024-12-17
|
||||
o1-pro
|
||||
o1-pro-2025-03-19
|
||||
o3
|
||||
o3-2025-04-16
|
||||
o3-mini
|
||||
o3-mini-2025-01-31
|
||||
o4-mini
|
||||
o4-mini-2025-04-16
|
||||
o4-mini-deep-research
|
||||
o4-mini-deep-research-2025-06-26
|
||||
omni-moderation-2024-09-26
|
||||
omni-moderation-latest
|
||||
sora-2
|
||||
sora-2-pro
|
||||
text-embedding-3-large
|
||||
text-embedding-3-small
|
||||
text-embedding-ada-002
|
||||
tts-1
|
||||
tts-1-1106
|
||||
tts-1-hd
|
||||
tts-1-hd-1106
|
||||
whisper-1
|
||||
-----
|
||||
|
||||
[35m[1m[22m[39m
|
||||
[35m[1m Deepseek models:[22m[39m
|
||||
[35m[1m[22m[39m
|
||||
deepseek-chat
|
||||
deepseek-reasoner
|
||||
-----
|
||||
*/
|
||||
model?: string | undefined;
|
||||
/** Router to use: openai, openrouter or deepseek */
|
||||
router?: string;
|
||||
/** Chat completion mode:
|
||||
completion, tools, assistant.
|
||||
[32m[1mcompletion[22m[39m: no support for tools, please use --dst parameter to save the output.
|
||||
[32m[1mtools[22m[39m: allows for tools to be used, eg 'save to ./output.md'. Not all models support this mode.
|
||||
[32m[1mresponses[22m[39m: allows for responses to be used, eg 'save to ./output.md'. Not all models support this mode.
|
||||
[32m[1massistant[22m[39m: : allows documents (PDF, DOCX, ...) to be added but dont support tools. Use --dst to save the output. Supported files :
|
||||
[32m[1mcustom[22m[39m: custom mode
|
||||
*/
|
||||
mode?: "completion" | "tools" | "assistant" | "responses" | "custom";
|
||||
/** Logging level for the application */
|
||||
logLevel?: number;
|
||||
/** Path to profile for variables. Supports environment variables. */
|
||||
profile?: string | undefined;
|
||||
/** Base URL for the API, set via --router or directly */
|
||||
baseURL?: string | undefined;
|
||||
/** Path to JSON configuration file (API keys). Supports environment variables. */
|
||||
config?: string | undefined;
|
||||
/** Create a script */
|
||||
dump?: string | undefined;
|
||||
/** Path to preferences file, eg: location, your email address, gender, etc. Supports environment variables. */
|
||||
preferences?: string;
|
||||
/** Logging directory */
|
||||
logs?: string;
|
||||
/** Enable streaming (verbose LLM output) */
|
||||
stream?: boolean;
|
||||
/** Use alternate tokenizer & instead of $ */
|
||||
alt?: boolean;
|
||||
/** Environment (in profile) */
|
||||
env?: string;
|
||||
variables?: {
|
||||
[x: string]: string;
|
||||
};
|
||||
/** List of filters to apply to the output.
|
||||
Used only in completion mode and a given output file specified with --dst.
|
||||
It unwraps by default any code or data in Markdown.
|
||||
Choices:
|
||||
JSON,JSONUnescape,JSONPretty,AlphaSort,code,JSONParse,trim,markdown
|
||||
*/
|
||||
filters?: (string | ("JSON" | "JSONUnescape" | "JSONPretty" | "AlphaSort" | "code" | "JSONParse" | "trim" | "markdown")[] | string[] | ((...args_0: unknown[]) => unknown)[]);
|
||||
/** JSONPath query to be used to transform input objects */
|
||||
query?: (string | null);
|
||||
/** Dry run - only write out parameters without making API calls */
|
||||
dry?: (boolean | string);
|
||||
/** Format for structured outputs. Can be a Zod schema, a Zod schema string, a JSON schema string, or a path to a JSON file. */
|
||||
format?: (string | any) | undefined;
|
||||
}
|
||||
75
packages/kbot/src/ai-tools/zod_schemas.ts
Normal file
75
packages/kbot/src/ai-tools/zod_schemas.ts
Normal file
@ -0,0 +1,75 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
/** Schema for listing files in a directory */
|
||||
export const FileListingOptionsSchema = z.object({
|
||||
directory: z.string().describe('Directory path to list files from'),
|
||||
pattern: z.string().optional().describe('Glob pattern for filtering files')
|
||||
}).describe('IFileListingOptions')
|
||||
|
||||
/** Schema for file removal operations */
|
||||
export const FileRemovalOptionsSchema = z.object({
|
||||
path: z.string().describe('Path of the file to remove')
|
||||
}).describe('IFileRemovalOptions');
|
||||
|
||||
/** Schema for git commit operations */
|
||||
export const GitCommitSchema = z.object({
|
||||
files: z.array(z.string()).describe('Files to commit'),
|
||||
message: z.string().describe('Commit message')
|
||||
}).describe('IGitCommitOptions');
|
||||
|
||||
/** Schema for git revert operations */
|
||||
export const GitRevertSchema = z.object({
|
||||
files: z.array(z.string()).describe('Files to revert')
|
||||
}).describe('IGitRevertOptions');
|
||||
|
||||
/** Schema for git version switch operations */
|
||||
export const GitSwitchVersionSchema = z.object({
|
||||
branch: z.string().describe('Branch name to switch to'),
|
||||
remote: z.string().default('origin').describe('Remote name')
|
||||
}).describe('IGitSwitchVersionOptions');
|
||||
|
||||
/** Schema for git raw file retrieval */
|
||||
export const GitRawFileSchema = z.object({
|
||||
url: z.string().optional().describe('Full GitHub raw URL'),
|
||||
repo: z.string().optional().describe('Repository in format owner/repo'),
|
||||
path: z.string().optional().describe('File path within repository')
|
||||
}).refine(
|
||||
data => (data.url) || (data.repo && data.path),
|
||||
'Either url or both repo and path must be provided'
|
||||
).describe('IGitRawFileOptions');
|
||||
|
||||
/** Schema for npm run command */
|
||||
export const NpmRunSchema = z.object({
|
||||
command: z.string().describe('Command to run (e.g. install, test, etc)'),
|
||||
args: z.array(z.string()).optional().describe('Additional arguments for the command')
|
||||
}).describe('INpmRunOptions');
|
||||
|
||||
/** Schema for terminal command execution */
|
||||
export const TerminalCommandSchema = z.object({
|
||||
command: z.string().describe('Command to execute'),
|
||||
args: z.array(z.string()).optional().describe('Command arguments'),
|
||||
cwd: z.string().optional().describe('Working directory for command execution'),
|
||||
background: z.boolean().optional().describe('Run command in background (non-blocking)'),
|
||||
window: z.boolean().optional().describe('Open command in new terminal window'),
|
||||
detached: z.boolean().optional().describe('Run process detached from parent')
|
||||
}).describe('ITerminalCommandOptions');
|
||||
|
||||
/** Schema for tool invocation parameters */
|
||||
export const InvokeToolSchema = z.object({
|
||||
tools: z.string().describe('Tool category to use (fs, npm, git, terminal)'),
|
||||
function: z.string().describe('Function name to invoke'),
|
||||
target: z.string().default(process.cwd()).describe('Target directory'),
|
||||
params: z.string().optional().describe('JSON string of parameters'),
|
||||
output: z.string().optional().describe('Path to write the output to'),
|
||||
env_key: z.string().optional().describe('Environment configuration key')
|
||||
}).describe('IInvokeToolOptions');
|
||||
|
||||
/** Schema for list command options */
|
||||
export const ListCommandSchema = z.object({
|
||||
output: z.string().default("./llm-tools.json").describe('Output file path for tools list')
|
||||
}).describe('IListCommandOptions');
|
||||
|
||||
/** Schema for tool listing options */
|
||||
export const ToolListingOptionsSchema = z.object({
|
||||
output: z.string().default('./llm-tools.json').describe('Path to write the output to')
|
||||
}).describe('IToolListingOptions');
|
||||
68
packages/kbot/src/ai-tools/zod_types.ts
Normal file
68
packages/kbot/src/ai-tools/zod_types.ts
Normal file
@ -0,0 +1,68 @@
|
||||
export interface IFileListingOptions {
|
||||
/** Directory path to list files from */
|
||||
directory: string;
|
||||
/** Glob pattern for filtering files */
|
||||
pattern?: string | undefined;
|
||||
}
|
||||
export interface IFileRemovalOptions {
|
||||
/** Path of the file to remove */
|
||||
path: string;
|
||||
}
|
||||
export interface IGitCommitOptions {
|
||||
/** Files to commit */
|
||||
files: string[];
|
||||
/** Commit message */
|
||||
message: string;
|
||||
}
|
||||
export interface IGitRevertOptions {
|
||||
/** Files to revert */
|
||||
files: string[];
|
||||
}
|
||||
export interface IGitSwitchVersionOptions {
|
||||
/** Branch name to switch to */
|
||||
branch: string;
|
||||
/** Remote name */
|
||||
remote: string;
|
||||
}
|
||||
export interface IInvokeToolOptions {
|
||||
/** Tool category to use (fs, npm, git, terminal) */
|
||||
tools: string;
|
||||
/** Function name to invoke */
|
||||
function: string;
|
||||
/** Target directory */
|
||||
target: string;
|
||||
/** JSON string of parameters */
|
||||
params?: string | undefined;
|
||||
/** Path to write the output to */
|
||||
output?: string | undefined;
|
||||
/** Environment configuration key */
|
||||
env_key?: string | undefined;
|
||||
}
|
||||
export interface IToolListingOptions {
|
||||
/** Path to write the output to */
|
||||
output: string;
|
||||
}
|
||||
export interface ITerminalCommandOptions {
|
||||
/** Command to execute */
|
||||
command: string;
|
||||
/** Command arguments */
|
||||
args?: string[] | undefined;
|
||||
/** Working directory for command execution */
|
||||
cwd?: string | undefined;
|
||||
/** Run command in background (non-blocking) */
|
||||
background?: boolean | undefined;
|
||||
/** Open command in new terminal window */
|
||||
window?: boolean | undefined;
|
||||
/** Run process detached from parent */
|
||||
detached?: boolean | undefined;
|
||||
}
|
||||
export interface IListCommandOptions {
|
||||
/** Output file path for tools list */
|
||||
output: string;
|
||||
}
|
||||
export interface INpmRunOptions {
|
||||
/** Command to run (e.g. install, test, etc) */
|
||||
command: string;
|
||||
/** Additional arguments for the command */
|
||||
args?: string[] | undefined;
|
||||
}
|
||||
251
packages/kbot/tests/unit/ollama-basics.test.ts
Normal file
251
packages/kbot/tests/unit/ollama-basics.test.ts
Normal file
@ -0,0 +1,251 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { sync as exists } from "@polymech/fs/exists"
|
||||
import { z } from 'zod'
|
||||
|
||||
import {
|
||||
TEST_TIMEOUT,
|
||||
TestResult,
|
||||
runTest,
|
||||
generateTestReport,
|
||||
getReportPaths
|
||||
} from './commons'
|
||||
import { zodFunction } from '../../src/ai-tools/lib/tools/index.js'
|
||||
|
||||
const models = ['qwen2.5:3b']
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Mock tool implementations
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const addTool = zodFunction({
|
||||
name: 'add',
|
||||
description: 'Add two numbers together and return the sum.',
|
||||
schema: z.object({
|
||||
a: z.number().describe('First number'),
|
||||
b: z.number().describe('Second number'),
|
||||
}),
|
||||
function: async ({ a, b }) => ({ result: a + b }),
|
||||
})
|
||||
|
||||
const multiplyTool = zodFunction({
|
||||
name: 'multiply',
|
||||
description: 'Multiply two numbers and return the product.',
|
||||
schema: z.object({
|
||||
a: z.number().describe('First number'),
|
||||
b: z.number().describe('Second number'),
|
||||
}),
|
||||
function: async ({ a, b }) => ({ result: a * b }),
|
||||
})
|
||||
|
||||
const getWeatherTool = zodFunction({
|
||||
name: 'get_weather',
|
||||
description: 'Get the current weather for a city. Returns temperature in Celsius and a condition string.',
|
||||
schema: z.object({
|
||||
city: z.string().describe('The city name to get weather for'),
|
||||
}),
|
||||
function: async ({ city }) => ({
|
||||
city,
|
||||
temperature_c: 22,
|
||||
condition: 'sunny',
|
||||
}),
|
||||
})
|
||||
|
||||
const formatNumberTool = zodFunction({
|
||||
name: 'format_number',
|
||||
description: 'Format a number with thousand separators and optional decimal places.',
|
||||
schema: z.object({
|
||||
value: z.number().describe('The number to format'),
|
||||
decimals: z.number().optional().describe('Number of decimal places (default 2)'),
|
||||
}),
|
||||
function: async ({ value, decimals = 2 }) => ({
|
||||
formatted: value.toLocaleString('en-US', { minimumFractionDigits: decimals, maximumFractionDigits: decimals }),
|
||||
}),
|
||||
})
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Basic Ollama Operations
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('Ollama Basic Operations', () => {
|
||||
let testResults: TestResult[] = []
|
||||
const TEST_LOG_PATH = getReportPaths('ollama-basics', 'json')
|
||||
const TEST_REPORT_PATH = getReportPaths('ollama-basics', 'md')
|
||||
|
||||
it.each(models)('should add two numbers with model %s', async (modelName) => {
|
||||
const result = await runTest(
|
||||
'add 5 and 3. Return only the number, no explanation.',
|
||||
'8',
|
||||
'addition',
|
||||
modelName,
|
||||
TEST_LOG_PATH,
|
||||
'completion',
|
||||
{ router: 'ollama' }
|
||||
)
|
||||
testResults.push(result)
|
||||
expect(result.result[0]?.trim()?.toLowerCase()).toEqual('8')
|
||||
}, { timeout: TEST_TIMEOUT })
|
||||
|
||||
it.each(models)('should multiply two numbers with model %s', async (modelName) => {
|
||||
const result = await runTest(
|
||||
'multiply 8 and 3. Return only the number, no explanation.',
|
||||
'24',
|
||||
'multiplication',
|
||||
modelName,
|
||||
TEST_LOG_PATH,
|
||||
'completion',
|
||||
{ router: 'ollama' }
|
||||
)
|
||||
testResults.push(result)
|
||||
expect(result.result[0]?.trim()?.toLowerCase()).toEqual('24')
|
||||
}, { timeout: TEST_TIMEOUT })
|
||||
|
||||
it.each(models)('should divide two numbers with model %s', async (modelName) => {
|
||||
const result = await runTest(
|
||||
'divide 15 by 3. Return only the number, no explanation.',
|
||||
'5',
|
||||
'division',
|
||||
modelName,
|
||||
TEST_LOG_PATH,
|
||||
'completion',
|
||||
{ router: 'ollama' }
|
||||
)
|
||||
testResults.push(result)
|
||||
expect(result.result[0]?.trim()?.toLowerCase()).toEqual('5')
|
||||
}, { timeout: TEST_TIMEOUT })
|
||||
|
||||
it('should generate markdown report', () => {
|
||||
generateTestReport(testResults, 'Ollama Basic Operations Test Results', TEST_REPORT_PATH)
|
||||
expect(exists(TEST_REPORT_PATH) === 'file').toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Custom Tool Call Quality
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('Ollama Custom Tool Call Quality', () => {
|
||||
let testResults: TestResult[] = []
|
||||
const TEST_LOG_PATH = getReportPaths('ollama-tools', 'json')
|
||||
const TEST_REPORT_PATH = getReportPaths('ollama-tools', 'md')
|
||||
|
||||
it.each(models)(
|
||||
'should call add tool and return correct sum [%s]',
|
||||
async (modelName) => {
|
||||
const result = await runTest(
|
||||
'Use the add tool to add 17 and 25. Report back the result.',
|
||||
'42',
|
||||
'tool-add',
|
||||
modelName,
|
||||
TEST_LOG_PATH,
|
||||
'tools',
|
||||
{
|
||||
router: 'ollama',
|
||||
customTools: [addTool],
|
||||
equalityCheck: 'llm_equal',
|
||||
}
|
||||
)
|
||||
testResults.push(result)
|
||||
// Result must contain 42
|
||||
expect(result.result[0]).toMatch(/42/)
|
||||
},
|
||||
{ timeout: TEST_TIMEOUT }
|
||||
)
|
||||
|
||||
it.each(models)(
|
||||
'should call multiply tool and return correct product [%s]',
|
||||
async (modelName) => {
|
||||
const result = await runTest(
|
||||
'Use the multiply tool to compute 6 times 7. Tell me the answer.',
|
||||
'42',
|
||||
'tool-multiply',
|
||||
modelName,
|
||||
TEST_LOG_PATH,
|
||||
'tools',
|
||||
{
|
||||
router: 'ollama',
|
||||
customTools: [multiplyTool],
|
||||
equalityCheck: 'llm_equal',
|
||||
}
|
||||
)
|
||||
testResults.push(result)
|
||||
expect(result.result[0]).toMatch(/42/)
|
||||
},
|
||||
{ timeout: TEST_TIMEOUT }
|
||||
)
|
||||
|
||||
it.each(models)(
|
||||
'should call get_weather tool with correct city argument [%s]',
|
||||
async (modelName) => {
|
||||
const result = await runTest(
|
||||
"What's the weather like in Paris? Use the get_weather tool.",
|
||||
'sunny',
|
||||
'tool-weather',
|
||||
modelName,
|
||||
TEST_LOG_PATH,
|
||||
'tools',
|
||||
{
|
||||
router: 'ollama',
|
||||
customTools: [getWeatherTool],
|
||||
equalityCheck: 'llm_equal',
|
||||
}
|
||||
)
|
||||
testResults.push(result)
|
||||
// Response must mention the mocked condition "sunny" and/or 22°C
|
||||
const lower = result.result[0]?.toLowerCase() ?? ''
|
||||
expect(lower).toMatch(/sunny|22/)
|
||||
},
|
||||
{ timeout: TEST_TIMEOUT }
|
||||
)
|
||||
|
||||
it.each(models)(
|
||||
'should select the correct tool from multiple available tools [%s]',
|
||||
async (modelName) => {
|
||||
const result = await runTest(
|
||||
'Use the appropriate tool to add 100 and 200.',
|
||||
'300',
|
||||
'tool-selection',
|
||||
modelName,
|
||||
TEST_LOG_PATH,
|
||||
'tools',
|
||||
{
|
||||
router: 'ollama',
|
||||
// Both tools available — model must pick add, not multiply
|
||||
customTools: [addTool, multiplyTool, getWeatherTool],
|
||||
equalityCheck: 'llm_equal',
|
||||
}
|
||||
)
|
||||
testResults.push(result)
|
||||
expect(result.result[0]).toMatch(/300/)
|
||||
},
|
||||
{ timeout: TEST_TIMEOUT }
|
||||
)
|
||||
|
||||
it.each(models)(
|
||||
'should chain two tool calls: multiply then format [%s]',
|
||||
async (modelName) => {
|
||||
const result = await runTest(
|
||||
'First multiply 123 by 456, then format the result with 2 decimal places.',
|
||||
'56,088.00',
|
||||
'tool-chain',
|
||||
modelName,
|
||||
TEST_LOG_PATH,
|
||||
'tools',
|
||||
{
|
||||
router: 'ollama',
|
||||
customTools: [multiplyTool, formatNumberTool],
|
||||
equalityCheck: 'llm_equal',
|
||||
}
|
||||
)
|
||||
testResults.push(result)
|
||||
// 123 * 456 = 56088 → formatted as 56,088.00
|
||||
expect(result.result[0]).toMatch(/56[,.]?088/)
|
||||
},
|
||||
{ timeout: TEST_TIMEOUT }
|
||||
)
|
||||
|
||||
it('should generate tool quality markdown report', () => {
|
||||
generateTestReport(testResults, 'Ollama Custom Tool Call Quality Results', TEST_REPORT_PATH)
|
||||
expect(exists(TEST_REPORT_PATH) === 'file').toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
79
packages/kbot/tests/unit/reports/ollama-basics.json
Normal file
79
packages/kbot/tests/unit/reports/ollama-basics.json
Normal file
@ -0,0 +1,79 @@
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"test": "addition",
|
||||
"prompt": "add 5 and 3. Return only the number, no explanation.",
|
||||
"result": [
|
||||
"8"
|
||||
],
|
||||
"expected": "8",
|
||||
"model": "qwen2.5:3b",
|
||||
"router": "qwen2.5:3b",
|
||||
"timestamp": "2026-03-19T15:42:19.097Z",
|
||||
"passed": true,
|
||||
"duration": 738,
|
||||
"category": "ollama-basics"
|
||||
},
|
||||
{
|
||||
"test": "multiplication",
|
||||
"prompt": "multiply 8 and 3. Return only the number, no explanation.",
|
||||
"result": [
|
||||
"24"
|
||||
],
|
||||
"expected": "24",
|
||||
"model": "qwen2.5:3b",
|
||||
"router": "qwen2.5:3b",
|
||||
"timestamp": "2026-03-19T15:42:19.848Z",
|
||||
"passed": true,
|
||||
"duration": 745,
|
||||
"category": "ollama-basics"
|
||||
},
|
||||
{
|
||||
"test": "division",
|
||||
"prompt": "divide 15 by 3. Return only the number, no explanation.",
|
||||
"result": [
|
||||
"5"
|
||||
],
|
||||
"expected": "5",
|
||||
"model": "qwen2.5:3b",
|
||||
"router": "qwen2.5:3b",
|
||||
"timestamp": "2026-03-19T15:42:20.529Z",
|
||||
"passed": true,
|
||||
"duration": 677,
|
||||
"category": "ollama-basics"
|
||||
}
|
||||
],
|
||||
"highscores": [
|
||||
{
|
||||
"test": "addition",
|
||||
"rankings": [
|
||||
{
|
||||
"model": "qwen2.5:3b",
|
||||
"duration": 738,
|
||||
"duration_secs": 0.738
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"test": "multiplication",
|
||||
"rankings": [
|
||||
{
|
||||
"model": "qwen2.5:3b",
|
||||
"duration": 745,
|
||||
"duration_secs": 0.745
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"test": "division",
|
||||
"rankings": [
|
||||
{
|
||||
"model": "qwen2.5:3b",
|
||||
"duration": 677,
|
||||
"duration_secs": 0.677
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"lastUpdated": "2026-03-19T15:42:20.529Z"
|
||||
}
|
||||
50
packages/kbot/tests/unit/reports/ollama-basics.md
Normal file
50
packages/kbot/tests/unit/reports/ollama-basics.md
Normal file
@ -0,0 +1,50 @@
|
||||
# Ollama Basic Operations Test Results
|
||||
|
||||
## Highscores
|
||||
|
||||
### Performance Rankings (Duration)
|
||||
|
||||
| Test | Model | Duration (ms) | Duration (s) |
|
||||
|------|-------|--------------|--------------|
|
||||
| addition | qwen2.5:3b | 738 | 0.74 |
|
||||
| multiplication | qwen2.5:3b | 745 | 0.74 |
|
||||
| division | qwen2.5:3b | 677 | 0.68 |
|
||||
|
||||
## Summary
|
||||
|
||||
- Total Tests: 3
|
||||
- Passed: 3
|
||||
- Failed: 0
|
||||
- Success Rate: 100.00%
|
||||
- Average Duration: 720ms (0.72s)
|
||||
|
||||
## Failed Tests
|
||||
|
||||
*No failed tests*
|
||||
|
||||
## Passed Tests
|
||||
|
||||
### addition - qwen2.5:3b
|
||||
|
||||
- Prompt: `add 5 and 3. Return only the number, no explanation.`
|
||||
- Expected: `8`
|
||||
- Actual: `8`
|
||||
- Duration: 738ms (0.74s)
|
||||
- Timestamp: 3/19/2026, 4:42:19 PM
|
||||
|
||||
### multiplication - qwen2.5:3b
|
||||
|
||||
- Prompt: `multiply 8 and 3. Return only the number, no explanation.`
|
||||
- Expected: `24`
|
||||
- Actual: `24`
|
||||
- Duration: 745ms (0.74s)
|
||||
- Timestamp: 3/19/2026, 4:42:19 PM
|
||||
|
||||
### division - qwen2.5:3b
|
||||
|
||||
- Prompt: `divide 15 by 3. Return only the number, no explanation.`
|
||||
- Expected: `5`
|
||||
- Actual: `5`
|
||||
- Duration: 677ms (0.68s)
|
||||
- Timestamp: 3/19/2026, 4:42:20 PM
|
||||
|
||||
1386
packages/tm/package-lock.json
generated
Normal file
1386
packages/tm/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
16
packages/tm/package.json
Normal file
16
packages/tm/package.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "tm",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"type": "commonjs",
|
||||
"dependencies": {
|
||||
"@plastichub/kbot": "^1.1.23"
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user