init repo

This commit is contained in:
Alex Yang
2024-04-12 02:48:04 -05:00
commit ad534abaa1
14 changed files with 4119 additions and 0 deletions
+132
View File
@@ -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.*
+7
View File
@@ -0,0 +1,7 @@
{
"type": "module",
"dependencies": {
"@llamaindex/tool": "workspace:*",
"openai": "^4.33.0"
}
}
+13
View File
@@ -0,0 +1,13 @@
import { getWeather } from './utils'
/**
* Get current location
*/
export function getCurrentLocation() {
console.log('Getting current location')
return 'London'
}
export {
getWeather
}
+46
View File
@@ -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 }))
}
+10
View File
@@ -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!`;
}
+26
View File
@@ -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"
}
}
+3628
View File
File diff suppressed because it is too large Load Diff
+2
View File
@@ -0,0 +1,2 @@
packages:
- "examples/*"
+19
View File
@@ -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')
}
+70
View File
@@ -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}`)
}
+10
View File
@@ -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
View File
@@ -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
}
+3
View File
@@ -0,0 +1,3 @@
import { register } from 'node:module'
register('./loader.ts', import.meta.url)
+12
View File
@@ -0,0 +1,12 @@
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "bundler",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"noUncheckedIndexedAccess": true,
"skipLibCheck": true
}
}