mirror of
https://github.com/run-llama/tool.git
synced 2026-06-30 21:17:57 -04:00
init repo
This commit is contained in:
+132
@@ -0,0 +1,132 @@
|
||||
### Node template
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
.cache
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@llamaindex/tool": "workspace:*",
|
||||
"openai": "^4.33.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { getWeather } from './utils'
|
||||
|
||||
/**
|
||||
* Get current location
|
||||
*/
|
||||
export function getCurrentLocation() {
|
||||
console.log('Getting current location')
|
||||
return 'London'
|
||||
}
|
||||
|
||||
export {
|
||||
getWeather
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import * as Tools from './index.llama'
|
||||
import { getTools, registerTools } from '@llamaindex/tool'
|
||||
import { OpenAI } from 'openai'
|
||||
import { inspect } from 'node:util'
|
||||
import { OpenAIAgent } from 'llamaindex'
|
||||
|
||||
registerTools(Tools)
|
||||
const openai = new OpenAI()
|
||||
{
|
||||
const response = await openai.chat.completions.create({
|
||||
model: 'gpt-3.5-turbo',
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: 'What\'s my current weather?'
|
||||
}],
|
||||
tools: getTools('openai')
|
||||
})
|
||||
|
||||
console.log('response:',
|
||||
inspect(response, { depth: Number.MAX_VALUE, colors: true }))
|
||||
}
|
||||
{
|
||||
const response = await openai.chat.completions.create({
|
||||
model: 'gpt-3.5-turbo',
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: 'What\'s the weather in London?'
|
||||
}],
|
||||
tools: getTools('openai')
|
||||
})
|
||||
|
||||
console.log('response:',
|
||||
inspect(response, { depth: Number.MAX_VALUE, colors: true }))
|
||||
}
|
||||
{
|
||||
const agent = new OpenAIAgent({
|
||||
tools: getTools('llamaindex')
|
||||
})
|
||||
const response = await agent.chat({
|
||||
message: 'What\'s my current weather?'
|
||||
})
|
||||
console.log('response:',
|
||||
inspect(response, { depth: Number.MAX_VALUE, colors: true }))
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Get the weather for a city
|
||||
* @param city The city to get the weather for
|
||||
* @returns The weather for the city, e.g. "Sunny", "Rainy", etc.
|
||||
*/
|
||||
export function getWeather(
|
||||
city: string,
|
||||
) {
|
||||
return `The weather in ${city} is sunny!`;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "@llamaindex/tool",
|
||||
"type": "module",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "./src/index.ts",
|
||||
"scripts": {
|
||||
"test": "node --import tsx --import ./src/register.ts ./examples/01_node/src/index.ts"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@swc/core": "^1.4.13",
|
||||
"jotai": "^2.8.0",
|
||||
"openai": "^4.33.0",
|
||||
"ts-json-schema-generator": "^1.5.1",
|
||||
"typedoc": "^0.25.13",
|
||||
"typescript": "^5.4.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@swc/types": "^0.1.6",
|
||||
"@types/json-schema": "^7.0.15",
|
||||
"llamaindex": "^0.2.7",
|
||||
"tsx": "^4.7.2",
|
||||
"vitest": "^1.5.0"
|
||||
}
|
||||
}
|
||||
Generated
+3628
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
packages:
|
||||
- "examples/*"
|
||||
@@ -0,0 +1,19 @@
|
||||
import td from 'typedoc'
|
||||
|
||||
export async function parseRoot (
|
||||
entryPoint: string
|
||||
) {
|
||||
const app = await td.Application.bootstrapWithPlugins({
|
||||
entryPoints: [entryPoint]
|
||||
}, [
|
||||
new td.TypeDocReader(),
|
||||
new td.PackageJsonReader(),
|
||||
new td.TSConfigReader()
|
||||
])
|
||||
const project = await app.convert()
|
||||
|
||||
if (project) {
|
||||
return app.serializer.projectToObject(project, process.cwd())
|
||||
}
|
||||
throw new Error('Failed to parse root')
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import { ChatCompletionTool } from 'openai/resources/chat/completions'
|
||||
import { Info, store, toolMetadataAtom, toolsAtom } from './internal'
|
||||
import { BaseTool, ToolMetadata } from 'llamaindex'
|
||||
|
||||
export type {
|
||||
Info
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export function injectMetadata (
|
||||
metadata: ToolMetadata,
|
||||
info: Info
|
||||
) {
|
||||
store.get(toolMetadataAtom).push([metadata, info])
|
||||
}
|
||||
|
||||
export function registerTools (
|
||||
fns: Record<string, (...args: any[]) => any>
|
||||
) {
|
||||
store.set(
|
||||
toolsAtom,
|
||||
fns
|
||||
)
|
||||
}
|
||||
|
||||
export function getTools (
|
||||
format: 'openai'
|
||||
): ChatCompletionTool[];
|
||||
export function getTools (
|
||||
format: 'llamaindex'
|
||||
): BaseTool[];
|
||||
export function getTools (
|
||||
format: string
|
||||
): ChatCompletionTool[] | BaseTool[] {
|
||||
switch (format) {
|
||||
case 'openai': {
|
||||
return store.get(toolMetadataAtom).
|
||||
map<ChatCompletionTool>(([metadata]) => ({
|
||||
type: 'function',
|
||||
function: {
|
||||
parameters: metadata.parameters,
|
||||
name: metadata.name,
|
||||
description: metadata.description
|
||||
}
|
||||
}))
|
||||
}
|
||||
case 'llamaindex': {
|
||||
const fns = store.get(toolsAtom)
|
||||
return store.get(toolMetadataAtom).map(([metadata, info]) => ({
|
||||
call: (input: Record<string, unknown>) => {
|
||||
const args = Object.entries(info.parameterMapping).
|
||||
reduce((arr, [name, idx]) => {
|
||||
arr[idx] = input[name]
|
||||
return arr
|
||||
}, [] as unknown[])
|
||||
console.debug('find function:', metadata.name, args)
|
||||
const fn = fns[metadata.name]
|
||||
if (!fn) {
|
||||
throw new Error(`Cannot find function to call: ${metadata.name}`)
|
||||
}
|
||||
return fn(...args)
|
||||
},
|
||||
metadata
|
||||
}))
|
||||
}
|
||||
}
|
||||
throw new Error(`Unknown format: ${format}`)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { atom, createStore } from 'jotai/vanilla'
|
||||
import { ToolMetadata } from 'llamaindex'
|
||||
|
||||
export type Info = {
|
||||
parameterMapping: Record<string, number>
|
||||
}
|
||||
|
||||
export const store = createStore()
|
||||
export const toolMetadataAtom = atom<[ToolMetadata, Info][]>([])
|
||||
export const toolsAtom = atom<Record<string, (...args: any[]) => any>>({})
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
import type { LoadHook } from 'node:module'
|
||||
import {
|
||||
JSONSchema7,
|
||||
JSONSchema7Definition,
|
||||
JSONSchema7TypeName
|
||||
} from 'json-schema'
|
||||
import { stat } from 'node:fs/promises'
|
||||
import { parse } from '@swc/core'
|
||||
import type {
|
||||
ExportDeclaration,
|
||||
ImportDeclaration,
|
||||
ExpressionStatement
|
||||
} from '@swc/types'
|
||||
import { parseRoot } from './compiler'
|
||||
import type { ToolMetadata } from 'llamaindex'
|
||||
import { inspect } from 'node:util'
|
||||
import type { Info } from '.'
|
||||
|
||||
export const load: LoadHook = async (url, context, nextLoad) => {
|
||||
const output = await nextLoad(url, context)
|
||||
if (typeof output.source === 'string') {
|
||||
const m = await parse(output.source)
|
||||
const importNodes = m.body.filter(
|
||||
(node): node is ImportDeclaration => node.type === 'ImportDeclaration')
|
||||
const exportNodes = m.body.filter(
|
||||
(node): node is ExportDeclaration => node.type === 'ExportDeclaration')
|
||||
const hasRegisterTool = importNodes.find(
|
||||
node => node.source.value === '@llamaindex/tool' &&
|
||||
node.specifiers.find(specifier =>
|
||||
specifier.type === 'ImportSpecifier' &&
|
||||
specifier.local.value === 'registerTools'
|
||||
)
|
||||
)
|
||||
if (hasRegisterTool) {
|
||||
// if injectMetadata is not called, inject it
|
||||
if (
|
||||
importNodes.find(node =>
|
||||
node.source.value === '@llamaindex/tool' &&
|
||||
node.specifiers.find(specifier =>
|
||||
specifier.type === 'ImportSpecifier' &&
|
||||
specifier.local.value === 'injectMetadata'
|
||||
)
|
||||
) === undefined
|
||||
) {
|
||||
output.source = `import { injectMetadata } from "@llamaindex/tool";` +
|
||||
output.source
|
||||
}
|
||||
// user register tools, find target import file
|
||||
const [callExpression, ...rest] = m.body.filter(
|
||||
(node): node is ExpressionStatement => node.type ===
|
||||
'ExpressionStatement' &&
|
||||
node.expression.type === 'CallExpression' &&
|
||||
node.expression.callee.type === 'Identifier' &&
|
||||
node.expression.callee.value === 'registerTools'
|
||||
)
|
||||
if (rest.length !== 0) {
|
||||
console.warn('registerTools should be only called once')
|
||||
}
|
||||
if (callExpression && callExpression.expression.type ===
|
||||
'CallExpression') {
|
||||
const arg = callExpression.expression.arguments[0]
|
||||
if (!arg) {
|
||||
throw new Error('registerTools should have one argument')
|
||||
}
|
||||
if (arg.expression.type === 'Identifier') {
|
||||
const id = arg.expression.value
|
||||
const node = importNodes.find(node =>
|
||||
node.specifiers.find(specifier => specifier.local.value === id)
|
||||
)
|
||||
if (!node) {
|
||||
throw new Error('Cannot find import node')
|
||||
}
|
||||
const source = node.source.value
|
||||
const baseUrl = new URL('./', url)
|
||||
let targetFile = new URL(source, baseUrl).pathname
|
||||
const extensions = ['.ts']
|
||||
for (const ext of extensions) {
|
||||
try {
|
||||
await stat(targetFile + ext)
|
||||
targetFile += ext
|
||||
break
|
||||
} catch (e) {
|
||||
if (e && typeof e === 'object' && 'code' in e && e.code !==
|
||||
'ENOENT') {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
const json = await parseRoot(targetFile)
|
||||
const children = json.children
|
||||
if (Array.isArray(children)) {
|
||||
const schema = {
|
||||
type: 'object',
|
||||
properties: {} as {
|
||||
[key: string]: JSONSchema7Definition
|
||||
},
|
||||
additionalItems: false,
|
||||
required: [] as string[]
|
||||
} satisfies JSONSchema7
|
||||
const info: Info = {
|
||||
parameterMapping: {}
|
||||
}
|
||||
children.forEach(child => {
|
||||
const metadata: ToolMetadata = {
|
||||
name: child.name,
|
||||
description: '',
|
||||
parameters: schema
|
||||
}
|
||||
child.signatures?.forEach(signature => {
|
||||
const description = signature.comment?.summary.map(x => x.text).
|
||||
join('\n')
|
||||
if (description) {
|
||||
metadata.description += description
|
||||
}
|
||||
signature.parameters?.map((parameter, idx) => {
|
||||
if (parameter.type?.type === 'intrinsic') {
|
||||
// parameter.type.name
|
||||
schema.properties[parameter.name as string] = {
|
||||
type: parameter.type.name as JSONSchema7TypeName,
|
||||
description: parameter.comment?.summary.map(x => x.text).
|
||||
join('\n')
|
||||
} as JSONSchema7Definition
|
||||
schema.required.push(parameter.name as string)
|
||||
info.parameterMapping[parameter.name as string] = idx
|
||||
}
|
||||
})
|
||||
})
|
||||
console.log(
|
||||
inspect(metadata, { depth: Number.MAX_VALUE, colors: true }))
|
||||
output.source = `\ninjectMetadata(${JSON.stringify(
|
||||
metadata)}, ${JSON.stringify(info)});\n` +
|
||||
output.source
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import { register } from 'node:module'
|
||||
|
||||
register('./loader.ts', import.meta.url)
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"skipLibCheck": true
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user