Compare commits

...

21 Commits

Author SHA1 Message Date
github-actions[bot] 72687b4f69 Release 0.3.5 (#805)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-05-03 18:16:39 -05:00
Alex Yang 0c67e1f8f3 build: fix new version script 2024-05-03 18:13:24 -05:00
Alex Yang 4a0619758a chore: fix jsr.json 2024-05-03 18:09:05 -05:00
Alex Yang bc7a11cdbe fix: inline ollama build (#807) 2024-05-03 18:03:23 -05:00
Alex Yang 5596e31947 feat: improve @llamaindex/env (#787) 2024-05-03 18:03:14 -05:00
Alex Yang 2fe2b813ba fix: filter with multiple filters in ChromaDB (#784) 2024-05-03 17:07:45 -05:00
Alex Yang be5df5b01b fix(core): multple chat on anthropic agent (#799) 2024-05-03 16:18:46 -05:00
JT-Dev-215 e74fe88342 fix: change <-> to <=> in the SELECT query (#804)
Co-authored-by: Alex Yang <himself65@outlook.com>
2024-05-03 12:10:36 -05:00
github-actions[bot] f1862ccab1 Release 0.3.4 (#797)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-05-02 20:02:06 -05:00
Yi Ding 9e74a4327f feat: add top k to asQueryEngine (#801)
Co-authored-by: Alex Yang <himself65@outlook.com>
2024-05-02 19:59:36 -05:00
Alex Yang 5e61934d5a fix: remove clone object in CallbackManager.dispatchEvent (#802) 2024-05-02 19:55:41 -05:00
Alex Yang 2008efe0ee feat: add verbose mode to Agent (#800) 2024-05-02 19:54:05 -05:00
Alex Yang ee719a1fda fix: streaming for ReAct Agent (#798) 2024-05-02 18:52:18 -05:00
Alex Yang 1dce275a7c fix: export StorageContext on edge runtime (#793) 2024-05-02 14:52:16 -05:00
Thuc Pham d10533ef77 feat: add hugging face llm (#796) 2024-05-02 18:43:05 +08:00
github-actions[bot] 8aeb8ae690 Release 0.3.3 (#792)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-05-01 21:47:16 -05:00
Thuc Pham e8c41c5c27 fix: wrong gemini streaming chat response (#791) 2024-05-02 08:39:57 +07:00
github-actions[bot] 051b4ddfa2 Release 0.3.2 (#790)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-05-01 19:30:09 -05:00
Alex Yang 61103b677b fix: streaming for Agent.createTask (#788) 2024-05-01 19:26:06 -05:00
Alex Yang e69cac672a docs: update blog post 2024-05-01 13:01:55 -05:00
Alex Yang 94246a3ca8 chore: bump jsr.json 2024-05-01 12:59:03 -05:00
66 changed files with 4527 additions and 1610 deletions
+37
View File
@@ -1,5 +1,42 @@
# docs
## 0.0.13
### Patch Changes
- Updated dependencies [bc7a11c]
- Updated dependencies [2fe2b81]
- Updated dependencies [5596e31]
- Updated dependencies [e74fe88]
- Updated dependencies [be5df5b]
- llamaindex@0.3.5
## 0.0.12
### Patch Changes
- Updated dependencies [1dce275]
- Updated dependencies [d10533e]
- Updated dependencies [2008efe]
- Updated dependencies [5e61934]
- Updated dependencies [9e74a43]
- Updated dependencies [ee719a1]
- llamaindex@0.3.4
## 0.0.11
### Patch Changes
- Updated dependencies [e8c41c5]
- llamaindex@0.3.3
## 0.0.10
### Patch Changes
- Updated dependencies [61103b6]
- llamaindex@0.3.2
## 0.0.9
### Patch Changes
+11 -18
View File
@@ -72,12 +72,8 @@ export class MyAgent extends AgentRunner<MyLLM> {
// create store is a function to create a store for each task, by default it only includes `messages` and `toolOutputs`
createStore = AgentRunner.defaultCreateStore;
static taskHandler: TaskHandler<Anthropic> = async (step) => {
const { input } = step;
static taskHandler: TaskHandler<Anthropic> = async (step, enqueueOutput) => {
const { llm, stream } = step.context;
if (input) {
step.context.store.messages = [...step.context.store.messages, input];
}
// initialize the input
const response = await llm.chat({
stream,
@@ -90,27 +86,21 @@ export class MyAgent extends AgentRunner<MyLLM> {
];
// your logic here to decide whether to continue the task
const shouldContinue = Math.random(); /* <-- replace with your logic here */
enqueueOutput({
taskStep: step,
output: response,
isLast: !shouldContinue,
});
if (shouldContinue) {
const content = await someHeavyFunctionCall();
// if you want to continue the task, you can insert your new context for the next task step
step.context.store.messages = [
...step.context.store.messages,
{
content: "INSERT MY NEW DATA",
content,
role: "user",
},
];
return {
taskStep: step,
output: response,
isLast: false,
};
} else {
// if you want to end the task, you can return the response with `isLast: true`
return {
taskStep: step,
output: response,
isLast: true,
};
}
};
}
@@ -263,6 +253,9 @@ const sumNumbers = FunctionTool.from<Input>(
In addition to Node.js, LlamaIndexTS now offers enhanced support for Next.js, Deno, and Cloudflare Workers, making it
more versatile across different platforms.
For now, you can install llamaindex and directly import it into your existing Next.js, Deno or Cloudflare Worker project
**without any extra configuration**.
#### [Deno](https://deno.com/)
You can use LlamaIndexTS in Deno by installation through JSR:
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "docs",
"version": "0.0.9",
"version": "0.0.13",
"private": true,
"scripts": {
"docusaurus": "docusaurus",
+1
View File
@@ -0,0 +1 @@
DEBUG=llamaindex
+39
View File
@@ -0,0 +1,39 @@
import { ChatResponseChunk, OpenAIAgent } from "llamaindex";
import { ReadableStream } from "node:stream/web";
import {
getCurrentIDTool,
getUserInfoTool,
getWeatherTool,
} from "./utils/tools";
async function main() {
// Create an OpenAIAgent with the function tools
const agent = new OpenAIAgent({
tools: [getCurrentIDTool, getUserInfoTool, getWeatherTool],
});
const task = await agent.createTask(
"What is my current address weather based on my profile?",
true,
);
for await (const stepOutput of task) {
const stream = stepOutput.output as ReadableStream<ChatResponseChunk>;
if (stepOutput.isLast) {
for await (const chunk of stream) {
process.stdout.write(chunk.delta);
}
process.stdout.write("\n");
} else {
// handing function call
console.log("handling function call...");
for await (const chunk of stream) {
console.log("debug:", JSON.stringify(chunk.raw));
}
}
}
}
void main().then(() => {
console.log("Done");
});
+1 -1
View File
@@ -53,7 +53,7 @@ async function main() {
message: "How much is 5 + 5? then divide by 2",
});
console.log(String(response));
console.log(response.response.message);
}
void main().then(() => {
+40
View File
@@ -0,0 +1,40 @@
import { ChatResponseChunk, ReActAgent } from "llamaindex";
import { ReadableStream } from "node:stream/web";
import {
getCurrentIDTool,
getUserInfoTool,
getWeatherTool,
} from "./utils/tools";
async function main() {
// Create an OpenAIAgent with the function tools
const agent = new ReActAgent({
tools: [getCurrentIDTool, getUserInfoTool, getWeatherTool],
});
const task = await agent.createTask(
"What is my current address weather based on my profile?",
true,
);
for await (const stepOutput of task) {
const stream = stepOutput.output as ReadableStream<ChatResponseChunk>;
if (stepOutput.isLast) {
for await (const chunk of stream) {
process.stdout.write(chunk.delta);
}
process.stdout.write("\n");
} else {
// handing function call
console.log("handling function call...");
for await (const chunk of stream) {
console.log("debug:", JSON.stringify(chunk.raw));
}
}
console.log("---");
}
}
void main().then(() => {
console.log("Done");
});
-97
View File
@@ -1,97 +0,0 @@
import { FunctionTool, OpenAIAgent } from "llamaindex";
import { ReadableStream } from "node:stream/web";
// Define a function to sum two numbers
function sumNumbers({ a, b }: { a: number; b: number }) {
return `${a + b}`;
}
// Define a function to divide two numbers
function divideNumbers({ a, b }: { a: number; b: number }) {
return `${a / b}`;
}
// Define the parameters of the sum function as a JSON schema
const sumJSON = {
type: "object",
properties: {
a: {
type: "number",
description: "The first number",
},
b: {
type: "number",
description: "The second number",
},
},
required: ["a", "b"],
} as const;
const divideJSON = {
type: "object",
properties: {
a: {
type: "number",
description: "The dividend",
},
b: {
type: "number",
description: "The divisor",
},
},
required: ["a", "b"],
} as const;
async function main() {
// Create a function tool from the sum function
const functionTool = new FunctionTool(sumNumbers, {
name: "sumNumbers",
description: "Use this function to sum two numbers",
parameters: sumJSON,
});
// Create a function tool from the divide function
const functionTool2 = new FunctionTool(divideNumbers, {
name: "divideNumbers",
description: "Use this function to divide two numbers",
parameters: divideJSON,
});
// Create an OpenAIAgent with the function tools
const agent = new OpenAIAgent({
tools: [functionTool, functionTool2],
});
// Create a task to sum and divide numbers
const task = await agent.createTask("How much is 5 + 5? then divide by 2");
let count = 0;
for await (const stepOutput of task) {
console.log(`Runnning step ${count++}`);
console.log(`======== OUTPUT ==========`);
const output = stepOutput.output;
if (output instanceof ReadableStream) {
for await (const chunk of output) {
process.stdout.write(chunk.delta);
}
} else {
console.log(output);
}
console.log(`==========================`);
if (stepOutput.isLast) {
if (stepOutput.output instanceof ReadableStream) {
for await (const chunk of stepOutput.output) {
process.stdout.write(chunk.delta);
}
} else {
console.log(stepOutput.output);
}
}
}
}
void main().then(() => {
console.log("Done");
});
+54
View File
@@ -0,0 +1,54 @@
import { FunctionTool } from "llamaindex";
export const getCurrentIDTool = FunctionTool.from(
() => {
console.log("Getting user id...");
return crypto.randomUUID();
},
{
name: "get_user_id",
description: "Get a random user id",
},
);
export const getUserInfoTool = FunctionTool.from(
({ userId }: { userId: string }) => {
console.log("Getting user info...", userId);
return `Name: Alex; Address: 1234 Main St, CA; User ID: ${userId}`;
},
{
name: "get_user_info",
description: "Get user info",
parameters: {
type: "object",
properties: {
userId: {
type: "string",
description: "The user id",
},
},
required: ["userId"],
},
},
);
export const getWeatherTool = FunctionTool.from(
({ address }: { address: string }) => {
console.log("Getting weather...", address);
return `${address} is in a sunny location!`;
},
{
name: "get_weather",
description: "Get the current weather for a location",
parameters: {
type: "object",
properties: {
address: {
type: "string",
description: "The address",
},
},
required: ["address"],
},
},
);
+22
View File
@@ -0,0 +1,22 @@
import { HuggingFaceInferenceAPI } from "llamaindex";
(async () => {
if (!process.env.HUGGING_FACE_TOKEN) {
throw new Error("Please set the HUGGING_FACE_TOKEN environment variable.");
}
const hf = new HuggingFaceInferenceAPI({
accessToken: process.env.HUGGING_FACE_TOKEN,
model: "mistralai/Mixtral-8x7B-Instruct-v0.1",
});
const result = await hf.chat({
messages: [
{ content: "You want to talk in rhymes.", role: "system" },
{
content:
"How much wood would a woodchuck chuck if a woodchuck could chuck wood?",
role: "user",
},
],
});
console.log(result);
})();
+1 -1
View File
@@ -15,7 +15,7 @@
"release": "pnpm run check-minor-version && pnpm run build:release && changeset publish",
"release-snapshot": "pnpm run check-minor-version && pnpm run build:release && changeset publish --tag snapshot",
"check-minor-version": "node ./scripts/check-minor-version",
"new-version": "changeset version && pnpm run check-minor-version && pnpm run build:release",
"new-version": "changeset version && pnpm run check-minor-version && pnpm format:write && pnpm run build:release",
"new-snapshot": "pnpm run build:release && changeset version --snapshot"
},
"devDependencies": {
+35
View File
@@ -1,5 +1,40 @@
# llamaindex
## 0.3.5
### Patch Changes
- bc7a11c: fix: inline ollama build
- 2fe2b81: fix: filter with multiple filters in ChromaDB
- 5596e31: feat: improve `@llamaindex/env`
- e74fe88: fix: change <-> to <=> in the SELECT query
- be5df5b: fix: anthropic agent on multiple chat
- Updated dependencies [5596e31]
- @llamaindex/env@0.1.1
## 0.3.4
### Patch Changes
- 1dce275: fix: export `StorageContext` on edge runtime
- d10533e: feat: add hugging face llm
- 2008efe: feat: add verbose mode to Agent
- 5e61934: fix: remove clone object in `CallbackManager.dispatchEvent`
- 9e74a43: feat: add top k to `asQueryEngine`
- ee719a1: fix: streaming for ReAct Agent
## 0.3.3
### Patch Changes
- e8c41c5: fix: wrong gemini streaming chat response
## 0.3.2
### Patch Changes
- 61103b6: fix: streaming for `Agent.createTask` API
## 0.3.1
### Patch Changes
+13
View File
@@ -0,0 +1,13 @@
# @llamaindex/core-e2e
## 0.0.4
### Patch Changes
- be5df5b: fix: anthropic agent on multiple chat
## 0.0.3
### Patch Changes
- 61103b6: fix: streaming for `Agent.createTask` API
@@ -1,5 +1,42 @@
# @llamaindex/cloudflare-worker-agent-test
## 0.0.6
### Patch Changes
- Updated dependencies [bc7a11c]
- Updated dependencies [2fe2b81]
- Updated dependencies [5596e31]
- Updated dependencies [e74fe88]
- Updated dependencies [be5df5b]
- llamaindex@0.3.5
## 0.0.5
### Patch Changes
- Updated dependencies [1dce275]
- Updated dependencies [d10533e]
- Updated dependencies [2008efe]
- Updated dependencies [5e61934]
- Updated dependencies [9e74a43]
- Updated dependencies [ee719a1]
- llamaindex@0.3.4
## 0.0.4
### Patch Changes
- Updated dependencies [e8c41c5]
- llamaindex@0.3.3
## 0.0.3
### Patch Changes
- Updated dependencies [61103b6]
- llamaindex@0.3.2
## 0.0.2
### Patch Changes
@@ -1,6 +1,6 @@
{
"name": "@llamaindex/cloudflare-worker-agent-test",
"version": "0.0.2",
"version": "0.0.6",
"type": "module",
"private": true,
"scripts": {
@@ -1,5 +1,42 @@
# @llamaindex/next-agent-test
## 0.1.6
### Patch Changes
- Updated dependencies [bc7a11c]
- Updated dependencies [2fe2b81]
- Updated dependencies [5596e31]
- Updated dependencies [e74fe88]
- Updated dependencies [be5df5b]
- llamaindex@0.3.5
## 0.1.5
### Patch Changes
- Updated dependencies [1dce275]
- Updated dependencies [d10533e]
- Updated dependencies [2008efe]
- Updated dependencies [5e61934]
- Updated dependencies [9e74a43]
- Updated dependencies [ee719a1]
- llamaindex@0.3.4
## 0.1.4
### Patch Changes
- Updated dependencies [e8c41c5]
- llamaindex@0.3.3
## 0.1.3
### Patch Changes
- Updated dependencies [61103b6]
- llamaindex@0.3.2
## 0.1.2
### Patch Changes
@@ -1,6 +1,6 @@
{
"name": "@llamaindex/next-agent-test",
"version": "0.1.2",
"version": "0.1.6",
"private": true,
"scripts": {
"dev": "next dev",
@@ -1,5 +1,11 @@
# test-edge-runtime
## 0.1.7
### Patch Changes
- @llamaindex/edge@0.3.5
## 0.1.6
### Patch Changes
@@ -1,6 +1,6 @@
{
"name": "@llamaindex/nextjs-edge-runtime-test",
"version": "0.1.6",
"version": "0.1.7",
"private": true,
"scripts": {
"dev": "next dev",
@@ -1,5 +1,42 @@
# @llamaindex/waku-query-engine-test
## 0.0.6
### Patch Changes
- Updated dependencies [bc7a11c]
- Updated dependencies [2fe2b81]
- Updated dependencies [5596e31]
- Updated dependencies [e74fe88]
- Updated dependencies [be5df5b]
- llamaindex@0.3.5
## 0.0.5
### Patch Changes
- Updated dependencies [1dce275]
- Updated dependencies [d10533e]
- Updated dependencies [2008efe]
- Updated dependencies [5e61934]
- Updated dependencies [9e74a43]
- Updated dependencies [ee719a1]
- llamaindex@0.3.4
## 0.0.4
### Patch Changes
- Updated dependencies [e8c41c5]
- llamaindex@0.3.3
## 0.0.3
### Patch Changes
- Updated dependencies [61103b6]
- llamaindex@0.3.2
## 0.0.2
### Patch Changes
@@ -1,6 +1,6 @@
{
"name": "@llamaindex/waku-query-engine-test",
"version": "0.0.2",
"version": "0.0.6",
"type": "module",
"private": true,
"scripts": {
+47 -3
View File
@@ -4,7 +4,7 @@ import { AnthropicAgent } from "llamaindex/agent/anthropic";
import { extractText } from "llamaindex/llm/utils";
import { ok, strictEqual } from "node:assert";
import { beforeEach, test } from "node:test";
import { sumNumbersTool } from "./fixtures/tools.js";
import { getWeatherTool, sumNumbersTool } from "./fixtures/tools.js";
import { mockLLMEvent } from "./utils.js";
let llm: LLM;
@@ -118,14 +118,58 @@ await test("anthropic agent", async (t) => {
});
await t.test("sum numbers", async () => {
const openaiAgent = new AnthropicAgent({
const anthropicAgent = new AnthropicAgent({
tools: [sumNumbersTool],
});
const { response } = await openaiAgent.chat({
const { response } = await anthropicAgent.chat({
message: "how much is 1 + 1?",
});
ok(extractText(response.message.content).includes("2"));
});
});
await test("anthropic agent with multiple chat", async (t) => {
await mockLLMEvent(t, "anthropic-agent-multiple-chat");
await t.test("chat", async () => {
const agent = new AnthropicAgent({
tools: [getWeatherTool],
});
{
const { response } = await agent.chat({
message: 'Hello? Response to me "Yes"',
});
consola.debug("response:", response.message.content);
ok(extractText(response.message.content).includes("Yes"));
}
{
const { response } = await agent.chat({
message: 'Hello? Response to me "No"',
});
consola.debug("response:", response.message.content);
ok(extractText(response.message.content).includes("No"));
}
{
const { response } = await agent.chat({
message: 'Hello? Response to me "Maybe"',
});
consola.debug("response:", response.message.content);
ok(extractText(response.message.content).includes("Maybe"));
}
{
const { response } = await agent.chat({
message: "What is the weather in San Francisco?",
});
consola.debug("response:", response.message.content);
ok(extractText(response.message.content).includes("72"));
}
{
const { response } = await agent.chat({
message: "What is the weather in Shanghai?",
});
consola.debug("response:", response.message.content);
ok(extractText(response.message.content).includes("72"));
}
});
});
+20
View File
@@ -27,3 +27,23 @@ await test("react agent", async (t) => {
ok(extractText(response.message.content).includes("72"));
});
});
await test("react agent stream", async (t) => {
await mockLLMEvent(t, "react-agent-stream");
await t.test("get weather", async () => {
const agent = new ReActAgent({
tools: [getWeatherTool],
});
const stream = await agent.chat({
stream: true,
message: "What is the weather like in San Francisco?",
});
let content = "";
for await (const { response } of stream) {
content += response.delta;
}
ok(content.includes("72"));
});
});
@@ -0,0 +1,552 @@
{
"llmEventStart": [
{
"id": "PRESERVE_0",
"messages": [
{
"role": "user",
"content": "Hello? Response to me \"Yes\""
}
]
},
{
"id": "PRESERVE_1",
"messages": [
{
"role": "user",
"content": "Hello? Response to me \"Yes\""
},
{
"content": [
{
"type": "text",
"text": "<thinking>\nThe user has not asked a question that requires using any of the available tools. They have simply requested that I respond with the word \"Yes\".\n</thinking>\n\nYes"
}
],
"role": "assistant",
"options": {}
},
{
"role": "user",
"content": "Hello? Response to me \"No\""
}
]
},
{
"id": "PRESERVE_2",
"messages": [
{
"role": "user",
"content": "Hello? Response to me \"Yes\""
},
{
"content": [
{
"type": "text",
"text": "<thinking>\nThe user has not asked a question that requires using any of the available tools. They have simply requested that I respond with the word \"Yes\".\n</thinking>\n\nYes"
}
],
"role": "assistant",
"options": {}
},
{
"role": "user",
"content": "Hello? Response to me \"No\""
},
{
"content": [
{
"type": "text",
"text": "<thinking>\nThe user has not asked a question that requires using any of the available tools. They have simply requested that I respond with the word \"No\".\n</thinking>\n\nNo"
}
],
"role": "assistant",
"options": {}
},
{
"role": "user",
"content": "Hello? Response to me \"Maybe\""
}
]
},
{
"id": "PRESERVE_3",
"messages": [
{
"role": "user",
"content": "Hello? Response to me \"Yes\""
},
{
"content": [
{
"type": "text",
"text": "<thinking>\nThe user has not asked a question that requires using any of the available tools. They have simply requested that I respond with the word \"Yes\".\n</thinking>\n\nYes"
}
],
"role": "assistant",
"options": {}
},
{
"role": "user",
"content": "Hello? Response to me \"No\""
},
{
"content": [
{
"type": "text",
"text": "<thinking>\nThe user has not asked a question that requires using any of the available tools. They have simply requested that I respond with the word \"No\".\n</thinking>\n\nNo"
}
],
"role": "assistant",
"options": {}
},
{
"role": "user",
"content": "Hello? Response to me \"Maybe\""
},
{
"content": [
{
"type": "text",
"text": "<thinking>\nThe user has not asked a question that requires using any of the available tools. They have simply requested that I respond with the word \"Maybe\".\n</thinking>\n\nMaybe"
}
],
"role": "assistant",
"options": {}
},
{
"role": "user",
"content": "What is the weather in San Francisco?"
}
]
},
{
"id": "PRESERVE_4",
"messages": [
{
"role": "user",
"content": "Hello? Response to me \"Yes\""
},
{
"content": [
{
"type": "text",
"text": "<thinking>\nThe user has not asked a question that requires using any of the available tools. They have simply requested that I respond with the word \"Yes\".\n</thinking>\n\nYes"
}
],
"role": "assistant",
"options": {}
},
{
"role": "user",
"content": "Hello? Response to me \"No\""
},
{
"content": [
{
"type": "text",
"text": "<thinking>\nThe user has not asked a question that requires using any of the available tools. They have simply requested that I respond with the word \"No\".\n</thinking>\n\nNo"
}
],
"role": "assistant",
"options": {}
},
{
"role": "user",
"content": "Hello? Response to me \"Maybe\""
},
{
"content": [
{
"type": "text",
"text": "<thinking>\nThe user has not asked a question that requires using any of the available tools. They have simply requested that I respond with the word \"Maybe\".\n</thinking>\n\nMaybe"
}
],
"role": "assistant",
"options": {}
},
{
"role": "user",
"content": "What is the weather in San Francisco?"
},
{
"content": [
{
"type": "text",
"text": "<thinking>\nThe user has asked for the weather in a specific city, San Francisco. The relevant tool to answer this is the getWeather function.\n\nLooking at the required parameters for getWeather:\ncity (string): The user directly provided the city \"San Francisco\"\n\nSince the required \"city\" parameter has been provided, we can proceed with the getWeather function call.\n</thinking>"
}
],
"role": "assistant",
"options": {
"toolCall": {
"id": "toolu_01Gy7Gxbx7uGmjVncGH6pubL",
"name": "getWeather",
"input": {
"city": "San Francisco"
}
}
}
},
{
"content": "The weather in San Francisco is 72 degrees",
"role": "user",
"options": {
"toolResult": {
"result": "The weather in San Francisco is 72 degrees",
"isError": false,
"id": "toolu_01Gy7Gxbx7uGmjVncGH6pubL"
}
}
}
]
},
{
"id": "PRESERVE_5",
"messages": [
{
"role": "user",
"content": "Hello? Response to me \"Yes\""
},
{
"content": [
{
"type": "text",
"text": "<thinking>\nThe user has not asked a question that requires using any of the available tools. They have simply requested that I respond with the word \"Yes\".\n</thinking>\n\nYes"
}
],
"role": "assistant",
"options": {}
},
{
"role": "user",
"content": "Hello? Response to me \"No\""
},
{
"content": [
{
"type": "text",
"text": "<thinking>\nThe user has not asked a question that requires using any of the available tools. They have simply requested that I respond with the word \"No\".\n</thinking>\n\nNo"
}
],
"role": "assistant",
"options": {}
},
{
"role": "user",
"content": "Hello? Response to me \"Maybe\""
},
{
"content": [
{
"type": "text",
"text": "<thinking>\nThe user has not asked a question that requires using any of the available tools. They have simply requested that I respond with the word \"Maybe\".\n</thinking>\n\nMaybe"
}
],
"role": "assistant",
"options": {}
},
{
"role": "user",
"content": "What is the weather in San Francisco?"
},
{
"content": [
{
"type": "text",
"text": "<thinking>\nThe user has asked for the weather in a specific city, San Francisco. The relevant tool to answer this is the getWeather function.\n\nLooking at the required parameters for getWeather:\ncity (string): The user directly provided the city \"San Francisco\"\n\nSince the required \"city\" parameter has been provided, we can proceed with the getWeather function call.\n</thinking>"
}
],
"role": "assistant",
"options": {
"toolCall": {
"id": "toolu_01Gy7Gxbx7uGmjVncGH6pubL",
"name": "getWeather",
"input": {
"city": "San Francisco"
}
}
}
},
{
"content": "The weather in San Francisco is 72 degrees",
"role": "user",
"options": {
"toolResult": {
"result": "The weather in San Francisco is 72 degrees",
"isError": false,
"id": "toolu_01Gy7Gxbx7uGmjVncGH6pubL"
}
}
},
{
"content": [
{
"type": "text",
"text": "The current weather in San Francisco is 72 degrees."
}
],
"role": "assistant",
"options": {}
},
{
"role": "user",
"content": "What is the weather in Shanghai?"
}
]
},
{
"id": "PRESERVE_6",
"messages": [
{
"role": "user",
"content": "Hello? Response to me \"Yes\""
},
{
"content": [
{
"type": "text",
"text": "<thinking>\nThe user has not asked a question that requires using any of the available tools. They have simply requested that I respond with the word \"Yes\".\n</thinking>\n\nYes"
}
],
"role": "assistant",
"options": {}
},
{
"role": "user",
"content": "Hello? Response to me \"No\""
},
{
"content": [
{
"type": "text",
"text": "<thinking>\nThe user has not asked a question that requires using any of the available tools. They have simply requested that I respond with the word \"No\".\n</thinking>\n\nNo"
}
],
"role": "assistant",
"options": {}
},
{
"role": "user",
"content": "Hello? Response to me \"Maybe\""
},
{
"content": [
{
"type": "text",
"text": "<thinking>\nThe user has not asked a question that requires using any of the available tools. They have simply requested that I respond with the word \"Maybe\".\n</thinking>\n\nMaybe"
}
],
"role": "assistant",
"options": {}
},
{
"role": "user",
"content": "What is the weather in San Francisco?"
},
{
"content": [
{
"type": "text",
"text": "<thinking>\nThe user has asked for the weather in a specific city, San Francisco. The relevant tool to answer this is the getWeather function.\n\nLooking at the required parameters for getWeather:\ncity (string): The user directly provided the city \"San Francisco\"\n\nSince the required \"city\" parameter has been provided, we can proceed with the getWeather function call.\n</thinking>"
}
],
"role": "assistant",
"options": {
"toolCall": {
"id": "toolu_01Gy7Gxbx7uGmjVncGH6pubL",
"name": "getWeather",
"input": {
"city": "San Francisco"
}
}
}
},
{
"content": "The weather in San Francisco is 72 degrees",
"role": "user",
"options": {
"toolResult": {
"result": "The weather in San Francisco is 72 degrees",
"isError": false,
"id": "toolu_01Gy7Gxbx7uGmjVncGH6pubL"
}
}
},
{
"content": [
{
"type": "text",
"text": "The current weather in San Francisco is 72 degrees."
}
],
"role": "assistant",
"options": {}
},
{
"role": "user",
"content": "What is the weather in Shanghai?"
},
{
"content": [
{
"type": "text",
"text": "<thinking>\nThe user has asked for the weather in a specific city, Shanghai. The relevant tool to answer this is the getWeather function.\n\nLooking at the required parameters for getWeather:\ncity (string): The user directly provided the city \"Shanghai\"\n\nSince the required \"city\" parameter has been provided, we can proceed with the getWeather function call.\n</thinking>"
}
],
"role": "assistant",
"options": {
"toolCall": {
"id": "toolu_01NHyahSUqrPjxQk9mvCvvGe",
"name": "getWeather",
"input": {
"city": "Shanghai"
}
}
}
},
{
"content": "The weather in Shanghai is 72 degrees",
"role": "user",
"options": {
"toolResult": {
"result": "The weather in Shanghai is 72 degrees",
"isError": false,
"id": "toolu_01NHyahSUqrPjxQk9mvCvvGe"
}
}
}
]
}
],
"llmEventEnd": [
{
"id": "PRESERVE_0",
"response": {
"raw": null,
"message": {
"content": [
{
"type": "text",
"text": "<thinking>\nThe user has not asked a question that requires using any of the available tools. They have simply requested that I respond with the word \"Yes\".\n</thinking>\n\nYes"
}
],
"role": "assistant",
"options": {}
}
}
},
{
"id": "PRESERVE_1",
"response": {
"raw": null,
"message": {
"content": [
{
"type": "text",
"text": "<thinking>\nThe user has not asked a question that requires using any of the available tools. They have simply requested that I respond with the word \"No\".\n</thinking>\n\nNo"
}
],
"role": "assistant",
"options": {}
}
}
},
{
"id": "PRESERVE_2",
"response": {
"raw": null,
"message": {
"content": [
{
"type": "text",
"text": "<thinking>\nThe user has not asked a question that requires using any of the available tools. They have simply requested that I respond with the word \"Maybe\".\n</thinking>\n\nMaybe"
}
],
"role": "assistant",
"options": {}
}
}
},
{
"id": "PRESERVE_3",
"response": {
"raw": null,
"message": {
"content": [
{
"type": "text",
"text": "<thinking>\nThe user has asked for the weather in a specific city, San Francisco. The relevant tool to answer this is the getWeather function.\n\nLooking at the required parameters for getWeather:\ncity (string): The user directly provided the city \"San Francisco\"\n\nSince the required \"city\" parameter has been provided, we can proceed with the getWeather function call.\n</thinking>"
}
],
"role": "assistant",
"options": {
"toolCall": {
"id": "toolu_01Gy7Gxbx7uGmjVncGH6pubL",
"name": "getWeather",
"input": {
"city": "San Francisco"
}
}
}
}
}
},
{
"id": "PRESERVE_4",
"response": {
"raw": null,
"message": {
"content": [
{
"type": "text",
"text": "The current weather in San Francisco is 72 degrees."
}
],
"role": "assistant",
"options": {}
}
}
},
{
"id": "PRESERVE_5",
"response": {
"raw": null,
"message": {
"content": [
{
"type": "text",
"text": "<thinking>\nThe user has asked for the weather in a specific city, Shanghai. The relevant tool to answer this is the getWeather function.\n\nLooking at the required parameters for getWeather:\ncity (string): The user directly provided the city \"Shanghai\"\n\nSince the required \"city\" parameter has been provided, we can proceed with the getWeather function call.\n</thinking>"
}
],
"role": "assistant",
"options": {
"toolCall": {
"id": "toolu_01NHyahSUqrPjxQk9mvCvvGe",
"name": "getWeather",
"input": {
"city": "Shanghai"
}
}
}
}
}
},
{
"id": "PRESERVE_6",
"response": {
"raw": null,
"message": {
"content": [
{
"type": "text",
"text": "The current weather in Shanghai is 72 degrees."
}
],
"role": "assistant",
"options": {}
}
}
}
],
"llmEventStream": []
}
@@ -0,0 +1,488 @@
{
"llmEventStart": [
{
"id": "PRESERVE_0",
"messages": [
{
"role": "system",
"content": "You are designed to help with a variety of tasks, from answering questions to providing summaries to other types of analyses.\n\n## Tools\nYou have access to a wide variety of tools. You are responsible for using\nthe tools in any sequence you deem appropriate to complete the task at hand.\nThis may require breaking the task into subtasks and using different tools\nto complete each subtask.\n\nYou have access to the following tools:\n- getWeather: Get the weather for a city with schema: {\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\",\"description\":\"The city to get the weather for\"}},\"required\":[\"city\"]}\n\n## Output Format\nTo answer the question, please use the following format.\n\n\"\"\"\nThought: I need to use a tool to help me answer the question.\nAction: tool name (one of getWeather) if using a tool.\nAction Input: the input to the tool, in a JSON format representing the kwargs (e.g. {{\"input\": \"hello world\", \"num_beams\": 5}})\n\"\"\"\n\nPlease ALWAYS start with a Thought.\n\nPlease use a valid JSON format for the Action Input. Do NOT do this {{'input': 'hello world', 'num_beams': 5}}.\n\nIf this format is used, the user will respond in the following format:\n\n\"\"\"\"\nObservation: tool response\n\"\"\"\"\n\nYou should keep repeating the above format until you have enough information\nto answer the question without using any more tools. At that point, you MUST respond\nin the one of the following two formats:\n\n\"\"\"\"\nThought: I can answer without using any more tools.\nAnswer: [your answer here]\n\"\"\"\"\n\n\"\"\"\"\nThought: I cannot answer the question with the provided tools.\nAnswer: Sorry, I cannot answer your query.\n\"\"\"\"\n\n## Current Conversation\nBelow is the current conversation consisting of interleaving human and assistant messages."
},
{
"role": "user",
"content": "What is the weather like in San Francisco?"
}
]
},
{
"id": "PRESERVE_1",
"messages": [
{
"role": "system",
"content": "You are designed to help with a variety of tasks, from answering questions to providing summaries to other types of analyses.\n\n## Tools\nYou have access to a wide variety of tools. You are responsible for using\nthe tools in any sequence you deem appropriate to complete the task at hand.\nThis may require breaking the task into subtasks and using different tools\nto complete each subtask.\n\nYou have access to the following tools:\n- getWeather: Get the weather for a city with schema: {\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\",\"description\":\"The city to get the weather for\"}},\"required\":[\"city\"]}\n\n## Output Format\nTo answer the question, please use the following format.\n\n\"\"\"\nThought: I need to use a tool to help me answer the question.\nAction: tool name (one of getWeather) if using a tool.\nAction Input: the input to the tool, in a JSON format representing the kwargs (e.g. {{\"input\": \"hello world\", \"num_beams\": 5}})\n\"\"\"\n\nPlease ALWAYS start with a Thought.\n\nPlease use a valid JSON format for the Action Input. Do NOT do this {{'input': 'hello world', 'num_beams': 5}}.\n\nIf this format is used, the user will respond in the following format:\n\n\"\"\"\"\nObservation: tool response\n\"\"\"\"\n\nYou should keep repeating the above format until you have enough information\nto answer the question without using any more tools. At that point, you MUST respond\nin the one of the following two formats:\n\n\"\"\"\"\nThought: I can answer without using any more tools.\nAnswer: [your answer here]\n\"\"\"\"\n\n\"\"\"\"\nThought: I cannot answer the question with the provided tools.\nAnswer: Sorry, I cannot answer your query.\n\"\"\"\"\n\n## Current Conversation\nBelow is the current conversation consisting of interleaving human and assistant messages."
},
{
"role": "user",
"content": "What is the weather like in San Francisco?"
},
{
"role": "assistant",
"content": "Thought: I need to use a tool to help me answer the question.\nAction: getWeather\nInput: {\n city: San Francisco\n}"
},
{
"role": "user",
"content": "Observation: The weather in San Francisco is 72 degrees"
}
]
}
],
"llmEventEnd": [
{
"id": "PRESERVE_0",
"response": {
"raw": null,
"message": {
"content": "Thought: I need to use a tool to help me answer the question.\nAction: getWeather\nAction Input: {\"city\": \"San Francisco\"}",
"role": "assistant",
"options": {}
}
}
},
{
"id": "PRESERVE_1",
"response": {
"raw": null,
"message": {
"content": "Thought: I can answer without using any more tools.\nAnswer: The weather in San Francisco is 72 degrees.",
"role": "assistant",
"options": {}
}
}
}
],
"llmEventStream": [
{
"id": "PRESERVE_0",
"chunk": {
"raw": null,
"options": {},
"delta": "Thought"
}
},
{
"id": "PRESERVE_0",
"chunk": {
"raw": null,
"options": {},
"delta": ":"
}
},
{
"id": "PRESERVE_0",
"chunk": {
"raw": null,
"options": {},
"delta": " I"
}
},
{
"id": "PRESERVE_0",
"chunk": {
"raw": null,
"options": {},
"delta": " need"
}
},
{
"id": "PRESERVE_0",
"chunk": {
"raw": null,
"options": {},
"delta": " to"
}
},
{
"id": "PRESERVE_0",
"chunk": {
"raw": null,
"options": {},
"delta": " use"
}
},
{
"id": "PRESERVE_0",
"chunk": {
"raw": null,
"options": {},
"delta": " a"
}
},
{
"id": "PRESERVE_0",
"chunk": {
"raw": null,
"options": {},
"delta": " tool"
}
},
{
"id": "PRESERVE_0",
"chunk": {
"raw": null,
"options": {},
"delta": " to"
}
},
{
"id": "PRESERVE_0",
"chunk": {
"raw": null,
"options": {},
"delta": " help"
}
},
{
"id": "PRESERVE_0",
"chunk": {
"raw": null,
"options": {},
"delta": " me"
}
},
{
"id": "PRESERVE_0",
"chunk": {
"raw": null,
"options": {},
"delta": " answer"
}
},
{
"id": "PRESERVE_0",
"chunk": {
"raw": null,
"options": {},
"delta": " the"
}
},
{
"id": "PRESERVE_0",
"chunk": {
"raw": null,
"options": {},
"delta": " question"
}
},
{
"id": "PRESERVE_0",
"chunk": {
"raw": null,
"options": {},
"delta": ".\n"
}
},
{
"id": "PRESERVE_0",
"chunk": {
"raw": null,
"options": {},
"delta": "Action"
}
},
{
"id": "PRESERVE_0",
"chunk": {
"raw": null,
"options": {},
"delta": ":"
}
},
{
"id": "PRESERVE_0",
"chunk": {
"raw": null,
"options": {},
"delta": " get"
}
},
{
"id": "PRESERVE_0",
"chunk": {
"raw": null,
"options": {},
"delta": "Weather"
}
},
{
"id": "PRESERVE_0",
"chunk": {
"raw": null,
"options": {},
"delta": "\n"
}
},
{
"id": "PRESERVE_0",
"chunk": {
"raw": null,
"options": {},
"delta": "Action"
}
},
{
"id": "PRESERVE_0",
"chunk": {
"raw": null,
"options": {},
"delta": " Input"
}
},
{
"id": "PRESERVE_0",
"chunk": {
"raw": null,
"options": {},
"delta": ":"
}
},
{
"id": "PRESERVE_0",
"chunk": {
"raw": null,
"options": {},
"delta": " {\""
}
},
{
"id": "PRESERVE_0",
"chunk": {
"raw": null,
"options": {},
"delta": "city"
}
},
{
"id": "PRESERVE_0",
"chunk": {
"raw": null,
"options": {},
"delta": "\":"
}
},
{
"id": "PRESERVE_0",
"chunk": {
"raw": null,
"options": {},
"delta": " \""
}
},
{
"id": "PRESERVE_0",
"chunk": {
"raw": null,
"options": {},
"delta": "San"
}
},
{
"id": "PRESERVE_0",
"chunk": {
"raw": null,
"options": {},
"delta": " Francisco"
}
},
{
"id": "PRESERVE_0",
"chunk": {
"raw": null,
"options": {},
"delta": "\"}"
}
},
{
"id": "PRESERVE_1",
"chunk": {
"raw": null,
"options": {},
"delta": "Thought"
}
},
{
"id": "PRESERVE_1",
"chunk": {
"raw": null,
"options": {},
"delta": ":"
}
},
{
"id": "PRESERVE_1",
"chunk": {
"raw": null,
"options": {},
"delta": " I"
}
},
{
"id": "PRESERVE_1",
"chunk": {
"raw": null,
"options": {},
"delta": " can"
}
},
{
"id": "PRESERVE_1",
"chunk": {
"raw": null,
"options": {},
"delta": " answer"
}
},
{
"id": "PRESERVE_1",
"chunk": {
"raw": null,
"options": {},
"delta": " without"
}
},
{
"id": "PRESERVE_1",
"chunk": {
"raw": null,
"options": {},
"delta": " using"
}
},
{
"id": "PRESERVE_1",
"chunk": {
"raw": null,
"options": {},
"delta": " any"
}
},
{
"id": "PRESERVE_1",
"chunk": {
"raw": null,
"options": {},
"delta": " more"
}
},
{
"id": "PRESERVE_1",
"chunk": {
"raw": null,
"options": {},
"delta": " tools"
}
},
{
"id": "PRESERVE_1",
"chunk": {
"raw": null,
"options": {},
"delta": ".\n"
}
},
{
"id": "PRESERVE_1",
"chunk": {
"raw": null,
"options": {},
"delta": "Answer"
}
},
{
"id": "PRESERVE_1",
"chunk": {
"raw": null,
"options": {},
"delta": ":"
}
},
{
"id": "PRESERVE_1",
"chunk": {
"raw": null,
"options": {},
"delta": " The"
}
},
{
"id": "PRESERVE_1",
"chunk": {
"raw": null,
"options": {},
"delta": " weather"
}
},
{
"id": "PRESERVE_1",
"chunk": {
"raw": null,
"options": {},
"delta": " in"
}
},
{
"id": "PRESERVE_1",
"chunk": {
"raw": null,
"options": {},
"delta": " San"
}
},
{
"id": "PRESERVE_1",
"chunk": {
"raw": null,
"options": {},
"delta": " Francisco"
}
},
{
"id": "PRESERVE_1",
"chunk": {
"raw": null,
"options": {},
"delta": " is"
}
},
{
"id": "PRESERVE_1",
"chunk": {
"raw": null,
"options": {},
"delta": " "
}
},
{
"id": "PRESERVE_1",
"chunk": {
"raw": null,
"options": {},
"delta": "72"
}
},
{
"id": "PRESERVE_1",
"chunk": {
"raw": null,
"options": {},
"delta": " degrees"
}
},
{
"id": "PRESERVE_1",
"chunk": {
"raw": null,
"options": {},
"delta": "."
}
}
]
}
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@llamaindex/core-e2e",
"private": true,
"version": "0.0.2",
"version": "0.0.4",
"type": "module",
"scripts": {
"e2e": "node --import tsx --import ./mock-register.js --test ./node/*.e2e.ts",
+5 -2
View File
@@ -1,8 +1,11 @@
{
"name": "@llamaindex/core",
"version": "0.3.0",
"version": "0.3.5",
"exports": "./src/index.ts",
"imports": {
"@llamaindex/env": "jsr:@llamaindex/env@0.0.6"
"@llamaindex/env": "jsr:@llamaindex/env@0.1.1"
},
"publish": {
"include": ["LICENSE", "README.md", "src/**/*"]
}
}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "llamaindex",
"version": "0.3.1",
"version": "0.3.5",
"expectedMinorVersion": "3",
"license": "MIT",
"type": "module",
@@ -10,6 +10,7 @@
"@datastax/astra-db-ts": "^1.0.1",
"@google/generative-ai": "^0.8.0",
"@grpc/grpc-js": "^1.10.6",
"@huggingface/inference": "^2.6.7",
"@llamaindex/cloud": "0.0.5",
"@llamaindex/env": "workspace:*",
"@mistralai/mistralai": "^0.1.3",
@@ -31,7 +32,6 @@
"md-utils-ts": "^2.0.0",
"mongodb": "^6.5.0",
"notion-md-crawler": "^1.0.0",
"ollama": "^0.5.0",
"openai": "^4.38.0",
"papaparse": "^5.4.1",
"pathe": "^1.1.2",
+3 -3
View File
@@ -55,9 +55,9 @@ class GlobalSettings implements Config {
get debug() {
const debug = getEnv("DEBUG");
return (
getEnv("NODE_ENV") === "development" &&
Boolean(debug) &&
debug?.includes("llamaindex")
(Boolean(debug) && debug?.includes("llamaindex")) ||
debug === "*" ||
debug === "true"
);
}
+23 -27
View File
@@ -49,6 +49,7 @@ export class AnthropicAgent extends AgentRunner<Anthropic> {
"tools" in params
? params.tools
: params.toolRetriever.retrieve.bind(params.toolRetriever),
verbose: params.verbose ?? false,
});
}
@@ -67,12 +68,8 @@ export class AnthropicAgent extends AgentRunner<Anthropic> {
return super.chat(params);
}
static taskHandler: TaskHandler<Anthropic> = async (step) => {
const { input } = step;
static taskHandler: TaskHandler<Anthropic> = async (step, enqueueOutput) => {
const { llm, getTools, stream } = step.context;
if (input) {
step.context.store.messages = [...step.context.store.messages, input];
}
const lastMessage = step.context.store.messages.at(-1)!.content;
const tools = await getTools(lastMessage);
if (stream === true) {
@@ -88,37 +85,36 @@ export class AnthropicAgent extends AgentRunner<Anthropic> {
response.message,
];
const options = response.message.options ?? {};
enqueueOutput({
taskStep: step,
output: response,
isLast: !("toolCall" in options),
});
if ("toolCall" in options) {
const { toolCall } = options;
const targetTool = tools.find(
(tool) => tool.metadata.name === toolCall.name,
);
const toolOutput = await callTool(targetTool, toolCall);
const toolOutput = await callTool(
targetTool,
toolCall,
step.context.logger,
);
step.context.store.toolOutputs.push(toolOutput);
return {
taskStep: step,
output: {
raw: response.raw,
message: {
content: stringifyJSONToMessageContent(toolOutput.output),
role: "user",
options: {
toolResult: {
result: toolOutput.output,
isError: toolOutput.isError,
id: toolCall.id,
},
step.context.store.messages = [
...step.context.store.messages,
{
content: stringifyJSONToMessageContent(toolOutput.output),
role: "user",
options: {
toolResult: {
result: toolOutput.output,
isError: toolOutput.isError,
id: toolCall.id,
},
},
},
isLast: false,
};
} else {
return {
taskStep: step,
output: response,
isLast: true,
};
];
}
};
}
+87 -67
View File
@@ -4,12 +4,14 @@ import {
pipeline,
randomUUID,
} from "@llamaindex/env";
import { Settings } from "../Settings.js";
import {
type ChatEngine,
type ChatEngineParamsNonStreaming,
type ChatEngineParamsStreaming,
} from "../engines/chat/index.js";
import { wrapEventCaller } from "../internal/context/EventCaller.js";
import { consoleLogger, emptyLogger } from "../internal/logger.js";
import { getCallbackManager } from "../internal/settings/CallbackManager.js";
import { isAsyncIterable } from "../internal/utils.js";
import type {
@@ -27,14 +29,10 @@ import type {
TaskStep,
TaskStepOutput,
} from "./types.js";
import { consumeAsyncIterable } from "./utils.js";
export const MAX_TOOL_CALLS = 10;
/**
* @internal
*/
export async function* createTaskImpl<
export function createTaskOutputStream<
Model extends LLM,
Store extends object = {},
AdditionalMessageOptions extends object = Model extends LLM<
@@ -46,65 +44,67 @@ export async function* createTaskImpl<
>(
handler: TaskHandler<Model, Store, AdditionalMessageOptions>,
context: AgentTaskContext<Model, Store, AdditionalMessageOptions>,
_input: ChatMessage<AdditionalMessageOptions>,
): AsyncGenerator<TaskStepOutput<Model, Store, AdditionalMessageOptions>> {
let isFirst = true;
let isDone = false;
let input: ChatMessage<AdditionalMessageOptions> | null = _input;
let prevStep: TaskStep<Model, Store, AdditionalMessageOptions> | null = null;
while (!isDone) {
const step: TaskStep<Model, Store, AdditionalMessageOptions> = {
id: randomUUID(),
input,
context,
prevStep,
nextSteps: new Set(),
};
if (prevStep) {
prevStep.nextSteps.add(step);
}
const prevToolCallCount = step.context.toolCallCount;
if (!step.context.shouldContinue(step)) {
throw new Error("Tool call count exceeded limit");
}
if (isFirst) {
): ReadableStream<TaskStepOutput<Model, Store, AdditionalMessageOptions>> {
const steps: TaskStep<Model, Store, AdditionalMessageOptions>[] = [];
return new ReadableStream<
TaskStepOutput<Model, Store, AdditionalMessageOptions>
>({
pull: async (controller) => {
const step: TaskStep<Model, Store, AdditionalMessageOptions> = {
id: randomUUID(),
context,
prevStep: null,
nextSteps: new Set(),
};
if (steps.length > 0) {
step.prevStep = steps[steps.length - 1];
}
const taskOutputs: TaskStepOutput<
Model,
Store,
AdditionalMessageOptions
>[] = [];
steps.push(step);
const enqueueOutput = (
output: TaskStepOutput<Model, Store, AdditionalMessageOptions>,
) => {
context.logger.log("Enqueueing output for step(id, %s).", step.id);
taskOutputs.push(output);
controller.enqueue(output);
};
getCallbackManager().dispatchEvent("agent-start", {
payload: {
startStep: step,
},
});
isFirst = false;
}
const taskOutput = await handler(step);
const { isLast, output, taskStep } = taskOutput;
// do not consume last output
if (!isLast) {
if (output) {
input = isAsyncIterable(output)
? await consumeAsyncIterable(output)
: output.message;
} else {
input = null;
}
}
context = {
...taskStep.context,
store: {
...taskStep.context.store,
},
toolCallCount: prevToolCallCount + 1,
};
if (isLast) {
isDone = true;
getCallbackManager().dispatchEvent("agent-end", {
payload: {
endStep: step,
context.logger.log("Starting step(id, %s).", step.id);
await handler(step, enqueueOutput);
context.logger.log("Finished step(id, %s).", step.id);
// fixme: support multi-thread when there are multiple outputs
// todo: for now we pretend there is only one task output
const { isLast, taskStep } = taskOutputs[0];
context = {
...taskStep.context,
store: {
...taskStep.context.store,
},
});
}
prevStep = taskStep;
yield taskOutput;
}
toolCallCount: 1,
};
if (isLast) {
context.logger.log(
"Final step(id, %s) reached, closing task.",
step.id,
);
getCallbackManager().dispatchEvent("agent-end", {
payload: {
endStep: step,
},
});
controller.close();
}
},
});
}
export type AgentStreamChatResponse<Options extends object> = {
@@ -134,6 +134,7 @@ export type AgentRunnerParams<
tools:
| BaseToolWithCall[]
| ((query: MessageContent) => Promise<BaseToolWithCall[]>);
verbose: boolean;
};
export type AgentParamsBase<
@@ -148,6 +149,7 @@ export type AgentParamsBase<
llm?: AI;
chatHistory?: ChatMessage<AdditionalMessageOptions>[];
systemPrompt?: MessageContent;
verbose?: boolean;
};
/**
@@ -170,15 +172,16 @@ export abstract class AgentWorker<
query: string,
context: AgentTaskContext<AI, Store, AdditionalMessageOptions>,
): ReadableStream<TaskStepOutput<AI, Store, AdditionalMessageOptions>> {
const taskGenerator = createTaskImpl(this.taskHandler, context, {
context.store.messages.push({
role: "user",
content: query,
});
const taskOutputStream = createTaskOutputStream(this.taskHandler, context);
return new ReadableStream<
TaskStepOutput<AI, Store, AdditionalMessageOptions>
>({
start: async (controller) => {
for await (const stepOutput of taskGenerator) {
for await (const stepOutput of taskOutputStream) {
this.#taskSet.add(stepOutput.taskStep);
controller.enqueue(stepOutput);
if (stepOutput.isLast) {
@@ -226,6 +229,7 @@ export abstract class AgentRunner<
readonly #systemPrompt: MessageContent | null = null;
#chatHistory: ChatMessage<AdditionalMessageOptions>[];
readonly #runner: AgentWorker<AI, Store, AdditionalMessageOptions>;
readonly #verbose: boolean;
// create extra store
abstract createStore(): Store;
@@ -237,14 +241,15 @@ export abstract class AgentRunner<
protected constructor(
params: AgentRunnerParams<AI, Store, AdditionalMessageOptions>,
) {
const { llm, chatHistory, runner, tools } = params;
const { llm, chatHistory, systemPrompt, runner, tools, verbose } = params;
this.#llm = llm;
this.#chatHistory = chatHistory;
this.#runner = runner;
if (params.systemPrompt) {
this.#systemPrompt = params.systemPrompt;
if (systemPrompt) {
this.#systemPrompt = systemPrompt;
}
this.#tools = tools;
this.#verbose = verbose;
}
get llm() {
@@ -255,6 +260,10 @@ export abstract class AgentRunner<
return this.#chatHistory;
}
get verbose(): boolean {
return Settings.debug || this.#verbose;
}
public reset(): void {
this.#chatHistory = [];
}
@@ -278,8 +287,11 @@ export abstract class AgentRunner<
return task.context.toolCallCount < MAX_TOOL_CALLS;
}
// fixme: this shouldn't be async
async createTask(message: MessageContent, stream: boolean = false) {
createTask(
message: MessageContent,
stream: boolean = false,
verbose: boolean | undefined = undefined,
) {
const initialMessages = [...this.#chatHistory];
if (this.#systemPrompt !== null) {
const systemPrompt = this.#systemPrompt;
@@ -304,6 +316,13 @@ export abstract class AgentRunner<
toolOutputs: [] as ToolOutput[],
},
shouldContinue: AgentRunner.shouldContinue,
logger:
// disable verbose if explicitly set to false
verbose === false
? emptyLogger
: verbose || this.verbose
? consoleLogger
: emptyLogger,
});
}
@@ -320,7 +339,7 @@ export abstract class AgentRunner<
| AgentChatResponse<AdditionalMessageOptions>
| ReadableStream<AgentStreamChatResponse<AdditionalMessageOptions>>
> {
const task = await this.createTask(params.message, !!params.stream);
const task = this.createTask(params.message, !!params.stream);
const stepOutput = await pipeline(
task,
async (
@@ -329,6 +348,8 @@ export abstract class AgentRunner<
>,
) => {
for await (const stepOutput of iter) {
// update chat history for each round
this.#chatHistory = [...stepOutput.taskStep.context.store.messages];
if (stepOutput.isLast) {
return stepOutput;
}
@@ -337,7 +358,6 @@ export abstract class AgentRunner<
},
);
const { output, taskStep } = stepOutput;
this.#chatHistory = [...taskStep.context.store.messages];
if (isAsyncIterable(output)) {
return output.pipeThrough<
AgentStreamChatResponse<AdditionalMessageOptions>
+33 -39
View File
@@ -46,17 +46,14 @@ export class OpenAIAgent extends AgentRunner<OpenAI> {
"tools" in params
? params.tools
: params.toolRetriever.retrieve.bind(params.toolRetriever),
verbose: params.verbose ?? false,
});
}
createStore = AgentRunner.defaultCreateStore;
static taskHandler: TaskHandler<OpenAI> = async (step) => {
const { input } = step;
static taskHandler: TaskHandler<OpenAI> = async (step, enqueueOutput) => {
const { llm, stream, getTools } = step.context;
if (input) {
step.context.store.messages = [...step.context.store.messages, input];
}
const lastMessage = step.context.store.messages.at(-1)!.content;
const tools = await getTools(lastMessage);
const response = await llm.chat({
@@ -71,37 +68,36 @@ export class OpenAIAgent extends AgentRunner<OpenAI> {
response.message,
];
const options = response.message.options ?? {};
enqueueOutput({
taskStep: step,
output: response,
isLast: !("toolCall" in options),
});
if ("toolCall" in options) {
const { toolCall } = options;
const targetTool = tools.find(
(tool) => tool.metadata.name === toolCall.name,
);
const toolOutput = await callTool(targetTool, toolCall);
const toolOutput = await callTool(
targetTool,
toolCall,
step.context.logger,
);
step.context.store.toolOutputs.push(toolOutput);
return {
taskStep: step,
output: {
raw: response.raw,
message: {
content: stringifyJSONToMessageContent(toolOutput.output),
role: "user",
options: {
toolResult: {
result: toolOutput.output,
isError: toolOutput.isError,
id: toolCall.id,
},
step.context.store.messages = [
...step.context.store.messages,
{
role: "user" as const,
content: stringifyJSONToMessageContent(toolOutput.output),
options: {
toolResult: {
result: toolOutput.output,
isError: toolOutput.isError,
id: toolCall.id,
},
},
},
isLast: false,
};
} else {
return {
taskStep: step,
output: response,
isLast: true,
};
];
}
} else {
const responseChunkStream = new ReadableStream<
@@ -126,6 +122,11 @@ export class OpenAIAgent extends AgentRunner<OpenAI> {
// check if first chunk has tool calls, if so, this is a function call
// otherwise, it's a regular message
const hasToolCall = !!(value.options && "toolCall" in value.options);
enqueueOutput({
taskStep: step,
output: finalStream,
isLast: !hasToolCall,
});
if (hasToolCall) {
// you need to consume the response to get the full toolCalls
@@ -158,7 +159,11 @@ export class OpenAIAgent extends AgentRunner<OpenAI> {
},
},
];
const toolOutput = await callTool(targetTool, toolCall);
const toolOutput = await callTool(
targetTool,
toolCall,
step.context.logger,
);
step.context.store.messages = [
...step.context.store.messages,
{
@@ -175,17 +180,6 @@ export class OpenAIAgent extends AgentRunner<OpenAI> {
];
step.context.store.toolOutputs.push(toolOutput);
}
return {
taskStep: step,
output: null,
isLast: false,
};
} else {
return {
taskStep: step,
output: finalStream,
isLast: true,
};
}
}
};
+76 -64
View File
@@ -1,5 +1,4 @@
import { pipeline, randomUUID } from "@llamaindex/env";
import { Settings } from "../Settings.js";
import { randomUUID, ReadableStream } from "@llamaindex/env";
import { getReACTAgentSystemHeader } from "../internal/prompt/react.js";
import {
isAsyncIterable,
@@ -13,6 +12,7 @@ import {
} from "../llm/index.js";
import { extractText } from "../llm/utils.js";
import { ObjectRetriever } from "../objects/index.js";
import { Settings } from "../Settings.js";
import type {
BaseTool,
BaseToolWithCall,
@@ -60,7 +60,7 @@ type ActionReason = BaseReason & {
type ResponseReason = BaseReason & {
type: "response";
thought: string;
response: ChatResponse | AsyncIterable<ChatResponseChunk>;
response: ChatResponse;
};
type Reason = ObservationReason | ActionReason | ResponseReason;
@@ -74,16 +74,9 @@ function reasonFormatter(reason: Reason): string | Promise<string> {
reason.input,
)}`;
case "response": {
if (isAsyncIterable(reason.response)) {
return consumeAsyncIterable(reason.response).then(
(message) =>
`Thought: ${reason.thought}\nAnswer: ${extractText(message.content)}`,
);
} else {
return `Thought: ${reason.thought}\nAnswer: ${extractText(
reason.response.message.content,
)}`;
}
return `Thought: ${reason.thought}\nAnswer: ${extractText(
reason.response.message.content,
)}`;
}
}
}
@@ -146,35 +139,52 @@ function actionInputParser(jsonStr: string): JSONObject {
type ReACTOutputParser = <Options extends object>(
output: ChatResponse<Options> | AsyncIterable<ChatResponseChunk<Options>>,
onResolveType: (
type: "action" | "thought" | "answer",
response:
| ChatResponse<Options>
| ReadableStream<ChatResponseChunk<Options>>,
) => void,
) => Promise<Reason>;
const reACTOutputParser: ReACTOutputParser = async (
output,
onResolveType,
): Promise<Reason> => {
let reason: Reason | null = null;
if (isAsyncIterable(output)) {
const [peakStream, finalStream] = createReadableStream(output).tee();
const type = await pipeline(peakStream, async (iter) => {
let content = "";
for await (const chunk of iter) {
content += chunk.delta;
if (content.includes("Action:")) {
return "action";
} else if (content.includes("Answer:")) {
return "answer";
} else if (content.includes("Thought:")) {
return "thought";
}
const reader = peakStream.getReader();
let type: "action" | "thought" | "answer" | null = null;
let content = "";
do {
const { done, value } = await reader.read();
if (done) {
break;
}
});
content += value.delta;
if (content.includes("Action:")) {
type = "action";
} else if (content.includes("Answer:")) {
type = "answer";
}
} while (true);
if (type === null) {
// `Thought:` is always present at the beginning of the output.
type = "thought";
}
reader.releaseLock();
if (!type) {
throw new Error("Could not determine type of output");
}
onResolveType(type, finalStream);
// step 2: do the parsing from content
switch (type) {
case "action": {
// have to consume the stream to get the full content
const response = await consumeAsyncIterable(finalStream);
const { content } = response;
const [thought, action, input] = extractToolUse(content);
const response = await consumeAsyncIterable(peakStream, content);
const [thought, action, input] = extractToolUse(response.content);
const jsonStr = extractJsonStr(input);
let json: JSONObject;
try {
@@ -192,18 +202,20 @@ const reACTOutputParser: ReACTOutputParser = async (
}
case "thought": {
const thought = "(Implicit) I can answer without any more tools!";
const response = await consumeAsyncIterable(peakStream, content);
reason = {
type: "response",
thought,
// bypass the response, because here we don't need to do anything with it
response: finalStream,
response: {
raw: peakStream,
message: response,
},
};
break;
}
case "answer": {
const response = await consumeAsyncIterable(finalStream);
const { content } = response;
const [thought, answer] = extractFinalResponse(content);
const response = await consumeAsyncIterable(peakStream, content);
const [thought, answer] = extractFinalResponse(response.content);
reason = {
type: "response",
thought,
@@ -227,7 +239,9 @@ const reACTOutputParser: ReACTOutputParser = async (
? "answer"
: content.includes("Action:")
? "action"
: "thought";
: // `Thought:` is always present at the beginning of the output.
"thought";
onResolveType(type, output);
// step 2: do the parsing from content
switch (type) {
@@ -340,6 +354,7 @@ export class ReActAgent extends AgentRunner<LLM, ReACTAgentStore> {
"tools" in params
? params.tools
: params.toolRetriever.retrieve.bind(params.toolRetriever),
verbose: params.verbose ?? false,
});
}
@@ -349,12 +364,11 @@ export class ReActAgent extends AgentRunner<LLM, ReACTAgentStore> {
};
}
static taskHandler: TaskHandler<LLM, ReACTAgentStore> = async (step) => {
static taskHandler: TaskHandler<LLM, ReACTAgentStore> = async (
step,
enqueueOutput,
) => {
const { llm, stream, getTools } = step.context;
const input = step.input;
if (input) {
step.context.store.messages.push(input);
}
const lastMessage = step.context.store.messages.at(-1)!.content;
const tools = await getTools(lastMessage);
const messages = await chatFormatter(
@@ -367,35 +381,33 @@ export class ReActAgent extends AgentRunner<LLM, ReACTAgentStore> {
stream,
messages,
});
const reason = await reACTOutputParser(response);
step.context.store.reasons = [...step.context.store.reasons, reason];
if (reason.type === "response") {
return {
isLast: true,
output: response,
const reason = await reACTOutputParser(response, (type, response) => {
enqueueOutput({
taskStep: step,
};
} else {
if (reason.type === "action") {
const tool = tools.find((tool) => tool.metadata.name === reason.action);
const toolOutput = await callTool(tool, {
output: response,
isLast: type !== "action",
});
});
step.context.logger.log("current reason: %O", reason);
step.context.store.reasons = [...step.context.store.reasons, reason];
if (reason.type === "action") {
const tool = tools.find((tool) => tool.metadata.name === reason.action);
const toolOutput = await callTool(
tool,
{
id: randomUUID(),
input: reason.input,
name: reason.action,
});
step.context.store.reasons = [
...step.context.store.reasons,
{
type: "observation",
observation: toolOutput.output,
},
];
}
return {
isLast: false,
output: null,
taskStep: step,
};
},
step.context.logger,
);
step.context.store.reasons = [
...step.context.store.reasons,
{
type: "observation",
observation: toolOutput.output,
},
];
}
};
}
+14 -18
View File
@@ -1,4 +1,5 @@
import { ReadableStream } from "@llamaindex/env";
import type { Logger } from "../internal/logger.js";
import type { BaseEvent } from "../internal/type.js";
import type {
ChatMessage,
@@ -32,6 +33,7 @@ export type AgentTaskContext<
toolOutputs: ToolOutput[];
messages: ChatMessage<AdditionalMessageOptions>[];
} & Store;
logger: Readonly<Logger>;
};
export type TaskStep<
@@ -45,7 +47,6 @@ export type TaskStep<
: never,
> = {
id: UUID;
input: ChatMessage<AdditionalMessageOptions> | null;
context: AgentTaskContext<Model, Store, AdditionalMessageOptions>;
// linked list
@@ -62,22 +63,14 @@ export type TaskStepOutput<
>
? AdditionalMessageOptions
: never,
> =
| {
taskStep: TaskStep<Model, Store, AdditionalMessageOptions>;
output:
| null
| ChatResponse<AdditionalMessageOptions>
| ReadableStream<ChatResponseChunk<AdditionalMessageOptions>>;
isLast: false;
}
| {
taskStep: TaskStep<Model, Store, AdditionalMessageOptions>;
output:
| ChatResponse<AdditionalMessageOptions>
| ReadableStream<ChatResponseChunk<AdditionalMessageOptions>>;
isLast: true;
};
> = {
taskStep: TaskStep<Model, Store, AdditionalMessageOptions>;
// output shows the response to the user
output:
| ChatResponse<AdditionalMessageOptions>
| ReadableStream<ChatResponseChunk<AdditionalMessageOptions>>;
isLast: boolean;
};
export type TaskHandler<
Model extends LLM,
@@ -90,7 +83,10 @@ export type TaskHandler<
: never,
> = (
step: TaskStep<Model, Store, AdditionalMessageOptions>,
) => Promise<TaskStepOutput<Model, Store, AdditionalMessageOptions>>;
enqueueOutput: (
taskOutput: TaskStepOutput<Model, Store, AdditionalMessageOptions>,
) => void,
) => Promise<void>;
export type AgentStartEvent = BaseEvent<{
startStep: TaskStep;
+17 -1
View File
@@ -1,4 +1,5 @@
import { ReadableStream } from "@llamaindex/env";
import type { Logger } from "../internal/logger.js";
import { getCallbackManager } from "../internal/settings/CallbackManager.js";
import { isAsyncIterable, prettifyError } from "../internal/utils.js";
import type {
@@ -13,12 +14,14 @@ import type { BaseTool, JSONObject, JSONValue, ToolOutput } from "../types.js";
export async function callTool(
tool: BaseTool | undefined,
toolCall: ToolCall | PartialToolCall,
logger: Logger,
): Promise<ToolOutput> {
const input: JSONObject =
typeof toolCall.input === "string"
? JSON.parse(toolCall.input)
: toolCall.input;
if (!tool) {
logger.error(`Tool ${toolCall.name} does not exist.`);
const output = `Tool ${toolCall.name} does not exist.`;
return {
tool,
@@ -30,6 +33,9 @@ export async function callTool(
const call = tool.call;
let output: JSONValue;
if (!call) {
logger.error(
`Tool ${tool.metadata.name} (remote:${toolCall.name}) does not have a implementation.`,
);
output = `Tool ${tool.metadata.name} (remote:${toolCall.name}) does not have a implementation.`;
return {
tool,
@@ -45,6 +51,10 @@ export async function callTool(
},
});
output = await call.call(tool, input);
logger.log(
`Tool ${tool.metadata.name} (remote:${toolCall.name}) succeeded.`,
);
logger.log(`Output: ${JSON.stringify(output)}`);
const toolOutput: ToolOutput = {
tool,
input,
@@ -60,6 +70,9 @@ export async function callTool(
return toolOutput;
} catch (e) {
output = prettifyError(e);
logger.error(
`Tool ${tool.metadata.name} (remote:${toolCall.name}) failed: ${output}`,
);
}
return {
tool,
@@ -71,16 +84,19 @@ export async function callTool(
export async function consumeAsyncIterable<Options extends object>(
input: ChatMessage<Options>,
previousContent?: string,
): Promise<ChatMessage<Options>>;
export async function consumeAsyncIterable<Options extends object>(
input: AsyncIterable<ChatResponseChunk<Options>>,
previousContent?: string,
): Promise<TextChatMessage<Options>>;
export async function consumeAsyncIterable<Options extends object>(
input: ChatMessage<Options> | AsyncIterable<ChatResponseChunk<Options>>,
previousContent: string = "",
): Promise<ChatMessage<Options>> {
if (isAsyncIterable(input)) {
const result: ChatMessage<Options> = {
content: "",
content: previousContent,
// only assistant will give streaming response
role: "assistant",
options: {} as Options,
@@ -212,10 +212,13 @@ export class CallbackManager implements CallbackManagerMethods {
if (!handlers) {
return;
}
const clone = structuredClone(detail);
queueMicrotask(() => {
handlers.forEach((handler) =>
handler(LlamaIndexCustomEvent.fromEvent(event, clone)),
handler(
LlamaIndexCustomEvent.fromEvent(event, {
...detail,
}),
),
);
});
}
+5
View File
@@ -13,6 +13,11 @@ export interface ChatEngineParamsBase {
* Optional chat history if you want to customize the chat history.
*/
chatHistory?: ChatMessage[] | ChatHistory;
/**
* Optional flag to enable verbose mode.
* @default false
*/
verbose?: boolean;
}
export interface ChatEngineParamsStreaming extends ChatEngineParamsBase {
+1
View File
@@ -27,6 +27,7 @@ export * from "./objects/index.js";
export * from "./postprocessors/index.js";
export * from "./prompts/index.js";
export * from "./selectors/index.js";
export * from "./storage/StorageContext.js";
export * from "./synthesizers/index.js";
export * from "./tools/index.js";
export * from "./types.js";
+16 -5
View File
@@ -279,18 +279,29 @@ export class VectorStoreIndex extends BaseIndex<IndexDict> {
return new VectorIndexRetriever({ index: this, ...options });
}
/**
* Create a RetrieverQueryEngine.
* similarityTopK is only used if no existing retriever is provided.
*/
asQueryEngine(options?: {
retriever?: BaseRetriever;
responseSynthesizer?: BaseSynthesizer;
preFilters?: MetadataFilters;
nodePostprocessors?: BaseNodePostprocessor[];
similarityTopK?: number;
}): QueryEngine & RetrieverQueryEngine {
const { retriever, responseSynthesizer } = options ?? {};
return new RetrieverQueryEngine(
retriever ?? this.asRetriever(),
const {
retriever,
responseSynthesizer,
options?.preFilters,
options?.nodePostprocessors,
preFilters,
nodePostprocessors,
similarityTopK,
} = options ?? {};
return new RetrieverQueryEngine(
retriever ?? this.asRetriever({ similarityTopK }),
responseSynthesizer,
preFilters,
nodePostprocessors,
);
}
+264
View File
@@ -0,0 +1,264 @@
type Fetch = typeof fetch;
interface Config {
host: string;
fetch?: Fetch;
proxy?: boolean;
}
interface Options {
numa: boolean;
num_ctx: number;
num_batch: number;
main_gpu: number;
low_vram: boolean;
f16_kv: boolean;
logits_all: boolean;
vocab_only: boolean;
use_mmap: boolean;
use_mlock: boolean;
embedding_only: boolean;
num_thread: number;
num_keep: number;
seed: number;
num_predict: number;
top_k: number;
top_p: number;
tfs_z: number;
typical_p: number;
repeat_last_n: number;
temperature: number;
repeat_penalty: number;
presence_penalty: number;
frequency_penalty: number;
mirostat: number;
mirostat_tau: number;
mirostat_eta: number;
penalize_newline: boolean;
stop: string[];
}
interface GenerateRequest {
model: string;
prompt: string;
system?: string;
template?: string;
context?: number[];
stream?: boolean;
raw?: boolean;
format?: string;
images?: Uint8Array[] | string[];
keep_alive?: string | number;
options?: Partial<Options>;
}
interface Message {
role: string;
content: string;
images?: Uint8Array[] | string[];
}
interface ChatRequest {
model: string;
messages?: Message[];
stream?: boolean;
format?: string;
keep_alive?: string | number;
options?: Partial<Options>;
}
interface PullRequest {
model: string;
insecure?: boolean;
stream?: boolean;
}
interface PushRequest {
model: string;
insecure?: boolean;
stream?: boolean;
}
interface CreateRequest {
model: string;
path?: string;
modelfile?: string;
stream?: boolean;
}
interface DeleteRequest {
model: string;
}
interface CopyRequest {
source: string;
destination: string;
}
interface ShowRequest {
model: string;
system?: string;
template?: string;
options?: Partial<Options>;
}
interface EmbeddingsRequest {
model: string;
prompt: string;
keep_alive?: string | number;
options?: Partial<Options>;
}
interface GenerateResponse {
model: string;
created_at: Date;
response: string;
done: boolean;
context: number[];
total_duration: number;
load_duration: number;
prompt_eval_count: number;
prompt_eval_duration: number;
eval_count: number;
eval_duration: number;
}
interface ChatResponse {
model: string;
created_at: Date;
message: Message;
done: boolean;
total_duration: number;
load_duration: number;
prompt_eval_count: number;
prompt_eval_duration: number;
eval_count: number;
eval_duration: number;
}
interface EmbeddingsResponse {
embedding: number[];
}
interface ProgressResponse {
status: string;
digest: string;
total: number;
completed: number;
}
interface ModelResponse {
name: string;
modified_at: Date;
size: number;
digest: string;
details: ModelDetails;
}
interface ModelDetails {
parent_model: string;
format: string;
family: string;
families: string[];
parameter_size: string;
quantization_level: string;
}
interface ShowResponse {
license: string;
modelfile: string;
parameters: string;
template: string;
system: string;
details: ModelDetails;
messages: Message[];
}
interface ListResponse {
models: ModelResponse[];
}
interface ErrorResponse {
error: string;
}
interface StatusResponse {
status: string;
}
declare class Ollama {
protected readonly config: Config;
protected readonly fetch: Fetch;
private abortController;
constructor(config?: Partial<Config>);
abort(): void;
protected processStreamableRequest<T extends object>(
endpoint: string,
request: {
stream?: boolean;
} & Record<string, any>,
): Promise<T | AsyncGenerator<T>>;
encodeImage(image: Uint8Array | string): Promise<string>;
generate(
request: GenerateRequest & {
stream: true;
},
): Promise<AsyncGenerator<GenerateResponse>>;
generate(
request: GenerateRequest & {
stream?: false;
},
): Promise<GenerateResponse>;
chat(
request: ChatRequest & {
stream: true;
},
): Promise<AsyncGenerator<ChatResponse>>;
chat(
request: ChatRequest & {
stream?: false;
},
): Promise<ChatResponse>;
create(
request: CreateRequest & {
stream: true;
},
): Promise<AsyncGenerator<ProgressResponse>>;
create(
request: CreateRequest & {
stream?: false;
},
): Promise<ProgressResponse>;
pull(
request: PullRequest & {
stream: true;
},
): Promise<AsyncGenerator<ProgressResponse>>;
pull(
request: PullRequest & {
stream?: false;
},
): Promise<ProgressResponse>;
push(
request: PushRequest & {
stream: true;
},
): Promise<AsyncGenerator<ProgressResponse>>;
push(
request: PushRequest & {
stream?: false;
},
): Promise<ProgressResponse>;
delete(request: DeleteRequest): Promise<StatusResponse>;
copy(request: CopyRequest): Promise<StatusResponse>;
list(): Promise<ListResponse>;
show(request: ShowRequest): Promise<ShowResponse>;
embeddings(request: EmbeddingsRequest): Promise<EmbeddingsResponse>;
}
declare const _default: Ollama;
export {
Ollama,
_default as default,
type ChatRequest,
type ChatResponse,
type Config,
type CopyRequest,
type CreateRequest,
type DeleteRequest,
type EmbeddingsRequest,
type EmbeddingsResponse,
type ErrorResponse,
type Fetch,
type GenerateRequest,
type GenerateResponse,
type ListResponse,
type Message,
type ModelDetails,
type ModelResponse,
type Options,
type ProgressResponse,
type PullRequest,
type PushRequest,
type ShowRequest,
type ShowResponse,
type StatusResponse,
};
+462
View File
@@ -0,0 +1,462 @@
// generate from "tsup ./src/browser.js --format esm --dts"
var __defProp = Object.defineProperty;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __knownSymbol = (name, symbol) => {
return (symbol = Symbol[name]) ? symbol : Symbol.for("Symbol." + name);
};
var __defNormalProp = (obj, key, value) =>
key in obj
? __defProp(obj, key, {
enumerable: true,
configurable: true,
writable: true,
value,
})
: (obj[key] = value);
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]);
}
return a;
};
var __async = (__this, __arguments, generator) => {
return new Promise((resolve, reject) => {
var fulfilled = (value) => {
try {
step(generator.next(value));
} catch (e) {
reject(e);
}
};
var rejected = (value) => {
try {
step(generator.throw(value));
} catch (e) {
reject(e);
}
};
var step = (x) =>
x.done
? resolve(x.value)
: Promise.resolve(x.value).then(fulfilled, rejected);
step((generator = generator.apply(__this, __arguments)).next());
});
};
var __await = function (promise, isYieldStar) {
this[0] = promise;
this[1] = isYieldStar;
};
var __asyncGenerator = (__this, __arguments, generator) => {
var resume = (k, v, yes, no) => {
try {
var x = generator[k](v),
isAwait = (v = x.value) instanceof __await,
done = x.done;
Promise.resolve(isAwait ? v[0] : v)
.then((y) =>
isAwait
? resume(
k === "return" ? k : "next",
v[1] ? { done: y.done, value: y.value } : y,
yes,
no,
)
: yes({ value: y, done }),
)
.catch((e) => resume("throw", e, yes, no));
} catch (e) {
no(e);
}
};
var method = (k) =>
(it[k] = (x) => new Promise((yes, no) => resume(k, x, yes, no)));
var it = {};
return (
(generator = generator.apply(__this, __arguments)),
(it[__knownSymbol("asyncIterator")] = () => it),
method("next"),
method("throw"),
method("return"),
it
);
};
var __forAwait = (obj, it, method) =>
(it = obj[__knownSymbol("asyncIterator")])
? it.call(obj)
: ((obj = obj[__knownSymbol("iterator")]()),
(it = {}),
(method = (key, fn) =>
(fn = obj[key]) &&
(it[key] = (arg) =>
new Promise(
(yes, no, done) => (
(arg = fn.call(obj, arg)),
(done = arg.done),
Promise.resolve(arg.value).then(
(value) => yes({ value, done }),
no,
)
),
))),
method("next"),
method("return"),
it);
// src/version.ts
var version = "0.0.0";
// src/utils.ts
var ResponseError = class _ResponseError extends Error {
constructor(error, status_code) {
super(error);
this.error = error;
this.status_code = status_code;
this.name = "ResponseError";
if (Error.captureStackTrace) {
Error.captureStackTrace(this, _ResponseError);
}
}
};
var checkOk = (response) =>
__async(void 0, null, function* () {
var _a;
if (!response.ok) {
let message = `Error ${response.status}: ${response.statusText}`;
let errorData = null;
if (
(_a = response.headers.get("content-type")) == null
? void 0
: _a.includes("application/json")
) {
try {
errorData = yield response.json();
message = errorData.error || message;
} catch (error) {
console.log("Failed to parse error response as JSON");
}
} else {
try {
console.log("Getting text from response");
const textResponse = yield response.text();
message = textResponse || message;
} catch (error) {
console.log("Failed to get text from error response");
}
}
throw new ResponseError(message, response.status);
}
});
function getPlatform() {
if (typeof window !== "undefined" && window.navigator) {
return `${window.navigator.platform.toLowerCase()} Browser/${navigator.userAgent};`;
} else if (typeof process !== "undefined") {
return `${process.arch} ${process.platform} Node.js/${process.version}`;
}
return "";
}
var fetchWithHeaders = (_0, _1, ..._2) =>
__async(void 0, [_0, _1, ..._2], function* (fetch2, url, options = {}) {
const defaultHeaders = {
"Content-Type": "application/json",
Accept: "application/json",
"User-Agent": `ollama-js/${version} (${getPlatform()})`,
};
if (!options.headers) {
options.headers = {};
}
options.headers = __spreadValues(
__spreadValues({}, defaultHeaders),
options.headers,
);
return fetch2(url, options);
});
var get = (fetch2, host) =>
__async(void 0, null, function* () {
const response = yield fetchWithHeaders(fetch2, host);
yield checkOk(response);
return response;
});
var post = (fetch2, host, data, options) =>
__async(void 0, null, function* () {
const isRecord = (input) => {
return (
input !== null && typeof input === "object" && !Array.isArray(input)
);
};
const formattedData = isRecord(data) ? JSON.stringify(data) : data;
const response = yield fetchWithHeaders(fetch2, host, {
method: "POST",
body: formattedData,
signal: options == null ? void 0 : options.signal,
});
yield checkOk(response);
return response;
});
var del = (fetch2, host, data) =>
__async(void 0, null, function* () {
const response = yield fetchWithHeaders(fetch2, host, {
method: "DELETE",
body: JSON.stringify(data),
});
yield checkOk(response);
return response;
});
var parseJSON = function (itr) {
return __asyncGenerator(this, null, function* () {
var _a;
const decoder = new TextDecoder("utf-8");
let buffer = "";
const reader = itr.getReader();
while (true) {
const { done, value: chunk } = yield new __await(reader.read());
if (done) {
break;
}
buffer += decoder.decode(chunk);
const parts = buffer.split("\n");
buffer = (_a = parts.pop()) != null ? _a : "";
for (const part of parts) {
try {
yield JSON.parse(part);
} catch (error) {
console.warn("invalid json: ", part);
}
}
}
for (const part of buffer.split("\n").filter((p) => p !== "")) {
try {
yield JSON.parse(part);
} catch (error) {
console.warn("invalid json: ", part);
}
}
});
};
var formatHost = (host) => {
if (!host) {
return "http://127.0.0.1:11434";
}
let isExplicitProtocol = host.includes("://");
if (host.startsWith(":")) {
host = `http://127.0.0.1${host}`;
isExplicitProtocol = false;
}
if (!isExplicitProtocol) {
host = `http://${host}`;
}
const url = new URL(host);
let port = url.port;
if (!port) {
if (!isExplicitProtocol) {
port = "11434";
} else {
port = url.protocol === "https:" ? "443" : "80";
}
}
let formattedHost = `${url.protocol}//${url.hostname}:${port}${url.pathname}`;
if (formattedHost.endsWith("/")) {
formattedHost = formattedHost.slice(0, -1);
}
return formattedHost;
};
// src/browser.ts
// import "whatwg-fetch";
var Ollama = class {
constructor(config) {
var _a;
this.config = {
host: "",
};
if (!(config == null ? void 0 : config.proxy)) {
this.config.host = formatHost(
(_a = config == null ? void 0 : config.host) != null
? _a
: "http://127.0.0.1:11434",
);
}
this.fetch = fetch;
if ((config == null ? void 0 : config.fetch) != null) {
this.fetch = config.fetch;
}
this.abortController = new AbortController();
}
// Abort any ongoing requests to Ollama
abort() {
this.abortController.abort();
this.abortController = new AbortController();
}
processStreamableRequest(endpoint, request) {
return __async(this, null, function* () {
var _a;
request.stream = (_a = request.stream) != null ? _a : false;
const response = yield post(
this.fetch,
`${this.config.host}/api/${endpoint}`,
__spreadValues({}, request),
{ signal: this.abortController.signal },
);
if (!response.body) {
throw new Error("Missing body");
}
const itr = parseJSON(response.body);
if (request.stream) {
return (function () {
return __asyncGenerator(this, null, function* () {
try {
for (
var iter = __forAwait(itr), more, temp, error;
(more = !(temp = yield new __await(iter.next())).done);
more = false
) {
const message = temp.value;
if ("error" in message) {
throw new Error(message.error);
}
yield message;
if (message.done || message.status === "success") {
return;
}
}
} catch (temp) {
error = [temp];
} finally {
try {
more &&
(temp = iter.return) &&
(yield new __await(temp.call(iter)));
} finally {
if (error) throw error[0];
}
}
throw new Error(
"Did not receive done or success response in stream.",
);
});
})();
} else {
const message = yield itr.next();
if (!message.value.done && message.value.status !== "success") {
throw new Error("Expected a completed response.");
}
return message.value;
}
});
}
encodeImage(image) {
return __async(this, null, function* () {
if (typeof image !== "string") {
const uint8Array = new Uint8Array(image);
const numberArray = Array.from(uint8Array);
const base64String = btoa(String.fromCharCode.apply(null, numberArray));
return base64String;
}
return image;
});
}
generate(request) {
return __async(this, null, function* () {
if (request.images) {
request.images = yield Promise.all(
request.images.map(this.encodeImage.bind(this)),
);
}
return this.processStreamableRequest("generate", request);
});
}
chat(request) {
return __async(this, null, function* () {
if (request.messages) {
for (const message of request.messages) {
if (message.images) {
message.images = yield Promise.all(
message.images.map(this.encodeImage.bind(this)),
);
}
}
}
return this.processStreamableRequest("chat", request);
});
}
create(request) {
return __async(this, null, function* () {
return this.processStreamableRequest("create", {
name: request.model,
stream: request.stream,
modelfile: request.modelfile,
});
});
}
pull(request) {
return __async(this, null, function* () {
return this.processStreamableRequest("pull", {
name: request.model,
stream: request.stream,
insecure: request.insecure,
});
});
}
push(request) {
return __async(this, null, function* () {
return this.processStreamableRequest("push", {
name: request.model,
stream: request.stream,
insecure: request.insecure,
});
});
}
delete(request) {
return __async(this, null, function* () {
yield del(this.fetch, `${this.config.host}/api/delete`, {
name: request.model,
});
return { status: "success" };
});
}
copy(request) {
return __async(this, null, function* () {
yield post(
this.fetch,
`${this.config.host}/api/copy`,
__spreadValues({}, request),
);
return { status: "success" };
});
}
list() {
return __async(this, null, function* () {
const response = yield get(this.fetch, `${this.config.host}/api/tags`);
const listResponse = yield response.json();
return listResponse;
});
}
show(request) {
return __async(this, null, function* () {
const response = yield post(
this.fetch,
`${this.config.host}/api/show`,
__spreadValues({}, request),
);
const showResponse = yield response.json();
return showResponse;
});
}
embeddings(request) {
return __async(this, null, function* () {
const response = yield post(
this.fetch,
`${this.config.host}/api/embeddings`,
__spreadValues({}, request),
);
const embeddingsResponse = yield response.json();
return embeddingsResponse;
});
}
};
var browser_default = new Ollama();
export { Ollama, browser_default as default };
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Saul
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+17
View File
@@ -0,0 +1,17 @@
export type Logger = {
log: (...args: unknown[]) => void;
error: (...args: unknown[]) => void;
warn: (...args: unknown[]) => void;
};
export const emptyLogger: Logger = Object.freeze({
log: () => {},
error: () => {},
warn: () => {},
});
export const consoleLogger: Logger = Object.freeze({
log: console.log.bind(console),
error: console.error.bind(console),
warn: console.warn.bind(console),
});
+95 -49
View File
@@ -156,58 +156,104 @@ export class Anthropic extends ToolCallLLM<AnthropicAdditionalChatOptions> {
formatMessages<Beta = false>(
messages: ChatMessage<ToolCallLLMMessageOptions>[],
): Beta extends true ? ToolsBetaMessageParam[] : MessageParam[] {
return messages.map<any>((message) => {
if (message.role !== "user" && message.role !== "assistant") {
throw new Error("Unsupported Anthropic role");
}
const options = message.options ?? {};
if ("toolResult" in options) {
const { id, isError } = options.toolResult;
return {
role: "user",
content: [
{
type: "tool_result",
is_error: isError,
content: [
{
type: "text",
text: extractText(message.content),
},
],
tool_use_id: id,
},
] satisfies ToolResultBlockParam[],
} satisfies ToolsBetaMessageParam;
} else if ("toolCall" in options) {
const aiThinkingText = extractText(message.content);
return {
role: "assistant",
content: [
// this could be empty when you call two tools in one query
...(aiThinkingText.trim()
? [
const result: ToolsBetaMessageParam[] = messages
.filter(
(message) => message.role === "user" || message.role === "assistant",
)
.map((message) => {
const options = message.options ?? {};
if ("toolResult" in options) {
const { id, isError } = options.toolResult;
return {
role: "user",
content: [
{
type: "tool_result",
is_error: isError,
content: [
{
type: "text",
text: aiThinkingText,
} satisfies TextBlockParam,
]
: []),
{
type: "tool_use",
id: options.toolCall.id,
name: options.toolCall.name,
input: options.toolCall.input,
} satisfies ToolUseBlockParam,
] satisfies ToolsBetaContentBlock[],
} satisfies ToolsBetaMessageParam;
}
text: extractText(message.content),
},
],
tool_use_id: id,
},
] satisfies ToolResultBlockParam[],
} satisfies ToolsBetaMessageParam;
} else if ("toolCall" in options) {
const aiThinkingText = extractText(message.content);
return {
role: "assistant",
content: [
// this could be empty when you call two tools in one query
...(aiThinkingText.trim()
? [
{
type: "text",
text: aiThinkingText,
} satisfies TextBlockParam,
]
: []),
{
type: "tool_use",
id: options.toolCall.id,
name: options.toolCall.name,
input: options.toolCall.input,
} satisfies ToolUseBlockParam,
] satisfies ToolsBetaContentBlock[],
} satisfies ToolsBetaMessageParam;
}
return {
content: extractText(message.content),
role: message.role,
} satisfies MessageParam;
});
return {
content: extractText(message.content),
role: message.role as "user" | "assistant",
} satisfies MessageParam;
});
// merge messages with the same role
// in case of 'messages: roles must alternate between "user" and "assistant", but found multiple "user" roles in a row'
const realResult: ToolsBetaMessageParam[] = [];
for (let i = 0; i < result.length; i++) {
if (i === 0) {
realResult.push(result[i]);
continue;
}
const current = result[i];
const previous = result[i - 1];
if (current.role === previous.role) {
// merge two messages with the same role
if (Array.isArray(previous.content)) {
if (Array.isArray(current.content)) {
previous.content.push(...current.content);
} else {
previous.content.push({
type: "text",
text: current.content,
});
}
} else {
if (Array.isArray(current.content)) {
previous.content = [
{
type: "text",
text: previous.content,
},
...current.content,
];
} else {
previous.content += `\n${current.content}`;
}
}
// no need to push the message
}
// if the roles are different, just push the message
else {
realResult.push(current);
}
}
return realResult as Beta extends true
? ToolsBetaMessageParam[]
: MessageParam[];
}
chat(
+4 -6
View File
@@ -302,12 +302,10 @@ export class Gemini extends ToolCallLLM<GeminiAdditionalChatOptions> {
): GeminiChatStreamResponse {
const { chat, messageContent } = this.prepareChat(params);
const result = await chat.sendMessageStream(messageContent);
return streamConverter(result.stream, (response) => {
return {
text: response.text(),
raw: response,
};
});
yield* streamConverter(result.stream, (response) => ({
delta: response.text(),
raw: response,
}));
}
chat(params: GeminiChatParamsStreaming): Promise<GeminiChatStreamResponse>;
+141
View File
@@ -0,0 +1,141 @@
import {
HfInference,
type Options as HfInferenceOptions,
} from "@huggingface/inference";
import { BaseLLM } from "./base.js";
import type {
ChatMessage,
ChatResponse,
ChatResponseChunk,
LLMChatParamsNonStreaming,
LLMChatParamsStreaming,
LLMMetadata,
ToolCallLLMMessageOptions,
} from "./types.js";
import { streamConverter, wrapLLMEvent } from "./utils.js";
const DEFAULT_PARAMS = {
temperature: 0.1,
topP: 1,
maxTokens: undefined,
contextWindow: 3900,
};
export type HFConfig = Partial<typeof DEFAULT_PARAMS> &
HfInferenceOptions & {
model: string;
accessToken: string;
endpoint?: string;
};
/**
Wrapper on the Hugging Face's Inference API.
API Docs: https://huggingface.co/docs/huggingface.js/inference/README
List of tasks with models: huggingface.co/api/tasks
Note that Conversational API is not yet supported by the Inference API.
They recommend using the text generation API instead.
See: https://github.com/huggingface/huggingface.js/issues/586#issuecomment-2024059308
*/
export class HuggingFaceInferenceAPI extends BaseLLM {
model: string;
temperature: number;
topP: number;
maxTokens?: number;
contextWindow: number;
hf: HfInference;
constructor(init: HFConfig) {
super();
const {
model,
temperature,
topP,
maxTokens,
contextWindow,
accessToken,
endpoint,
...hfInferenceOpts
} = init;
this.hf = new HfInference(accessToken, hfInferenceOpts);
this.model = model;
this.temperature = temperature ?? DEFAULT_PARAMS.temperature;
this.topP = topP ?? DEFAULT_PARAMS.topP;
this.maxTokens = maxTokens ?? DEFAULT_PARAMS.maxTokens;
this.contextWindow = contextWindow ?? DEFAULT_PARAMS.contextWindow;
if (endpoint) this.hf.endpoint(endpoint);
}
get metadata(): LLMMetadata {
return {
model: this.model,
temperature: this.temperature,
topP: this.topP,
maxTokens: this.maxTokens,
contextWindow: this.contextWindow,
tokenizer: undefined,
};
}
chat(
params: LLMChatParamsStreaming,
): Promise<AsyncIterable<ChatResponseChunk>>;
chat(params: LLMChatParamsNonStreaming): Promise<ChatResponse>;
@wrapLLMEvent
async chat(
params: LLMChatParamsStreaming | LLMChatParamsNonStreaming,
): Promise<AsyncIterable<ChatResponseChunk> | ChatResponse<object>> {
if (params.stream) return this.streamChat(params);
return this.nonStreamChat(params);
}
private messagesToPrompt(messages: ChatMessage<ToolCallLLMMessageOptions>[]) {
let prompt = "";
for (const message of messages) {
if (message.role === "system") {
prompt += `<|system|>\n${message.content}</s>\n`;
} else if (message.role === "user") {
prompt += `<|user|>\n${message.content}</s>\n`;
} else if (message.role === "assistant") {
prompt += `<|assistant|>\n${message.content}</s>\n`;
}
}
// ensure we start with a system prompt, insert blank if needed
if (!prompt.startsWith("<|system|>\n")) {
prompt = "<|system|>\n</s>\n" + prompt;
}
// add final assistant prompt
prompt = prompt + "<|assistant|>\n";
return prompt;
}
protected async nonStreamChat(
params: LLMChatParamsNonStreaming,
): Promise<ChatResponse> {
const res = await this.hf.textGeneration({
model: this.model,
inputs: this.messagesToPrompt(params.messages),
parameters: this.metadata,
});
return {
raw: res,
message: {
content: res.generated_text,
role: "assistant",
},
};
}
protected async *streamChat(
params: LLMChatParamsStreaming,
): AsyncIterable<ChatResponseChunk> {
const stream = this.hf.textGenerationStream({
model: this.model,
inputs: this.messagesToPrompt(params.messages),
parameters: this.metadata,
});
yield* streamConverter(stream, (chunk) => ({
delta: chunk.token.text,
raw: chunk,
}));
}
}
+1
View File
@@ -7,6 +7,7 @@ export {
export { FireworksLLM } from "./fireworks.js";
export { GEMINI_MODEL, Gemini } from "./gemini.js";
export { Groq } from "./groq.js";
export { HuggingFaceInferenceAPI } from "./huggingface.js";
export {
ALL_AVAILABLE_MISTRAL_MODELS,
MistralAI,
+2 -2
View File
@@ -1,3 +1,4 @@
import { BaseEmbedding } from "../embeddings/types.js";
import ollama, {
type CreateRequest,
type ChatResponse as OllamaChatResponse,
@@ -5,8 +6,7 @@ import ollama, {
type Options,
type ProgressResponse,
type ShowRequest,
} from "ollama/browser";
import { BaseEmbedding } from "../embeddings/types.js";
} from "../internal/deps/ollama.js";
import type {
ChatResponse,
ChatResponseChunk,
@@ -106,12 +106,32 @@ export class ChromaVectorStore implements VectorStore {
throw new Error("ChromaDB does not support querying by mode");
}
const chromaWhere: { [x: string]: string | number | boolean } = {};
// fixme: type is broken
let chromaWhere: any = {};
if (query.filters?.filters) {
query.filters.filters.map((filter) => {
const filterKey = filter.key;
const filterValue = filter.value;
chromaWhere[filterKey] = filterValue;
if (filterKey in chromaWhere || "$or" in chromaWhere) {
if (!chromaWhere["$or"]) {
chromaWhere = {
$or: [
{
...chromaWhere,
},
{
[filterKey]: filterValue,
},
],
};
} else {
chromaWhere["$or"].push({
[filterKey]: filterValue,
});
}
} else {
chromaWhere[filterKey] = filterValue;
}
});
}
@@ -130,6 +150,7 @@ export class ChromaVectorStore implements VectorStore {
IncludeEnum.Embeddings,
],
});
const vectorStoreQueryResult: VectorStoreQueryResult = {
nodes: queryResponse.ids[0].map((id, index) => {
const text = (queryResponse.documents as string[][])[0][index];
@@ -250,7 +250,7 @@ export class PGVectorStore implements VectorStore {
options?: any,
): Promise<VectorStoreQueryResult> {
// TODO QUERY TYPES:
// Distance: SELECT embedding <-> $1 AS distance FROM items;
// Distance: SELECT embedding <=> $1 AS distance FROM items;
// Inner Product: SELECT (embedding <#> $1) * -1 AS inner_product FROM items;
// Cosine Sim: SELECT 1 - (embedding <=> $1) AS cosine_similarity FROM items;
@@ -273,7 +273,7 @@ export class PGVectorStore implements VectorStore {
const sql = `SELECT
v.*,
embeddings <-> $1 s
embeddings <=> $1 s
FROM ${this.schemaName}.${this.tableName} v
${where}
ORDER BY s
+6
View File
@@ -1,5 +1,11 @@
# @llamaindex/core-test
## 0.0.3
### Patch Changes
- be5df5b: fix: anthropic agent on multiple chat
## 0.0.2
### Patch Changes
+66
View File
@@ -0,0 +1,66 @@
import { setEnvs } from "@llamaindex/env";
import { Anthropic } from "llamaindex";
import { beforeAll, describe, expect, test } from "vitest";
beforeAll(() => {
setEnvs({
ANTHROPIC_API_KEY: "valid",
});
});
describe("Anthropic llm", () => {
test("format messages", () => {
const anthropic = new Anthropic();
expect(
anthropic.formatMessages([
{
content: "You are a helpful assistant.",
role: "assistant",
},
{
content: "Hello?",
role: "user",
},
]),
).toEqual([
{
content: "You are a helpful assistant.",
role: "assistant",
},
{
content: "Hello?",
role: "user",
},
]);
expect(
anthropic.formatMessages([
{
content: "You are a helpful assistant.",
role: "assistant",
},
{
content: "Hello?",
role: "user",
},
{
content: "I am a system message.",
role: "system",
},
{
content: "What is your name?",
role: "user",
},
]),
).toEqual([
{
content: "You are a helpful assistant.",
role: "assistant",
},
{
content: "Hello?\nWhat is your name?",
role: "user",
},
]);
});
});
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@llamaindex/core-test",
"private": true,
"version": "0.0.2",
"version": "0.0.3",
"type": "module",
"scripts": {
"test": "vitest run"
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@llamaindex/edge",
"version": "0.3.1",
"version": "0.3.5",
"license": "MIT",
"type": "module",
"dependencies": {
@@ -9,6 +9,7 @@
"@datastax/astra-db-ts": "^1.0.1",
"@google/generative-ai": "^0.8.0",
"@grpc/grpc-js": "^1.10.6",
"@huggingface/inference": "^2.6.7",
"@llamaindex/cloud": "0.0.5",
"@llamaindex/env": "workspace:*",
"@mistralai/mistralai": "^0.1.3",
@@ -30,7 +31,6 @@
"md-utils-ts": "^2.0.0",
"mongodb": "^6.5.0",
"notion-md-crawler": "^1.0.0",
"ollama": "^0.5.0",
"openai": "^4.38.0",
"papaparse": "^5.4.1",
"pathe": "^1.1.2",
+6
View File
@@ -1,5 +1,11 @@
# @llamaindex/env
## 0.1.1
### Patch Changes
- 5596e31: feat: improve `@llamaindex/env`
## 0.1.0
### Minor Changes
+4 -1
View File
@@ -1,8 +1,11 @@
{
"name": "@llamaindex/env",
"version": "0.1.0",
"version": "0.1.1",
"exports": {
".": "./src/index.ts",
"./type": "./src/type.ts"
},
"publish": {
"include": ["LICENSE", "README.md", "src/**/*.ts"]
}
}
+13 -2
View File
@@ -1,7 +1,7 @@
{
"name": "@llamaindex/env",
"description": "environment wrapper",
"version": "0.1.0",
"description": "environment wrapper, supports all JS environment including node, deno, bun, edge runtime, and cloudflare worker",
"version": "0.1.1",
"type": "module",
"types": "dist/type/index.d.ts",
"main": "dist/cjs/index.js",
@@ -74,5 +74,16 @@
"@aws-crypto/sha256-js": "^5.2.0",
"pathe": "^1.1.2",
"readable-stream": "^4.5.2"
},
"peerDependenciesMeta": {
"@aws-crypto/sha256-js": {
"optional": true
},
"pathe": {
"optional": true
},
"readable-stream": {
"optional": true
}
}
}
+13
View File
@@ -1,3 +1,16 @@
/**
* This module is under Node.js environment.
* It provides a set of APIs to interact with the file system, streams, and other Node.js built-in modules.
*
* Use this under "node" condition,
*
* For example:
* ```shell
* node -e "const env = require('@llamaindex/env');"
* ```
*
* @module
*/
import { ok } from "node:assert";
import { createHash, randomUUID } from "node:crypto";
import fs from "node:fs/promises";
+7
View File
@@ -1,3 +1,10 @@
/**
* This module is under Cloudflare Workers environment.
*
* Most of Node.js APIs are not available in Cloudflare Workers environment.
*
* @module
*/
import { INTERNAL_ENV } from "./utils.js";
export * from "./index.polyfill.js";
+24
View File
@@ -1,6 +1,30 @@
// DO NOT EXPOSE THIS VARIABLE TO PUBLIC, IT IS USED INTERNALLY FOR CLOUDFLARE WORKER
export const INTERNAL_ENV: Record<string, string> = {};
/**
* Set environment variables before using llamaindex, because some LLM need to access API key before running.
*
* You have to set the environment variables in Cloudflare Worker environment,
* because it doesn't have any global environment variables.
*
* @example
* ```ts
* export default {
* async fetch(
* request: Request,
* env: Env,
* ctx: ExecutionContext,
* ): Promise<Response> {
* const { setEnvs } = await import("@llamaindex/env");
* setEnvs(env);
* // ...
* return new Response("Hello, World!");
* },
* };
* ```
*
* @param envs Environment variables
*/
export function setEnvs(envs: object): void {
Object.assign(INTERNAL_ENV, envs);
}
+37
View File
@@ -1,5 +1,42 @@
# @llamaindex/experimental
## 0.0.22
### Patch Changes
- Updated dependencies [bc7a11c]
- Updated dependencies [2fe2b81]
- Updated dependencies [5596e31]
- Updated dependencies [e74fe88]
- Updated dependencies [be5df5b]
- llamaindex@0.3.5
## 0.0.21
### Patch Changes
- Updated dependencies [1dce275]
- Updated dependencies [d10533e]
- Updated dependencies [2008efe]
- Updated dependencies [5e61934]
- Updated dependencies [9e74a43]
- Updated dependencies [ee719a1]
- llamaindex@0.3.4
## 0.0.20
### Patch Changes
- Updated dependencies [e8c41c5]
- llamaindex@0.3.3
## 0.0.19
### Patch Changes
- Updated dependencies [61103b6]
- llamaindex@0.3.2
## 0.0.18
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@llamaindex/experimental",
"description": "Experimental package for LlamaIndexTS",
"version": "0.0.18",
"version": "0.0.22",
"type": "module",
"types": "dist/type/index.d.ts",
"main": "dist/cjs/index.js",
+1517 -1186
View File
File diff suppressed because it is too large Load Diff
+24
View File
@@ -26,6 +26,30 @@ if (minorVersion !== expectedMinorVersion) {
process.exit(1);
}
const packages = ["env", "core"];
const envPackageJson = JSON.parse(
fs.readFileSync("./packages/env/package.json", "utf8"),
);
for (const pkg of packages) {
const packageJson = JSON.parse(
fs.readFileSync(`./packages/${pkg}/package.json`, "utf8"),
);
const jsrJson = JSON.parse(
fs.readFileSync(`./packages/${pkg}/jsr.json`, "utf8"),
);
jsrJson.version = packageJson.version;
if (pkg === "core") {
jsrJson.imports["@llamaindex/env"] =
`jsr:@llamaindex/env@${envPackageJson.version}`;
}
fs.writeFileSync(
`./packages/${pkg}/jsr.json`,
JSON.stringify(jsrJson, null, 2) + "\n",
);
}
console.log("Current expected minor version is: " + expectedMinorVersion);
console.log("Minor version is: " + minorVersion);
console.log("Good to go!");
+3
View File
@@ -35,6 +35,9 @@
{
"path": "./packages/core/e2e/examples/nextjs-edge-runtime/tsconfig.json"
},
{
"path": "./packages/core/e2e/examples/waku-query-engine/tsconfig.json"
},
{
"path": "./packages/core/tests/tsconfig.json"
},