UNPKG

@plastichub/osr-ai-tools

Version:

CLI and library for LLM tools

320 lines (318 loc) 13.5 kB
import * as path from 'path' import { RunnableToolFunction } from 'openai/lib/RunnableFunction' import { sync as rm } from '@plastichub/fs/remove' import { filesEx as glob } from '@plastichub/osr-commons/_glob' import { isString } from '@plastichub/core/primitives' import { sync as dir } from '@plastichub/fs/dir' import { sync as write } from '@plastichub/fs/write' import { sync as read } from '@plastichub/fs/read' import { sync as rename } from '@plastichub/fs/rename' import { sync as exists } from '@plastichub/fs/exists' import { filesEx } from '@plastichub/osr-commons/_glob' import { toolLogger } from '../..' import { IKBotTask } from '../../types' export const tools = (target: string, options: IKBotTask): Array<any> => { const logger = toolLogger('fs', options) const category = 'fs' 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, params.directory); if(!exists(directory)) { logger.debug(`Tool::ListFiles Directory ${directory} does not exist`); return [] } const pattern = params.pattern || '**/*'; logger.debug(`Tool::ListFiles Listing files in ${directory} with pattern ${pattern}`); return glob(directory, [ pattern ]); } 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, 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, params.src) logger.debug(`Tool::Rename file ${src} to ${params.dst}`) rename(src, params.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: "Modify existing project files", parameters: { type: "object", properties: { files: { type: "array", items: { type: "object", properties: { path: { type: "string" }, content: { type: "string" } }, 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) write(path.join(target, 'tools-output.json'), files) return error.message } } for (const file of files) { const filePath = path.join(target, file.path); logger.debug(`Tool:modify_project_files writing file ${filePath}`) await write(filePath, file.content) } } catch (error) { logger.error(`Error creating project structure`, error) } }, parse: JSON.parse, }, } as RunnableToolFunction<{ id: string }>, { type: 'function', function: { name: "create_file", description: "Creates a file, given a path and content", parameters: { type: "object", properties: { file: { type: "object", properties: { path: { type: "string" }, content: { type: "string" } } } }, required: ["file"], }, function: async (params) => { params = await options.collector.onToolBefore(category, 'create_file', params) || params debugger try { if (isString(params)) { try { params = JSON.parse(params) } catch (error: any) { logger.error(`Tool::create_file : Structure Error parsing files`, error, params) return error.message } } const { file } = params as any if (!target || !file.path || !file.content) { logger.error(`Tool::create_file : Path and content are required to create file`, params) return } logger.debug(`Tool::create_file Writing file ${file.path} in ${target}`) const filePath = path.join(target, file.path) write(filePath, file.content) return true } catch (error) { logger.error(`Tool:create_file Error writing file`, error) return false } }, 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 to `, ret) return } const filePath = path.join(target, file.path) 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 filePath = path.join(target, file.path) 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 }> ] };