Skip to content

Commit

Permalink
✨ feat(docs): add file read tool, rename copy tool
Browse files Browse the repository at this point in the history
  • Loading branch information
pelikhan committed Oct 18, 2024
1 parent 3f88e8e commit 5fa5df2
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 22 deletions.
3 changes: 2 additions & 1 deletion docs/src/components/BuiltinTools.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ import { LinkCard } from '@astrojs/starlight/components';
<LinkCard title="meta_schema" description="Generate a valid JSON schema for the described JSON. Source https://platform.openai.com/docs/guides/prompt-generation?context=structured-output-schema." href="/genaiscript/reference/scripts/system#systemmeta_schema" />
<LinkCard title="node_test" description="build and test current project using `npm test`" href="/genaiscript/reference/scripts/system#systemnode_test" />
<LinkCard title="python_code_interpreter_run" description="Executes python 3.12 code for Data Analysis tasks in a docker container. The process output is returned. Do not generate visualizations. The only packages available are numpy, pandas, scipy. There is NO network connectivity. Do not attempt to install other packages or make web requests. You must copy all the necessary files or pass all the data because the python code runs in a separate container." href="/genaiscript/reference/scripts/system#systempython_code_interpreter" />
<LinkCard title="python_code_interpreter_copy_files" description="Copy files from the host file system to the container file system. NO absolute paths. Returns the path of each file copied in the container." href="/genaiscript/reference/scripts/system#systempython_code_interpreter" />
<LinkCard title="python_code_interpreter_copy_files_to_container" description="Copy files from the host file system to the container file system. NO absolute paths. Returns the path of each file copied in the container." href="/genaiscript/reference/scripts/system#systempython_code_interpreter" />
<LinkCard title="python_code_interpreter_read_file" description="Reads a file from the container file system. No absolute paths." href="/genaiscript/reference/scripts/system#systempython_code_interpreter" />
<LinkCard title="retrieval_fuzz_search" description="Search for keywords using the full text of files and a fuzzy distance." href="/genaiscript/reference/scripts/system#systemretrieval_fuzz_search" />
<LinkCard title="retrieval_vector_search" description="Search files using embeddings and similarity distance." href="/genaiscript/reference/scripts/system#systemretrieval_vector_search" />
<LinkCard title="retrieval_web_search" description="Search the web for a user query using Bing Search." href="/genaiscript/reference/scripts/system#systemretrieval_web_search" />
Expand Down
32 changes: 29 additions & 3 deletions docs/src/content/docs/reference/scripts/system.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2601,7 +2601,8 @@ Python Dockerized code execution for data analysis
- tool `python_code_interpreter_run`: Executes python 3.12 code for Data Analysis tasks in a docker container. The process output is returned. Do not generate visualizations. The only packages available are numpy, pandas, scipy. There is NO network connectivity. Do not attempt to install other packages or make web requests. You must copy all the necessary files or pass all the data because the python code runs in a separate container.
- tool `python_code_interpreter_copy_files`: Copy files from the host file system to the container file system. NO absolute paths. Returns the path of each file copied in the container.
- tool `python_code_interpreter_copy_files_to_container`: Copy files from the host file system to the container file system. NO absolute paths. Returns the path of each file copied in the container.
- tool `python_code_interpreter_read_file`: Reads a file from the container file system. No absolute paths.
`````js wrap title="system.python_code_interpreter"
system({
Expand Down Expand Up @@ -2646,7 +2647,7 @@ defTool(
)
defTool(
"python_code_interpreter_copy_files",
"python_code_interpreter_copy_files_to_container",
"Copy files from the host file system to the container file system. NO absolute paths. Returns the path of each file copied in the container.",
{
type: "object",
Expand All @@ -2657,7 +2658,8 @@ defTool(
},
toFolder: {
type: "string",
description: "Container directory path. Not a filename.",
description:
"Container directory path. Default is '.' Not a filename.",
},
},
required: ["from"],
Expand All @@ -2669,6 +2671,30 @@ defTool(
const res = await container.scheduler.add(
async () => await container.copyTo(from, toFolder)
)
return res.join("\n")
}
)
defTool(
"python_code_interpreter_read_file",
"Reads a file from the container file system. No absolute paths.",
{
type: "object",
properties: {
filename: {
type: "string",
description: "Container file path",
},
},
required: ["filename"],
},
async (args) => {
const { context, filename } = args
context.log(`python: cat ${filename}`)
const container = await getContainer()
const res = await container.scheduler.add(
async () => await container.readText(filename)
)
return res
}
)
Expand Down
48 changes: 32 additions & 16 deletions packages/cli/src/docker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { isQuiet } from "./log"
import Dockerode, { Container } from "dockerode"
import { shellParse, shellQuote } from "../../core/src/shell"
import { PLimitPromiseQueue } from "../../core/src/concurrency"
import { delay } from "es-toolkit"

type DockerodeType = import("dockerode")

Expand Down Expand Up @@ -145,7 +146,9 @@ export class DockerManager {
filters,
})
const info = containers?.[0]
if (info) return this._docker.getContainer(info.Id)
if (info) {
return this._docker.getContainer(info.Id)
}
} catch {}
return undefined
}
Expand Down Expand Up @@ -173,13 +176,14 @@ export class DockerManager {
hostPath
)
this.containers.push(c)
logVerbose(`container: resuming ${name}`)
await c.resume()
const st = await container.inspect()
if (st.State?.Status !== "running") {
logVerbose(`container: start failed`)
trace?.error(`container: start failed`)
const status = st.State?.Status
if (status !== "running") {
logVerbose(`container: start failed (${status})`)
trace?.error(`container: ${status}`)
}
logVerbose(`container: resuming ${name}`)
return c
}
}
Expand All @@ -203,7 +207,7 @@ export class DockerManager {
} = options
let name = (userName || image).replace(/[^a-zA-Z0-9]+/g, "_")
if (persistent)
name += `_${(await sha1string(JSON.stringify({ image, name, ports, env, networkEnabled, postCreateCommands }))).slice(0, 12)}`
name += `_${(await sha1string(JSON.stringify({ image, name, ports, env, networkEnabled, postCreateCommands, CORE_VERSION }))).slice(0, 12)}`
else name += `_${randomHex(6)}`
const hostPath = host.path.resolve(
dotGenaiscriptPath(DOCKER_VOLUMES_DIR, name)
Expand Down Expand Up @@ -324,16 +328,24 @@ export class DockerManager {
const res = /^\//.test(to)
? host.path.resolve(
hostPath,
DOCKER_CONTAINER_VOLUME,
to.replace(/^\//, "")
)
: host.path.resolve(hostPath, DOCKER_CONTAINER_VOLUME, to || "")
: host.path.resolve(hostPath, to || "")
return res
}

const resume: () => Promise<void> = async () => {
const state = await container.inspect()
if (state.State.Paused) await container.unpause()
let state = await container.inspect()
if (state.State.Status === "paused") await container.unpause()
else if (state.State.Status === "exited") {
await container.start()
} else if (state.State.Status === "restarting") {
let retry = 0
while (state.State.Restarting && retry++ < 5) {
await delay(1000)
state = await container.inspect()
}
}
}

const pause: () => Promise<void> = async () => {
Expand Down Expand Up @@ -363,17 +375,21 @@ export class DockerManager {
}

const { cwd: userCwd, label } = options || {}
const cwd = userCwd
? resolveContainerPath(userCwd)
: "/" + DOCKER_CONTAINER_VOLUME
const cwd = "/" + host.path.join(DOCKER_CONTAINER_VOLUME, userCwd || ".")

try {
trace?.startDetails(`📦 ▶️ container exec ${label || command}`)
trace?.startDetails(
`📦 ▶️ container exec: ${userCwd || ""}> ${label || command}`
)
trace?.itemValue(`container`, container.id)
trace?.itemValue(`cwd`, cwd)
trace?.fence(`${command} ${shellQuote(args || [])}`, "sh")
trace?.fence(
`${cwd}> ${command} ${shellQuote(args || [])}`,
"sh"
)
if (!isQuiet)
logVerbose(
`container exec: ${shellQuote([command, ...args])}`
`container exec: ${userCwd || ""}> ${shellQuote([command, ...args])}`
)

let inspection = await container.inspect()
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ async function runToolCalls(
output = await tool.impl({ context, ...args })
} catch (e) {
logWarn(`tool: ${tool.spec.name} error`)
logError(e)
trace.error(`tool: ${tool.spec.name} error`, e)
output = errorMessage(e)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ defTool(
)

defTool(
"python_code_interpreter_copy_files",
"python_code_interpreter_copy_files_to_container",
"Copy files from the host file system to the container file system. NO absolute paths. Returns the path of each file copied in the container.",
{
type: "object",
Expand All @@ -51,7 +51,8 @@ defTool(
},
toFolder: {
type: "string",
description: "Container directory path. Not a filename.",
description:
"Container directory path. Default is '.' Not a filename.",
},
},
required: ["from"],
Expand All @@ -63,6 +64,30 @@ defTool(
const res = await container.scheduler.add(
async () => await container.copyTo(from, toFolder)
)
return res.join("\n")
}
)

defTool(
"python_code_interpreter_read_file",
"Reads a file from the container file system. No absolute paths.",
{
type: "object",
properties: {
filename: {
type: "string",
description: "Container file path",
},
},
required: ["filename"],
},
async (args) => {
const { context, filename } = args
context.log(`python: cat ${filename}`)
const container = await getContainer()
const res = await container.scheduler.add(
async () => await container.readText(filename)
)
return res
}
)

0 comments on commit 5fa5df2

Please sign in to comment.