Compare commits

...

31 Commits

Author SHA1 Message Date
Marcus Schiesser 951f215723 Merge branch 'main' into feat/build-wasm-with-extism 2024-09-16 15:40:17 +07:00
Thuc Pham 9447654332 Create shy-bottles-shop.md 2024-09-12 14:42:33 +07:00
Thuc Pham 3293c22ade Merge branch 'main' into feat/build-wasm-with-extism 2024-09-12 14:41:43 +07:00
Thuc Pham 33d57d1caf update test 2024-09-12 14:24:10 +07:00
Thuc Pham 92d0d098ab add todo tool example 2024-09-12 14:14:18 +07:00
Thuc Pham f76abceb8f refactor: remove enum 2024-09-12 14:09:34 +07:00
Thuc Pham 191986b337 refactor: create tool class params 2024-09-12 13:59:17 +07:00
Thuc Pham d718f4334e fix: upgrade extism to fix worker bug 2024-09-12 13:47:57 +07:00
Thuc Pham f34ea4563a Merge branch 'main' into feat/build-wasm-with-extism 2024-09-11 09:45:52 +07:00
Thuc Pham fda48fae63 fix: wrong path ts config.json 2024-09-09 18:43:14 +07:00
Thuc Pham 660eab8045 fix: e2e 2024-09-09 18:41:50 +07:00
Thuc Pham d95ed78ca4 fix: lint depend on build 2024-09-09 18:36:05 +07:00
Thuc Pham 8a73dac583 test wiki 2024-09-09 18:32:24 +07:00
Thuc Pham f7b666e24a refactor: add package.json to examples 2024-09-09 18:24:39 +07:00
Thuc Pham d48724fa1e fix: update doc 2024-09-09 17:28:01 +07:00
Thuc Pham 879c15f951 fix: lint 2024-09-09 17:22:34 +07:00
Thuc Pham 6cc8595d44 fix: typo and use same d.ts file for all tools 2024-09-09 17:20:22 +07:00
Thuc Pham 7710d9669f add getMetadata to wiki python example 2024-09-09 12:18:29 +07:00
Thuc Pham 1eddb6e8f3 export type 2024-09-09 12:13:04 +07:00
Thuc Pham 54abd709b2 feat: example with agent 2024-09-09 12:10:15 +07:00
Thuc Pham 507012a0c9 feat: ExtismToolFactory 2024-09-09 12:01:36 +07:00
Thuc Pham 4b74096fb6 update readme 2024-09-06 17:59:02 +07:00
Thuc Pham dfb6c2563a feat: add js example 2024-09-06 17:55:14 +07:00
Thuc Pham 9df0d63821 feat: make agent example 2024-09-06 17:42:11 +07:00
Thuc Pham 5c34d3a9cc create abstract ExtismTool 2024-09-06 17:08:13 +07:00
Thuc Pham fbb5ece07e fix: install extism for all tests 2024-09-06 16:39:05 +07:00
Thuc Pham de4d8ffb1d install extism for all actions 2024-09-06 16:34:42 +07:00
Thuc Pham efbd61f124 install extism-js 2024-09-06 16:30:15 +07:00
Thuc Pham ecc54cb540 fix: lock 2024-09-06 16:09:42 +07:00
Thuc Pham 02c81989c6 feat: detach packages for extism 2024-09-06 16:07:55 +07:00
Thuc Pham ccfc11c8cb feat: build wasm with extism 2024-09-06 12:39:28 +07:00
22 changed files with 473 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"@llamaindex/extism-tools": patch
---
Build WASM tools with extism
@@ -19,6 +19,10 @@ jobs:
with:
node-version-file: ".nvmrc"
cache: "pnpm"
- name: Install Extism
run: |
curl -O https://raw.githubusercontent.com/extism/js-pdk/main/install.sh
sh install.sh
- name: Install dependencies
run: pnpm install
- name: Run lint
+5
View File
@@ -18,6 +18,11 @@ jobs:
node-version-file: ".nvmrc"
cache: "pnpm"
- name: Install Extism
run: |
curl -O https://raw.githubusercontent.com/extism/js-pdk/main/install.sh
sh install.sh
- name: Install dependencies
run: pnpm install
+5
View File
@@ -23,6 +23,11 @@ jobs:
node-version-file: ".nvmrc"
cache: "pnpm"
- name: Install Extism
run: |
curl -O https://raw.githubusercontent.com/extism/js-pdk/main/install.sh
sh install.sh
- name: Install dependencies
run: pnpm install
+20
View File
@@ -42,6 +42,10 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
cache: "pnpm"
- name: Install Extism
run: |
curl -O https://raw.githubusercontent.com/extism/js-pdk/main/install.sh
sh install.sh
- name: Install dependencies
run: pnpm install
- name: Run E2E Tests
@@ -62,6 +66,10 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
cache: "pnpm"
- name: Install Extism
run: |
curl -O https://raw.githubusercontent.com/extism/js-pdk/main/install.sh
sh install.sh
- name: Install dependencies
run: pnpm install
- name: Run tests
@@ -77,6 +85,10 @@ jobs:
with:
node-version-file: ".nvmrc"
cache: "pnpm"
- name: Install Extism
run: |
curl -O https://raw.githubusercontent.com/extism/js-pdk/main/install.sh
sh install.sh
- name: Install dependencies
run: pnpm install
- name: Build
@@ -114,6 +126,10 @@ jobs:
with:
node-version-file: ".nvmrc"
cache: "pnpm"
- name: Install Extism
run: |
curl -O https://raw.githubusercontent.com/extism/js-pdk/main/install.sh
sh install.sh
- name: Install dependencies
run: pnpm install
- name: Build llamaindex
@@ -133,6 +149,10 @@ jobs:
with:
node-version-file: ".nvmrc"
cache: "pnpm"
- name: Install Extism
run: |
curl -O https://raw.githubusercontent.com/extism/js-pdk/main/install.sh
sh install.sh
- name: Install dependencies
run: pnpm install
- name: Build
+24
View File
@@ -0,0 +1,24 @@
## Extism Tools
### Prerequisites for Development
- [Extism PDK](https://github.com/extism/js-pdk?tab=readme-ov-file#linux-macos)
### Build WASM files
```bash
pnpm run build
```
### Run WASM files in Node.js using Extism SDK (https://github.com/extism/js-sdk)
```bash
cd examples
pnpm run test:wiki
```
### Run WASM files in Python using Extism SDK (https://github.com/extism/python-sdk)
```bash
python examples/wasm/wiki.py
```
+31
View File
@@ -0,0 +1,31 @@
// needed as extism-js doesn't support compiling multiple files
import { execSync } from "child_process";
import { mkdirSync, readdirSync } from "fs";
const WASM_SRC_FOLDER = "wasm";
const WASM_OUTPUT_FOLDER = "dist/wasm";
// get list of tools from files (except index.d.ts)
const tools = readdirSync(WASM_SRC_FOLDER)
.filter((file) => !file.includes("index.d.ts"))
.map((file) => file.split(".")[0]);
// create dist/wasm folder if it doesn't exist using fs
try {
mkdirSync(WASM_OUTPUT_FOLDER, { recursive: true });
} catch (error) {
console.error("Error creating dist/wasm folder:", error.message);
process.exit(1);
}
// loop through each tool, compile it to wasm using extism-js
tools.forEach((tool) => {
try {
execSync(
`extism-js ${WASM_SRC_FOLDER}/${tool}.js -i ${WASM_SRC_FOLDER}/index.d.ts -o ${WASM_OUTPUT_FOLDER}/${tool}.wasm`,
);
} catch (error) {
console.error(`Error compiling module ${tool}:`, error.message);
process.exit(1);
}
});
@@ -0,0 +1,17 @@
{
"name": "@llamaindex/extism-tools-examples",
"private": true,
"version": "1.0.0",
"type": "module",
"scripts": {
"test:wiki": "tsx ./src/wiki.ts",
"test:todo": "tsx ./src/todo.ts"
},
"dependencies": {
"@llamaindex/extism-tools": "workspace:*",
"llamaindex": "workspace:*"
},
"devDependencies": {
"tsx": "^4.19.0"
}
}
@@ -0,0 +1,13 @@
import { ExtismToolFactory } from "@llamaindex/extism-tools";
import { OpenAI, OpenAIAgent, Settings } from "llamaindex";
async function main() {
const TodoTool = await ExtismToolFactory.createToolClass("todo");
const todoTool = new TodoTool();
Settings.llm = new OpenAI();
const agent = new OpenAIAgent({ tools: [todoTool] });
const result = await agent.chat({ message: "Get first todo" });
console.log(result.message);
}
void main();
@@ -0,0 +1,34 @@
import extism
import json
from os.path import join, dirname
def read_local_wasm(file_name):
path = join(dirname(__file__), file_name) # Change this to your wasm file path
with open(path, "rb") as wasm_file:
return wasm_file.read()
def _manifest(file_name):
wasm = read_local_wasm(file_name)
return {
"wasm": [{"data": wasm}],
"allowed_hosts": ["*.wikipedia.org"],
}
manifest = _manifest("wiki.wasm")
with extism.Plugin(manifest, wasi=True) as plugin:
metadata = plugin.call(
"getMetadata",
"",
parse=lambda output: json.loads(bytes(output).decode("utf-8")),
)
data = plugin.call(
"call",
json.dumps({"query": "Ho Chi Minh City"}),
parse=lambda output: json.loads(bytes(output).decode("utf-8")),
)
print(metadata)
print(data)
@@ -0,0 +1,13 @@
import { ExtismToolFactory } from "@llamaindex/extism-tools";
import { OpenAI, OpenAIAgent, Settings } from "llamaindex";
async function main() {
const WikiTool = await ExtismToolFactory.createToolClass("wiki");
const wikiTool = new WikiTool();
Settings.llm = new OpenAI();
const agent = new OpenAIAgent({ tools: [wikiTool] });
const result = await agent.chat({ message: "Ho Chi Minh City" });
console.log(result.message);
}
void main();
@@ -0,0 +1,9 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"outDir": "./lib",
"module": "node16",
"moduleResolution": "node16"
},
"include": ["./src"]
}
+62
View File
@@ -0,0 +1,62 @@
{
"name": "@llamaindex/extism-tools",
"version": "0.0.1",
"license": "MIT",
"type": "module",
"dependencies": {
"@extism/extism": "^2.0.0-rc8",
"ajv": "^8.17.1",
"@llamaindex/core": "workspace:*",
"@llamaindex/env": "workspace:*",
"llamaindex": "workspace:*"
},
"devDependencies": {
"@swc/cli": "^0.4.0",
"@swc/core": "^1.7.22",
"typescript": "^5.5.4",
"@types/node": "^22.5.1"
},
"engines": {
"node": ">=18.0.0"
},
"types": "./dist/index.d.ts",
"main": "./dist/cjs/index.js",
"exports": {
".": {
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"require": {
"types": "./dist/index.d.ts",
"default": "./dist/cjs/index.js"
}
},
"./*": {
"import": {
"types": "./dist/*.d.ts",
"default": "./dist/*.js"
},
"require": {
"types": "./dist/*.d.ts",
"default": "./dist/cjs/*.js"
}
}
},
"files": [
"dist",
"CHANGELOG.md"
],
"repository": {
"type": "git",
"url": "https://github.com/run-llama/LlamaIndexTS.git",
"directory": "packages/extism-tools"
},
"scripts": {
"build": "rm -rf ./dist && pnpm run build:wasm && pnpm run build:esm && pnpm run build:cjs && pnpm run build:type",
"build:esm": "swc src -d dist --strip-leading-paths --config-file ../../.swcrc",
"build:cjs": "swc src -d dist/cjs --strip-leading-paths --config-file ../../.cjs.swcrc",
"build:type": "tsc -p tsconfig.json",
"build:wasm": "node bin/compile.js"
}
}
@@ -0,0 +1,82 @@
import createPlugin, { type Plugin } from "@extism/extism";
import type { JSONSchemaType } from "ajv";
import type { BaseToolWithCall, ToolMetadata } from "llamaindex";
import path from "path";
import { fileURLToPath } from "url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
export const WASM_DIRECTORY = path.join(__dirname, "..", "dist", "wasm");
export const DEFAULT_MAX_HTTP_RESPONSE_BYTES = 100 * 1024 * 1024; // 100 MB
export type ToolParams = Record<string, any>;
export type ToolClassParams = {
metadata?: ToolMetadata<JSONSchemaType<ToolParams>>;
};
export type CreateToolClassParams = {
wasmFilename: string;
allowedHosts: string[];
maxHttpResponseBytes: number;
transformResponse: (response: any) => any;
};
export const createPluginInstance = async (
params: Omit<CreateToolClassParams, "transformResponse">,
): Promise<Plugin> => {
const { wasmFilename, allowedHosts, maxHttpResponseBytes } = params;
const plugin = await createPlugin(`${WASM_DIRECTORY}/${wasmFilename}`, {
useWasi: true,
runInWorker: true,
allowedHosts,
memory: { maxHttpResponseBytes },
});
return plugin;
};
export const DEFAULT_TOOL_PARAMS: Omit<CreateToolClassParams, "wasmFilename"> =
{
allowedHosts: ["*"],
maxHttpResponseBytes: DEFAULT_MAX_HTTP_RESPONSE_BYTES,
transformResponse: (response: any) => response,
};
export class ExtismToolFactory {
static async createToolClass(
toolName: string,
params: Omit<CreateToolClassParams, "wasmFilename"> = DEFAULT_TOOL_PARAMS,
): Promise<new (params?: ToolClassParams) => BaseToolWithCall<ToolParams>> {
const config = { ...params, wasmFilename: `${toolName}.wasm` };
const plugin = await createPluginInstance(config);
try {
const wasmMetadata = await plugin.call("getMetadata");
if (!wasmMetadata) {
throw new Error("The WASM plugin did not return metadata.");
}
const defaultMetadata = wasmMetadata.json();
return class implements BaseToolWithCall<ToolParams> {
metadata: ToolMetadata<JSONSchemaType<ToolParams>>;
constructor(params?: ToolClassParams) {
this.metadata = params?.metadata || defaultMetadata;
}
async call(input: ToolParams): Promise<string> {
const pluginInstance = await createPluginInstance(config);
const data = await pluginInstance.call("call", JSON.stringify(input));
if (!data) return "No result";
const result = config.transformResponse(data.json());
await pluginInstance.close();
return result;
}
};
} catch (e) {
console.error(e);
throw new Error("Failed to create Tool instance.");
} finally {
await plugin.close();
}
}
}
+1
View File
@@ -0,0 +1 @@
export { ExtismToolFactory } from "./ExtismToolFactory.js";
+21
View File
@@ -0,0 +1,21 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./dist",
"tsBuildInfoFile": "./dist/.tsbuildinfo",
"emitDeclarationOnly": true,
"module": "node16",
"moduleResolution": "node16",
"skipLibCheck": true,
"strict": true,
"types": ["node"]
},
"include": ["./src"],
"exclude": ["node_modules"],
"references": [
{
"path": "../env/tsconfig.json"
}
]
}
+4
View File
@@ -0,0 +1,4 @@
declare module "main" {
export function getMetadata(): I32;
export function call(): I32;
}
+34
View File
@@ -0,0 +1,34 @@
function getMetadata() {
const metadata = {
name: "todo_tool",
description: "A tool helps search todo.",
parameters: {
type: "object",
properties: {
index: {
type: "number",
description: "The index of the todo to search for.",
},
},
required: ["index"],
},
};
Host.outputString(JSON.stringify(metadata));
}
function call() {
const params = JSON.parse(Host.inputString());
const index = params?.index;
if (!index) throw new Error("No index provided");
const request = {
method: "GET",
url: `https://jsonplaceholder.typicode.com/todos/${encodeURIComponent(index)}`,
};
const response = Http.request(request);
if (response.status != 200)
throw new Error(`Got non 200 response ${response.status}`);
Host.outputString(response.body);
}
module.exports = { getMetadata, call };
+33
View File
@@ -0,0 +1,33 @@
function getMetadata() {
const metadata = {
name: "wikipedia_tool",
description: "A tool that uses a query engine to search Wikipedia.",
parameters: {
type: "object",
properties: {
query: {
type: "string",
description: "The query to search for",
},
},
required: ["query"],
},
};
Host.outputString(JSON.stringify(metadata));
}
function call() {
const params = JSON.parse(Host.inputString());
const query = params?.query;
if (!query) throw new Error("No query provided");
const request = {
method: "GET",
url: `https://en.wikipedia.org/api/rest_v1/page/summary/${encodeURIComponent(query)}`,
};
const response = Http.request(request);
if (response.status != 200)
throw new Error(`Got non 200 response ${response.status}`);
Host.outputString(response.body);
}
module.exports = { getMetadata, call };
+49
View File
@@ -481,6 +481,50 @@ importers:
specifier: ^1.1.2
version: 1.1.2
packages/extism-tools:
dependencies:
'@extism/extism':
specifier: ^2.0.0-rc8
version: 2.0.0-rc8
'@llamaindex/core':
specifier: workspace:*
version: link:../core
'@llamaindex/env':
specifier: workspace:*
version: link:../env
ajv:
specifier: ^8.17.1
version: 8.17.1
llamaindex:
specifier: workspace:*
version: link:../llamaindex
devDependencies:
'@swc/cli':
specifier: ^0.4.0
version: 0.4.0(@swc/core@1.7.22(@swc/helpers@0.5.13))(chokidar@3.6.0)
'@swc/core':
specifier: ^1.7.22
version: 1.7.22(@swc/helpers@0.5.13)
'@types/node':
specifier: ^22.5.1
version: 22.5.4
typescript:
specifier: ^5.5.4
version: 5.6.2
packages/extism-tools/examples:
dependencies:
'@llamaindex/extism-tools':
specifier: workspace:*
version: link:..
llamaindex:
specifier: workspace:*
version: link:../../llamaindex
devDependencies:
tsx:
specifier: ^4.19.0
version: 4.19.0
packages/llamaindex:
dependencies:
'@anthropic-ai/sdk':
@@ -2819,6 +2863,9 @@ packages:
resolution: {integrity: sha512-autAXT203ixhqei9xt+qkYOvY8l6LAFIdT2UXc/RPNeUVfqRF1BV94GTJyVPFKT8nFM6MyVJhjLj9E8JWvf5zQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@extism/extism@2.0.0-rc8':
resolution: {integrity: sha512-a90ti82PDEiuANoDlFVBMjlW/sLnJl9zGSzvYY3eydX2cQyFA8AsdmC4JUWLK+NEP9eeta83+6N5B8UwYG33bw==}
'@faker-js/faker@8.4.1':
resolution: {integrity: sha512-XQ3cU+Q8Uqmrbf2e0cIC/QN43sTBSC8KF12u29Mb47tWrt2hAgBXSgpZMj4Ao8Uk0iJcU99QsOCaIL8934obCg==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0, npm: '>=6.14.13'}
@@ -14449,6 +14496,8 @@ snapshots:
levn: 0.4.1
optional: true
'@extism/extism@2.0.0-rc8': {}
'@faker-js/faker@8.4.1': {}
'@fastify/busboy@2.1.1': {}
+1
View File
@@ -9,3 +9,4 @@ packages:
- "packages/autotool/examples/*"
- "examples/"
- "examples/*"
- "packages/extism-tools/examples"
+6
View File
@@ -82,6 +82,12 @@
},
{
"path": "./packages/experimental/tsconfig.json"
},
{
"path": "./packages/extism-tools/tsconfig.json"
},
{
"path": "./packages/extism-tools/examples/tsconfig.json"
}
]
}