@plastichub/osr-ai-tools
Version:
CLI and library for LLM tools
320 lines (318 loc) • 13.5 kB
text/typescript
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 }>
]
};