Skip to content

Commit

Permalink
chore: update
Browse files Browse the repository at this point in the history
  • Loading branch information
grabbou committed Dec 9, 2024
1 parent 9c77c8c commit c8bdd62
Show file tree
Hide file tree
Showing 7 changed files with 212 additions and 185 deletions.
1 change: 1 addition & 0 deletions example/assets/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
library.html
53 changes: 23 additions & 30 deletions example/src/library_photo_to_website.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,63 +5,56 @@ import { solution, workflow } from '@dead-simple-ai-agent/framework/workflow'
import { visionTool } from '@dead-simple-ai-agent/tools/vision'
import path from 'path'

import { listFiles, readFile, saveFile } from './tools/fsTools.js'
import { createFileSystemTools } from './tools/filesystem.js'

const workingDir = path.resolve(__dirname, '../assets/')

const { saveFile, readFile, listFilesFromDirectory } = createFileSystemTools({
workingDir,
})

const librarian = agent({
role: 'Librarian',
description: `
You are skilled at scanning and identifying books in the library.
You look at the book library and automatically list all the books including
Title and author saving the result to a JSON file.
When asked, you will analyze the photo of the library and list all the books that you see, in details.
`,
tools: {
visionTool,
saveFile: saveFile({
workingDir: path.resolve(__dirname, '../assets/'),
}),
},
})

const webmaster = agent({
role: 'HTML Webmaster',
description: `
You are skilled at creating HTML pages.
You can create a simple HTML page with the list of items (books etc)
from the JSON database.
You are good at finding and using templates for creating HTML pages.
You are scanning the folder looking for assets and then bunding it all together.
You are good at using templates for creating HTML pages.
You can analyze existing HTML page and replace the content with the new one.
`,
tools: {
saveFile: saveFile({
workingDir: path.resolve(__dirname, '../assets/'),
}),
readFile: readFile({
workingDir: path.resolve(__dirname, '../assets/'),
}),
listFiles: listFiles({
workingDir: path.resolve(__dirname, '../assets/'),
}),
saveFile,
readFile,
listFilesFromDirectory,
},
})

const imagePath = path.resolve(__dirname, '../assets/photo-library.jpg')
const imagePath = path.join(workingDir, 'photo-library.jpg')
const outputPath = path.join(workingDir, 'library.html')

const bookLibraryWorkflow = workflow({
members: [librarian, webmaster],
description: `
Analyze the photo located in '${imagePath}' and list ALL the books.
Your working directory is: '${path.resolve(__dirname, '../assets/')}'.
Use absolute paths for tool calls - within this directory.
There is about 10 books in the photo.
Create a JSON database with the list of books and save it to file.
Create a simple HTML page based on a *.html template found in the directory.
Save the result to HTML file.
Generate a website that lists all the books in the library.
The photo of books in the library is in the "${imagePath}" file.
Important information:
- a HTML template is somewhere in the folder '.'
- the photo of books is located in the './assets/photo-library
- All available templates are in "${workingDir}" directory. Find the best template to use.
- You only have access to files in "${workingDir}" directory.
- Use absolute paths for tool calls.
`,
output: `
HTML page saved under 'library.html' with the list of books from the library.
Create a new HTML page in "${outputPath}" directory, based on the best template you found.
`,
snapshot: logger,
})
Expand Down
100 changes: 100 additions & 0 deletions example/src/tools/filesystem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { mkdir, readdir, readFile, writeFile } from 'node:fs/promises'
import path from 'node:path'

import { tool } from '@dead-simple-ai-agent/framework/tool'
import { z } from 'zod'

interface FileSystemOptions {
workingDir: string
}

const sanitizePath = (workingDir: string, userPath: string): string => {
const resolvedPath = path.resolve(workingDir, userPath)
if (!resolvedPath.startsWith(workingDir)) {
throw new Error('Path is outside the working directory.')
}
return resolvedPath
}

export const createFileSystemTools = ({ workingDir }: FileSystemOptions) => {
return {
listFilesFromDirectory: tool({
description: 'List all files in a directory',
parameters: z.object({
path: z.string().describe('Absolute path to directory to list files from'),
}),
execute: async ({ path: userPath }) => {
const dirPath = sanitizePath(workingDir, userPath)
return (await readdir(dirPath)).join('\n')
},
}),
currentDirectory: tool({
description: 'Get the current working directory',
parameters: z.object({}),
execute: async () => {
return workingDir
},
}),
makeDirectory: tool({
description: 'Create a new directory',
parameters: z.object({ path: z.string().describe('Absolute path to directory to create') }),
execute: async ({ path: userPath }) => {
const dirPath = sanitizePath(workingDir, userPath)
await mkdir(dirPath)
return dirPath
},
}),
readFile: tool({
description: 'Read a file at a given path',
parameters: z.object({
path: z.string().describe('Absolute path to file to read'),
is_image: z.boolean().describe('Specify if the file is an image'),
encoding: fileEncodingSchema
.describe('Encoding format for reading the file')
.default('utf-8'),
}),
execute: async ({ path: userPath, is_image, encoding }) => {
const filePath = sanitizePath(workingDir, userPath)
const file = await readFile(filePath, { encoding })
if (is_image) {
return `data:image/${path.extname(filePath).toLowerCase().replace('.', '')};base64,${Buffer.from(
file
).toString('base64')}`
} else {
return file
}
},
}),
saveFile: tool({
description: 'Save a file at a given path',
parameters: z.object({
path: z.string().describe('Absolute path to file to save to'),
content: z.string().describe('Content to save in the file'),
encoding: fileEncodingSchema
.describe('Encoding format for saving the file')
.default('utf-8'),
}),
execute: async ({ path: userPath, content, encoding }) => {
const filePath = sanitizePath(workingDir, userPath)
await writeFile(filePath, content, { encoding })
return `File saved successfully: ${filePath}`
},
}),
}
}

const fileEncodingSchema = z
.enum([
'ascii',
'utf8',
'utf-8',
'utf16le',
'ucs2',
'ucs-2',
'base64',
'base64url',
'latin1',
'binary',
'hex',
])
.default('utf-8')
101 changes: 0 additions & 101 deletions example/src/tools/fsTools.ts

This file was deleted.

5 changes: 2 additions & 3 deletions packages/framework/src/supervisor/nextTick.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,12 @@ export async function nextTick(workflow: Workflow, state: WorkflowState): Promis
*
* If further processing is required, we will carry `agentRequest` over to the next iteration.
*/
const [agentResponse, status] = await runAgent(agent, state.agentRequest)
const [agentResponse, status] = await runAgent(agent, state.messages, state.agentRequest)
if (status === 'complete') {
const agentFinalAnswer = agentResponse.at(-1)!
return {
...state,
status: 'idle',
messages: state.messages.concat(state.agentRequest[0], agentFinalAnswer),
messages: state.messages.concat(state.agentRequest[0], agentResponse),
}
}
return {
Expand Down
Loading

0 comments on commit c8bdd62

Please sign in to comment.