Compare commits

...

8 Commits

Author SHA1 Message Date
thucpn b3b7fc5551 test: crypto compatity with node18 2025-03-19 07:42:58 +07:00
ANKIT VARSHNEY 2a0a899d66 chore: added saftey setting as parameter for gemini (#1760) 2025-03-18 23:05:47 +07:00
ANKIT VARSHNEY 050cd53450 fix: delete by id in pinecone vector store (#1758) 2025-03-18 23:03:42 +07:00
George He 5189b446f4 fix: add retry handling logic to parser reader and fix lint issues (#1757)
Co-authored-by: Alex Yang <himself65@outlook.com>
2025-03-17 14:50:38 -07:00
Thuc Pham c7ff3233fe feat: @llamaindex/tools (#1755) 2025-03-17 21:20:24 +07:00
Jack Qian 21bebfcaa6 docs: add missing links (#1754) 2025-03-16 13:32:19 +07:00
ANKIT VARSHNEY 91a18e7057 feat: add support for structured output with zod schema. (#1749) 2025-03-16 11:58:28 +08:00
ANKIT VARSHNEY d1c1f99e06 feat: function calling support in mistral provider (#1756) 2025-03-15 12:18:36 +07:00
80 changed files with 2669 additions and 284 deletions
+7
View File
@@ -0,0 +1,7 @@
---
"@llamaindex/readers": patch
"@llamaindex/core": patch
"@llamaindex/doc": patch
---
Expose more content to fix the issue with unavailable documentation links, and adjust the documentation based on the latest code.
+5
View File
@@ -0,0 +1,5 @@
---
"@llamaindex/google": patch
---
Added saftey setting parameter for gemini
+5
View File
@@ -0,0 +1,5 @@
---
"@llamaindex/pinecone": minor
---
Fix deleting of document by id in PineconeVectorStore
+13
View File
@@ -0,0 +1,13 @@
---
"@llamaindex/huggingface": minor
"@llamaindex/anthropic": minor
"@llamaindex/mistral": minor
"@llamaindex/google": minor
"@llamaindex/ollama": minor
"@llamaindex/openai": minor
"@llamaindex/core": minor
"@llamaindex/examples": minor
---
Added support for structured output in the chat api of openai and ollama
Added structured output parameter in the provider
+5
View File
@@ -0,0 +1,5 @@
---
"@llamaindex/tools": patch
---
feat: @llamaindex/tools
+8
View File
@@ -0,0 +1,8 @@
---
"@llamaindex/mistral": minor
"@llamaindex/examples": minor
---
Added support for function calling in mistral provider
Update model list for mistral provider
Added example for the tool call in mistral
+8
View File
@@ -0,0 +1,8 @@
---
"@llamaindex/cloud": patch
"@llamaindex/community": patch
"@llamaindex/core": patch
"@llamaindex/readers": patch
---
fix: add retry handling logic to parser reader and fix lint issues
+7 -3
View File
@@ -162,7 +162,12 @@ async function validateLinks(): Promise<LinkValidationResult[]> {
const invalidLinks = links.filter(({ link }) => {
// Check if the link exists in valid routes
// First normalize the link (remove any query string or hash)
const normalizedLink = link.split("#")[0].split("?")[0];
const baseLink = link.split("?")[0].split("#")[0];
// Remove the trailing slash if present.
// This works with links like "api/interfaces/MetadataFilter#operator" and "api/interfaces/MetadataFilter/#operator".
const normalizedLink = baseLink.endsWith("/")
? baseLink.slice(0, -1)
: baseLink;
// Remove llamaindex/ prefix if it exists as it's the root of the docs
let routePath = normalizedLink;
@@ -192,8 +197,7 @@ async function main() {
try {
// Check for invalid internal links
const validationResults: LinkValidationResult[] = [];
await validateLinks();
const validationResults: LinkValidationResult[] = await validateLinks();
// Check for relative links
const relativeLinksResults = await findRelativeLinks();
@@ -35,7 +35,7 @@ Currently, the following readers are mapped to specific file types:
- [TextFileReader](/docs/api/classes/TextFileReader): `.txt`
- [PDFReader](/docs/api/classes/PDFReader): `.pdf`
- [PapaCSVReader](/docs/api/classes/PapaCSVReader): `.csv`
- [CSVReader](/docs/api/classes/CSVReader): `.csv`
- [MarkdownReader](/docs/api/classes/MarkdownReader): `.md`
- [DocxReader](/docs/api/classes/DocxReader): `.docx`
- [HTMLReader](/docs/api/classes/HTMLReader): `.htm`, `.html`
@@ -12,5 +12,5 @@ Check the [LlamaIndexTS Github](https://github.com/run-llama/LlamaIndexTS) for t
## API Reference
- [BaseChatStore](/docs/api/interfaces/BaseChatStore)
- [BaseChatStore](/docs/api/classes/BaseChatStore)
@@ -74,4 +74,4 @@ the response is not correct with a score of 2.5
## API Reference
- [CorrectnessEvaluator](/docs/api/classes/CorrectnessEvaluator)
- [CorrectnessEvaluator](/docs/api/classes/CorrectnessEvaluator)
@@ -28,14 +28,21 @@ Answer:`;
### 1. Customizing the default prompt on initialization
The first method is to create a new instance of `ResponseSynthesizer` (or the module you would like to update the prompt) and pass the custom prompt to the `responseBuilder` parameter. Then, pass the instance to the `asQueryEngine` method of the index.
The first method is to create a new instance of a Response Synthesizer (or the module you would like to update the prompt) by using the getResponseSynthesizer function. Instead of passing the custom prompt to the deprecated responseBuilder parameter, call getResponseSynthesizer with the mode as the first argument and supply the new prompt via the options parameter.
```ts
// Create an instance of response synthesizer
// Create an instance of Response Synthesizer
// Deprecated usage:
const responseSynthesizer = new ResponseSynthesizer({
responseBuilder: new CompactAndRefine(undefined, newTextQaPrompt),
});
// Current usage:
const responseSynthesizer = getResponseSynthesizer('compact', {
textQATemplate: newTextQaPrompt
})
// Create index
const index = await VectorStoreIndex.fromDocuments([document]);
@@ -75,5 +82,5 @@ const response = await queryEngine.query({
## API Reference
- [ResponseSynthesizer](/docs/api/classes/ResponseSynthesizer)
- [Response Synthesizer](/docs/llamaindex/modules/response_synthesizer)
- [CompactAndRefine](/docs/api/classes/CompactAndRefine)
@@ -1,5 +1,5 @@
---
title: ResponseSynthesizer
title: Response Synthesizer
---
The ResponseSynthesizer is responsible for sending the query, nodes, and prompt templates to the LLM to generate a response. There are a few key modes for generating a response:
@@ -12,15 +12,17 @@ The ResponseSynthesizer is responsible for sending the query, nodes, and prompt
multiple compact prompts. The same as `refine`, but should result in less LLM calls.
- `TreeSummarize`: Given a set of text chunks and the query, recursively construct a tree
and return the root node as the response. Good for summarization purposes.
- `SimpleResponseBuilder`: Given a set of text chunks and the query, apply the query to each text
chunk while accumulating the responses into an array. Returns a concatenated string of all
responses. Good for when you need to run the same query separately against each text
chunk.
- `MultiModal`: Combines textual inputs with additional modality-specific metadata to generate an integrated response.
It leverages a text QA template to build a prompt that incorporates various input types and produces either streaming or complete responses.
This approach is ideal for use cases where enriching the answer with multi-modal context (such as images, audio, or other data)
can enhance the output quality.
```typescript
import { NodeWithScore, TextNode, ResponseSynthesizer } from "llamaindex";
import { NodeWithScore, TextNode, getResponseSynthesizer, responseModeSchema } from "llamaindex";
const responseSynthesizer = new ResponseSynthesizer();
// you can also use responseModeSchema.Enum.refine, responseModeSchema.Enum.tree_summarize, responseModeSchema.Enum.multi_modal
// or you can use the CompactAndRefine, Refine, TreeSummarize, or MultiModal classes directly
const responseSynthesizer = getResponseSynthesizer(responseModeSchema.Enum.compact);
const nodesWithScore: NodeWithScore[] = [
{
@@ -55,8 +57,9 @@ for await (const chunk of stream) {
## API Reference
- [ResponseSynthesizer](/docs/api/classes/ResponseSynthesizer)
- [getResponseSynthesizer](/docs/api/functions/getResponseSynthesizer)
- [responseModeSchema](/docs/api/variables/responseModeSchema)
- [Refine](/docs/api/classes/Refine)
- [CompactAndRefine](/docs/api/classes/CompactAndRefine)
- [TreeSummarize](/docs/api/classes/TreeSummarize)
- [SimpleResponseBuilder](/docs/api/classes/SimpleResponseBuilder)
- [MultiModal](/docs/api/classes/MultiModal)
+6 -1
View File
@@ -1,8 +1,13 @@
{
"plugin": ["typedoc-plugin-markdown", "typedoc-plugin-merge-modules"],
"entryPoints": ["../../packages/**/src/index.ts"],
"entryPoints": [
"../../packages/{,**/}index.ts",
"../../packages/readers/src/*.ts",
"../../packages/cloud/src/{reader,utils}.ts"
],
"exclude": [
"../../packages/autotool/**/src/index.ts",
"../../packages/cloud/src/client/index.ts",
"**/node_modules/**",
"**/dist/**",
"**/test/**",
+1
View File
@@ -42,6 +42,7 @@ export class OpenAI implements LLM {
contextWindow: 2048,
tokenizer: undefined,
isFunctionCallingModel: true,
structuredOutput: false,
};
}
+3
View File
@@ -50,6 +50,9 @@ export default tseslint.config(
"**/lib/*",
"**/deps/**",
"**/.next/**",
"**/.source/**", // Ignore .source directories
"!.git", // Don't ignore .git directory
"**/.*", // Ignore all dot files and directories
"**/node_modules/**",
"**/build/**",
"**/.docusaurus/**",
+2 -3
View File
@@ -1,13 +1,12 @@
import { OpenAI } from "@llamaindex/openai";
import { wiki } from "@llamaindex/tools";
import { AgentStream, agent } from "llamaindex";
import { WikipediaTool } from "../wiki";
async function main() {
const llm = new OpenAI({ model: "gpt-4-turbo" });
const wikiTool = new WikipediaTool();
const workflow = agent({
tools: [wikiTool],
tools: [wiki()],
llm,
verbose: false,
});
+2 -2
View File
@@ -10,7 +10,7 @@ import {
import os from "os";
import { z } from "zod";
import { WikipediaTool } from "../wiki";
import { wiki } from "@llamaindex/tools";
const llm = openai({
model: "gpt-4o-mini",
});
@@ -46,7 +46,7 @@ async function main() {
description:
"Responsible for gathering relevant information from the internet",
systemPrompt: `You are a research agent. Your role is to gather information from the internet using the provided tools and then transfer this information to the report agent for content creation.`,
tools: [new WikipediaTool()],
tools: [wiki()],
canHandoffTo: [reportAgent],
llm,
});
+2 -2
View File
@@ -1,7 +1,7 @@
import { anthropic } from "@llamaindex/anthropic";
import { wiki } from "@llamaindex/tools";
import { agent, tool } from "llamaindex";
import { z } from "zod";
import { WikipediaTool } from "../wiki";
const workflow = agent({
tools: [
@@ -13,7 +13,7 @@ const workflow = agent({
}),
execute: ({ location }) => `The weather in ${location} is sunny`,
}),
new WikipediaTool(),
wiki(),
],
llm: anthropic({
apiKey: process.env.ANTHROPIC_API_KEY,
+39 -12
View File
@@ -1,4 +1,5 @@
import { OpenAI } from "@llamaindex/openai";
import { z } from "zod";
// Example using OpenAI's chat API to extract JSON from a sales call transcript
// using json_mode see https://platform.openai.com/docs/guides/text-generation/json-mode for more details
@@ -6,22 +7,47 @@ import { OpenAI } from "@llamaindex/openai";
const transcript =
"[Phone rings]\n\nJohn: Hello, this is John.\n\nSarah: Hi John, this is Sarah from XYZ Company. I'm calling to discuss our new product, the XYZ Widget, and see if it might be a good fit for your business.\n\nJohn: Hi Sarah, thanks for reaching out. I'm definitely interested in learning more about the XYZ Widget. Can you give me a quick overview of what it does?\n\nSarah: Of course! The XYZ Widget is a cutting-edge tool that helps businesses streamline their workflow and improve productivity. It's designed to automate repetitive tasks and provide real-time data analytics to help you make informed decisions.\n\nJohn: That sounds really interesting. I can see how that could benefit our team. Do you have any case studies or success stories from other companies who have used the XYZ Widget?\n\nSarah: Absolutely, we have several case studies that I can share with you. I'll send those over along with some additional information about the product. I'd also love to schedule a demo for you and your team to see the XYZ Widget in action.\n\nJohn: That would be great. I'll make sure to review the case studies and then we can set up a time for the demo. In the meantime, are there any specific action items or next steps we should take?\n\nSarah: Yes, I'll send over the information and then follow up with you to schedule the demo. In the meantime, feel free to reach out if you have any questions or need further information.\n\nJohn: Sounds good, I appreciate your help Sarah. I'm looking forward to learning more about the XYZ Widget and seeing how it can benefit our business.\n\nSarah: Thank you, John. I'll be in touch soon. Have a great day!\n\nJohn: You too, bye.";
const exampleSchema = z.object({
summary: z.string(),
products: z.array(z.string()),
rep_name: z.string(),
prospect_name: z.string(),
action_items: z.array(z.string()),
});
const example = {
summary:
"High-level summary of the call transcript. Should not exceed 3 sentences.",
products: ["product 1", "product 2"],
rep_name: "Name of the sales rep",
prospect_name: "Name of the prospect",
action_items: ["action item 1", "action item 2"],
};
async function main() {
const llm = new OpenAI({
model: "gpt-4-1106-preview",
additionalChatOptions: { response_format: { type: "json_object" } },
model: "gpt-4o",
});
const example = {
summary:
"High-level summary of the call transcript. Should not exceed 3 sentences.",
products: ["product 1", "product 2"],
rep_name: "Name of the sales rep",
prospect_name: "Name of the prospect",
action_items: ["action item 1", "action item 2"],
};
//response format as zod schema
const response = await llm.chat({
messages: [
{
role: "system",
content: `You are an expert assistant for summarizing and extracting insights from sales call transcripts.`,
},
{
role: "user",
content: `Here is the transcript: \n------\n${transcript}\n------`,
},
],
responseFormat: exampleSchema,
});
console.log(response.message.content);
//response format as json_object
const response2 = await llm.chat({
messages: [
{
role: "system",
@@ -34,9 +60,10 @@ async function main() {
content: `Here is the transcript: \n------\n${transcript}\n------`,
},
],
responseFormat: { type: "json_object" },
});
console.log(response.message.content);
console.log(response2.message.content);
}
main().catch(console.error);
+31
View File
@@ -0,0 +1,31 @@
import { mistral } from "@llamaindex/mistral";
import { wiki } from "@llamaindex/tools";
import { agent, tool } from "llamaindex";
import { z } from "zod";
const workflow = agent({
tools: [
tool({
name: "weather",
description: "Get the weather",
parameters: z.object({
location: z.string().describe("The location to get the weather for"),
}),
execute: ({ location }) => `The weather in ${location} is sunny`,
}),
wiki(),
],
llm: mistral({
apiKey: process.env.MISTRAL_API_KEY,
model: "mistral-small-latest",
}),
});
async function main() {
const result = await workflow.run(
"What is the weather in New York? What's the history of New York from Wikipedia in 3 sentences?",
);
console.log(result.data);
}
void main();
+1
View File
@@ -49,6 +49,7 @@
"@llamaindex/together": "^0.0.5",
"@llamaindex/jinaai": "^0.0.5",
"@llamaindex/perplexity": "^0.0.2",
"@llamaindex/tools": "^0.0.1",
"@notionhq/client": "^2.2.15",
"@pinecone-database/pinecone": "^4.0.0",
"@vercel/postgres": "^0.10.0",
+2 -2
View File
@@ -1,7 +1,7 @@
import { openai } from "@ai-sdk/openai";
import { wiki } from "@llamaindex/tools";
import { VercelLLM } from "@llamaindex/vercel";
import { LLMAgent } from "llamaindex";
import { WikipediaTool } from "../wiki";
async function main() {
// Create an instance of VercelLLM with the OpenAI model
@@ -33,7 +33,7 @@ async function main() {
console.log("\n=== Test 3: Using LLMAgent with WikipediaTool ===");
const agent = new LLMAgent({
llm: vercelLLM,
tools: [new WikipediaTool()],
tools: [wiki()],
});
const { message } = await agent.chat({
-62
View File
@@ -1,62 +0,0 @@
/** Example of a tool that uses Wikipedia */
import type { JSONSchemaType } from "ajv";
import type { BaseTool, ToolMetadata } from "llamaindex";
import { default as wiki } from "wikipedia";
type WikipediaParameter = {
query: string;
lang?: string;
};
type WikipediaToolParams = {
metadata?: ToolMetadata<JSONSchemaType<WikipediaParameter>>;
};
const DEFAULT_META_DATA: ToolMetadata<JSONSchemaType<WikipediaParameter>> = {
name: "wikipedia_search",
description: "A tool that uses a query engine to search Wikipedia.",
parameters: {
type: "object",
properties: {
query: {
type: "string",
description: "The query to search for",
},
lang: {
type: "string",
description: "The language to search in",
nullable: true,
},
},
required: ["query"],
},
};
export class WikipediaTool implements BaseTool<WikipediaParameter> {
private readonly DEFAULT_LANG = "en";
metadata: ToolMetadata<JSONSchemaType<WikipediaParameter>>;
constructor(params?: WikipediaToolParams) {
this.metadata = params?.metadata || DEFAULT_META_DATA;
}
async loadData(
page: string,
lang: string = this.DEFAULT_LANG,
): Promise<string> {
wiki.setLang(lang);
const pageResult = await wiki.page(page, { autoSuggest: false });
const content = await pageResult.content();
return content;
}
async call({
query,
lang = this.DEFAULT_LANG,
}: WikipediaParameter): Promise<string> {
const searchResult = await wiki.search(query);
if (searchResult.results.length === 0) return "No search results.";
return await this.loadData(searchResult.results[0].title, lang);
}
}
+3
View File
@@ -46,5 +46,8 @@
"*.{json,md,yml}": [
"prettier --write"
]
},
"dependencies": {
"p-retry": "^6.2.1"
}
}
+203 -118
View File
@@ -1,6 +1,8 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { type Client, createClient, createConfig } from "@hey-api/client-fetch";
import { Document, FileReader } from "@llamaindex/core/schema";
import { fs, getEnv, path } from "@llamaindex/env";
import pRetry from "p-retry";
import {
type Body_upload_file_api_v1_parsing_upload_post,
type ParserLanguages,
@@ -15,16 +17,18 @@ import {
import { sleep } from "./utils";
export type Language = ParserLanguages;
export type ResultType = "text" | "markdown" | "json";
//todo: should move into @llamaindex/env
// Export the backoff pattern type.
export type BackoffPattern = "constant" | "linear" | "exponential";
// TODO: should move into @llamaindex/env
type WriteStream = {
write: (text: string) => void;
};
// Do not modify this variable or cause type errors
// eslint-disable-next-line @typescript-eslint/no-explicit-any, no-var
// eslint-disable-next-line no-var
var process: any;
/**
@@ -41,48 +45,57 @@ export class LlamaParseReader extends FileReader {
// The result type for the parser.
resultType: ResultType = "text";
// The interval in seconds to check if the parsing is done.
checkInterval = 1;
checkInterval: number = 1;
// The maximum timeout in seconds to wait for the parsing to finish.
maxTimeout = 2000;
// Whether to print the progress of the parsing.
verbose = true;
// The language of the text to parse.
// The language to parse the file in.
language: ParserLanguages[] = ["en"];
// New polling options:
// Controls the backoff mode: "constant", "linear", or "exponential".
backoffPattern: BackoffPattern = "linear";
// Maximum interval in seconds between polls.
maxCheckInterval: number = 5;
// Maximum number of retryable errors before giving up.
maxErrorCount: number = 4;
// The parsing instruction for the parser. Backend default is an empty string.
parsingInstruction?: string | undefined;
// Wether to ignore diagonal text (when the text rotation in degrees is not 0, 90, 180 or 270, so not a horizontal or vertical text). Backend default is false.
// Whether to ignore diagonal text (when the text rotation in degrees is not 0, 90, 180, or 270). Backend default is false.
skipDiagonalText?: boolean | undefined;
// Wheter to ignore the cache and re-process the document. All documents are kept in cache for 48hours after the job was completed to avoid processing the same document twice. Backend default is false.
// Whether to ignore the cache and re-process the document. Documents are cached for 48 hours after job completion. Backend default is false.
invalidateCache?: boolean | undefined;
// Wether the document should not be cached in the first place. Backend default is false.
// Whether the document should not be cached. Backend default is false.
doNotCache?: boolean | undefined;
// Wether to use a faster mode to extract text from documents. This mode will skip OCR of images, and table/heading reconstruction. Note: Non-compatible with gpt4oMode. Backend default is false.
// Whether to use a faster mode to extract text (skipping OCR and table/heading reconstruction). Not compatible with gpt4oMode. Backend default is false.
fastMode?: boolean | undefined;
// Wether to keep column in the text according to document layout. Reduce reconstruction accuracy, and LLM's/embedings performances in most cases.
// Whether to keep columns in the text according to document layout. May reduce reconstruction accuracy and LLM/embedings performance.
doNotUnrollColumns?: boolean | undefined;
// A templated page separator to use to split the text. If the results contain `{page_number}` (e.g. JSON mode), it will be replaced by the next page number. If not set the default separator '\\n---\\n' will be used.
// A templated page separator for splitting text. If not set, default is "\n---\n".
pageSeparator?: string | undefined;
//A templated prefix to add to the beginning of each page. If the results contain `{page_number}`, it will be replaced by the page number.>
// A templated prefix to add at the beginning of each page.
pagePrefix?: string | undefined;
// A templated suffix to add to the end of each page. If the results contain `{page_number}`, it will be replaced by the page number.
// A templated suffix to add at the end of each page.
pageSuffix?: string | undefined;
// Deprecated. Use vendorMultimodal params. Whether to use gpt-4o to extract text from documents.
// Deprecated. Use vendorMultimodal params. Whether to use gpt-4o to extract text.
gpt4oMode: boolean = false;
// Deprecated. Use vendorMultimodal params. The API key for the GPT-4o API. Optional, lowers the cost of parsing. Can be set as an env variable: LLAMA_CLOUD_GPT4O_API_KEY.
// Deprecated. Use vendorMultimodal params. The API key for GPT-4o. Can be set via LLAMA_CLOUD_GPT4O_API_KEY.
gpt4oApiKey?: string | undefined;
// The bounding box to use to extract text from documents. Describe as a string containing the bounding box margins.
// The bounding box margins as a string.
boundingBox?: string | undefined;
// The target pages to extract text from documents. Describe as a comma separated list of page numbers. The first page of the document is page 0
// The target pages (comma separated list, starting at 0).
targetPages?: string | undefined;
// Whether or not to ignore and skip errors raised during parsing.
// Whether to ignore errors during parsing.
ignoreErrors: boolean = true;
// Whether to split by page using the pageSeparator or '\n---\n' as default.
// Whether to split by page using the pageSeparator (or "\n---\n" as default).
splitByPage: boolean = true;
// Whether to use the vendor multimodal API.
useVendorMultimodalModel: boolean = false;
// The model name for the vendor multimodal API
// The model name for the vendor multimodal API.
vendorMultimodalModelName?: string | undefined;
// The API key for the multimodal API. Can also be set as an env variable: LLAMA_CLOUD_VENDOR_MULTIMODAL_API_KEY
// The API key for the multimodal API. Can be set via LLAMA_CLOUD_VENDOR_MULTIMODAL_API_KEY.
vendorMultimodalApiKey?: string | undefined;
webhookUrl?: string | undefined;
@@ -173,7 +186,7 @@ export class LlamaParseReader extends FileReader {
}
this.apiKey = apiKey;
if (this.baseUrl.endsWith("/")) {
this.baseUrl = this.baseUrl.slice(0, -"/".length);
this.baseUrl = this.baseUrl.slice(0, -1);
}
if (this.baseUrl.endsWith("/api/parsing")) {
this.baseUrl = this.baseUrl.slice(0, -"/api/parsing".length);
@@ -203,13 +216,19 @@ export class LlamaParseReader extends FileReader {
);
}
// Create a job for the LlamaParse API
/**
* Creates a job for the LlamaParse API.
*
* @param data - The file data as a Uint8Array.
* @param filename - Optional filename for the file.
* @returns A Promise resolving to the job ID as a string.
*/
async #createJob(data: Uint8Array, filename?: string): Promise<string> {
if (this.verbose) {
console.log("Started uploading the file");
}
// todo: remove Blob usage when we drop Node.js 18 support
// TODO: remove Blob usage when we drop Node.js 18 support
const file: File | Blob =
globalThis.File && filename
? new File([data], filename)
@@ -320,87 +339,124 @@ export class LlamaParseReader extends FileReader {
return response.data.id;
}
// Get the result of the job
/**
* Retrieves the result of a parsing job.
*
* Uses a polling loop with retry logic. Each API call is retried
* up to maxErrorCount times if it fails with a 5XX or socket error.
* The delay between polls increases according to the specified backoffPattern ("constant", "linear", or "exponential"),
* capped by maxCheckInterval.
*
* @param jobId - The job ID.
* @param resultType - The type of result to fetch ("text", "json", or "markdown").
* @returns A Promise resolving to the job result.
*/
private async getJobResult(
jobId: string,
resultType: "text" | "json" | "markdown",
// eslint-disable-next-line @typescript-eslint/no-explicit-any
): Promise<any> {
const signal = AbortSignal.timeout(this.maxTimeout * 1000);
let tries = 0;
let currentInterval = this.checkInterval;
while (true) {
await sleep(this.checkInterval * 1000);
await sleep(currentInterval * 1000);
// Check the job status. If unsuccessful response, checks if maximum timeout has been reached. If reached, throws an error
const result = await getJobApiV1ParsingJobJobIdGet({
client: this.#client,
throwOnError: true,
path: {
job_id: jobId,
},
query: {
project_id: this.project_id ?? null,
organization_id: this.organization_id ?? null,
},
signal,
});
const { data } = result;
const status = (data as Record<string, unknown>)["status"];
// If job has completed, return the result
if (status === "SUCCESS") {
let result;
switch (resultType) {
case "json": {
result = await getJobJsonResultApiV1ParsingJobJobIdResultJsonGet({
// Wraps the API call in a retry
let result;
try {
result = await pRetry(
() =>
getJobApiV1ParsingJobJobIdGet({
client: this.#client,
throwOnError: true,
path: {
job_id: jobId,
},
path: { job_id: jobId },
query: {
project_id: this.project_id ?? null,
organization_id: this.organization_id ?? null,
},
signal,
});
signal: AbortSignal.timeout(this.maxTimeout * 1000),
}),
{
retries: this.maxErrorCount,
onFailedAttempt: (error) => {
// Retry only on 5XX or socket errors.
const status = (error.cause as any)?.response?.status;
if (
!(
(status && status >= 500 && status < 600) ||
((error.cause as any)?.code &&
((error.cause as any).code === "ECONNRESET" ||
(error.cause as any).code === "ETIMEDOUT" ||
(error.cause as any).code === "ECONNREFUSED"))
)
) {
throw error;
}
if (this.verbose) {
console.warn(
`Attempting to get job ${jobId} result (attempt ${error.attemptNumber}) failed. Retrying...`,
);
}
},
},
);
} catch (e: any) {
throw new Error(
`Max error count reached for job ${jobId}: ${e.message}`,
);
}
const { data } = result;
const status = (data as Record<string, unknown>)["status"];
if (status === "SUCCESS") {
let resultData;
switch (resultType) {
case "json": {
resultData =
await getJobJsonResultApiV1ParsingJobJobIdResultJsonGet({
client: this.#client,
throwOnError: true,
path: { job_id: jobId },
query: {
project_id: this.project_id ?? null,
organization_id: this.organization_id ?? null,
},
signal: AbortSignal.timeout(this.maxTimeout * 1000),
});
break;
}
case "markdown": {
result = await getJobResultApiV1ParsingJobJobIdResultMarkdownGet({
client: this.#client,
throwOnError: true,
path: {
job_id: jobId,
},
query: {
project_id: this.project_id ?? null,
organization_id: this.organization_id ?? null,
},
signal,
});
resultData =
await getJobResultApiV1ParsingJobJobIdResultMarkdownGet({
client: this.#client,
throwOnError: true,
path: { job_id: jobId },
query: {
project_id: this.project_id ?? null,
organization_id: this.organization_id ?? null,
},
signal: AbortSignal.timeout(this.maxTimeout * 1000),
});
break;
}
case "text": {
result = await getJobTextResultApiV1ParsingJobJobIdResultTextGet({
client: this.#client,
throwOnError: true,
path: {
job_id: jobId,
},
query: {
project_id: this.project_id ?? null,
organization_id: this.organization_id ?? null,
},
signal,
});
resultData =
await getJobTextResultApiV1ParsingJobJobIdResultTextGet({
client: this.#client,
throwOnError: true,
path: { job_id: jobId },
query: {
project_id: this.project_id ?? null,
organization_id: this.organization_id ?? null,
},
signal: AbortSignal.timeout(this.maxTimeout * 1000),
});
break;
}
}
return result.data;
// If job is still pending, check if maximum timeout has been reached. If reached, throws an error
return resultData.data;
} else if (status === "PENDING") {
signal.throwIfAborted();
if (this.verbose && tries % 10 === 0) {
this.stdout?.write(".");
}
@@ -408,23 +464,35 @@ export class LlamaParseReader extends FileReader {
} else {
if (this.verbose) {
console.error(
`Recieved Error response ${status} for job ${jobId}. Got Error Code: ${data.error_code} and Error Message: ${data.error_message}`,
`Received error response ${status} for job ${jobId}. Got Error Code: ${data.error_code} and Error Message: ${data.error_message}`,
);
}
throw new Error(
`Failed to parse the file: ${jobId}, status: ${status}`,
);
}
// Adjust the polling interval based on the backoff pattern.
if (this.backoffPattern === "exponential") {
currentInterval = Math.min(currentInterval * 2, this.maxCheckInterval);
} else if (this.backoffPattern === "linear") {
currentInterval = Math.min(
currentInterval + this.checkInterval,
this.maxCheckInterval,
);
} else if (this.backoffPattern === "constant") {
currentInterval = this.checkInterval;
}
}
}
/**
* Loads data from a file and returns an array of Document objects.
* To be used with resultType = "text" and "markdown"
* To be used with resultType "text" or "markdown".
*
* @param {Uint8Array} fileContent - The content of the file to be loaded.
* @param {string} filename - The name of the file to be loaded.
* @return {Promise<Document[]>} A Promise object that resolves to an array of Document objects.
* @param fileContent - The content of the file as a Uint8Array.
* @param filename - Optional filename for the file.
* @returns A Promise that resolves to an array of Document objects.
*/
async loadDataAsContent(
fileContent: Uint8Array,
@@ -436,42 +504,38 @@ export class LlamaParseReader extends FileReader {
console.log(`Started parsing the file under job id ${jobId}`);
}
// Return results as Document objects
// Return results as Document objects.
const jobResults = await this.getJobResult(jobId, this.resultType);
const resultText = jobResults[this.resultType];
// Split the text by separator if splitByPage is true
// Split the text by separator if splitByPage is true.
if (this.splitByPage) {
return this.splitTextBySeparator(resultText);
}
return [
new Document({
text: resultText,
}),
];
return [new Document({ text: resultText })];
})
.catch((error) => {
console.warn(
`Error while parsing the file with: ${error.message ?? error.detail}`,
);
if (this.ignoreErrors) {
console.warn(
`Error while parsing the file: ${error.message ?? error.detail}`,
);
return [];
} else {
throw error;
}
});
}
/**
* Loads data from a file and returns an array of JSON objects.
* To be used with resultType = "json"
* To be used with resultType "json".
*
* @param {string} filePathOrContent - The file path to the file or the content of the file as a Buffer
* @return {Promise<Record<string, any>[]>} A Promise that resolves to an array of JSON objects.
* @param filePathOrContent - The file path or the file content as a Uint8Array.
* @returns A Promise that resolves to an array of JSON objects.
*/
async loadJson(
filePathOrContent: string | Uint8Array,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
): Promise<Record<string, any>[]> {
let jobId;
const isFilePath = typeof filePathOrContent === "string";
@@ -479,7 +543,7 @@ export class LlamaParseReader extends FileReader {
const data = isFilePath
? await fs.readFile(filePathOrContent)
: filePathOrContent;
// Creates a job for the file
// Create a job for the file.
jobId = await this.#createJob(
data,
isFilePath ? path.basename(filePathOrContent) : undefined,
@@ -488,14 +552,14 @@ export class LlamaParseReader extends FileReader {
console.log(`Started parsing the file under job id ${jobId}`);
}
// Return results as an array of JSON objects (same format as Python version of the reader)
// Return results as an array of JSON objects.
const resultJson = await this.getJobResult(jobId, "json");
resultJson.job_id = jobId;
resultJson.file_path = isFilePath ? filePathOrContent : undefined;
return [resultJson];
} catch (e) {
console.error(`Error while parsing the file under job id ${jobId}`, e);
if (this.ignoreErrors) {
console.error(`Error while parsing the file under job id ${jobId}`, e);
return [];
} else {
throw e;
@@ -505,27 +569,24 @@ export class LlamaParseReader extends FileReader {
/**
* Downloads and saves images from a given JSON result to a specified download path.
* Currently only supports resultType = "json"
* Currently only supports resultType "json".
*
* @param {Record<string, any>[]} jsonResult - The JSON result containing image information.
* @param {string} downloadPath - The path to save the downloaded images.
* @return {Promise<Record<string, any>[]>} A Promise that resolves to an array of image objects.
* @param jsonResult - The JSON result containing image information.
* @param downloadPath - The path where the downloaded images will be saved.
* @returns A Promise that resolves to an array of image objects.
*/
async getImages(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
jsonResult: Record<string, any>[],
downloadPath: string,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
): Promise<Record<string, any>[]> {
try {
// Create download directory if it doesn't exist (Actually check for write access, not existence, since fsPromises does not have a `existsSync` method)
// Create download directory if it doesn't exist (checks for write access).
try {
await fs.access(downloadPath);
} catch {
await fs.mkdir(downloadPath, { recursive: true });
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const images: Record<string, any>[] = [];
for (const result of jsonResult) {
const jobId = result.job_id;
@@ -541,7 +602,7 @@ export class LlamaParseReader extends FileReader {
imageName,
);
await this.fetchAndSaveImage(imageName, imagePath, jobId);
// Assign metadata to the image
// Assign metadata to the image.
image.path = imagePath;
image.job_id = jobId;
image.original_pdf_path = result.file_path;
@@ -561,6 +622,14 @@ export class LlamaParseReader extends FileReader {
}
}
/**
* Constructs the file path for an image.
*
* @param downloadPath - The base download directory.
* @param jobId - The job ID.
* @param imageName - The image name.
* @returns A Promise that resolves to the full image path.
*/
private async getImagePath(
downloadPath: string,
jobId: string,
@@ -569,6 +638,13 @@ export class LlamaParseReader extends FileReader {
return path.join(downloadPath, `${jobId}-${imageName}`);
}
/**
* Fetches an image from the API and saves it to the specified path.
*
* @param imageName - The name of the image.
* @param imagePath - The local path to save the image.
* @param jobId - The associated job ID.
*/
private async fetchAndSaveImage(
imageName: string,
imagePath: string,
@@ -590,18 +666,21 @@ export class LlamaParseReader extends FileReader {
throw new Error(`Failed to download image: ${response.error.detail}`);
}
const blob = (await response.data) as Blob;
// Write the image buffer to the specified imagePath
// Write the image buffer to the specified imagePath.
await fs.writeFile(imagePath, new Uint8Array(await blob.arrayBuffer()));
}
// Filters out invalid values (null, undefined, empty string) of specific params.
/**
* Filters out invalid values (null, undefined, empty string) for specific parameters.
*
* @param params - The parameters object.
* @param keysToCheck - The keys to check for valid values.
* @returns A new object with filtered parameters.
*/
private filterSpecificParams(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
params: Record<string, any>,
keysToCheck: string[],
// eslint-disable-next-line @typescript-eslint/no-explicit-any
): Record<string, any> {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const filteredParams: Record<string, any> = {};
for (const [key, value] of Object.entries(params)) {
if (keysToCheck.includes(key)) {
@@ -615,6 +694,12 @@ export class LlamaParseReader extends FileReader {
return filteredParams;
}
/**
* Splits text into Document objects using the page separator.
*
* @param text - The text to be split.
* @returns An array of Document objects.
*/
private splitTextBySeparator(text: string): Document[] {
const separator = this.pageSeparator ?? "\n---\n";
const textChunks = text.split(separator);
@@ -24,6 +24,7 @@ import {
} from "./utils";
export class AmazonProvider extends Provider<ConverseStreamOutput> {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
getResultFromResponse(response: Record<string, any>): ConverseResponse {
return JSON.parse(toUtf8(response.body));
}
@@ -52,7 +53,7 @@ export class AmazonProvider extends Provider<ConverseStreamOutput> {
}
getTextFromStreamResponse(response: ResponseStream): string {
let event: ConverseStreamOutput | undefined =
const event: ConverseStreamOutput | undefined =
this.getStreamingEventResponse(response);
if (!event || !event.contentBlockDelta) return "";
const delta: ContentBlockDelta | undefined = event.contentBlockDelta.delta;
@@ -56,7 +56,7 @@ export const mapImageContent = (imageUrl: string): ImageBlock => {
mimeType as keyof typeof ACCEPTED_IMAGE_MIME_TYPE_FORMAT_MAP
],
// @ts-ignore: there's a mistake in the "@aws-sdk/client-bedrock-runtime" compared to the actual api
// @ts-expect-error: there's a mistake in the "@aws-sdk/client-bedrock-runtime" compared to the actual api
source: { bytes: data },
};
};
@@ -381,6 +381,7 @@ export class Bedrock extends ToolCallLLM<BedrockAdditionalChatOptions> {
maxTokens: this.maxTokens,
contextWindow: BEDROCK_FOUNDATION_LLMS[this.model] ?? 128000,
tokenizer: undefined,
structuredOutput: false,
};
}
+1
View File
@@ -20,4 +20,5 @@ export const DEFAULT_NAMESPACE = "docstore";
//#region llama cloud
export const DEFAULT_PROJECT_NAME = "Default";
export const DEFAULT_BASE_URL = "https://api.cloud.llamaindex.ai";
export const DEFAULT_EU_BASE_URL = "https://api.cloud.eu.llamaindex.ai";
//#endregion
+5 -1
View File
@@ -28,11 +28,12 @@ export abstract class BaseLLM<
async complete(
params: LLMCompletionParamsStreaming | LLMCompletionParamsNonStreaming,
): Promise<CompletionResponse | AsyncIterable<CompletionResponse>> {
const { prompt, stream } = params;
const { prompt, stream, responseFormat } = params;
if (stream) {
const stream = await this.chat({
messages: [{ content: prompt, role: "user" }],
stream: true,
...(responseFormat ? { responseFormat } : {}),
});
return streamConverter(stream, (chunk) => {
return {
@@ -41,9 +42,12 @@ export abstract class BaseLLM<
};
});
}
const chatResponse = await this.chat({
messages: [{ content: prompt, role: "user" }],
...(responseFormat ? { responseFormat } : {}),
});
return {
text: extractText(chatResponse.message.content),
raw: chatResponse.raw,
+4
View File
@@ -1,5 +1,6 @@
import type { Tokenizers } from "@llamaindex/env/tokenizers";
import type { JSONSchemaType } from "ajv";
import { z } from "zod";
import type { JSONObject, JSONValue } from "../global";
/**
@@ -106,6 +107,7 @@ export type LLMMetadata = {
maxTokens?: number | undefined;
contextWindow: number;
tokenizer: Tokenizers | undefined;
structuredOutput: boolean;
};
export interface LLMChatParamsBase<
@@ -115,6 +117,7 @@ export interface LLMChatParamsBase<
messages: ChatMessage<AdditionalMessageOptions>[];
additionalChatOptions?: AdditionalChatOptions;
tools?: BaseTool[];
responseFormat?: z.ZodType | object;
}
export interface LLMChatParamsStreaming<
@@ -133,6 +136,7 @@ export interface LLMChatParamsNonStreaming<
export interface LLMCompletionParamsBase {
prompt: MessageContent;
responseFormat?: z.ZodType | object;
}
export interface LLMCompletionParamsStreaming extends LLMCompletionParamsBase {
@@ -23,7 +23,7 @@ import {
} from "./base-synthesizer";
import { createMessageContent } from "./utils";
const responseModeSchema = z.enum([
export const responseModeSchema = z.enum([
"refine",
"compact",
"tree_summarize",
@@ -35,7 +35,7 @@ export type ResponseMode = z.infer<typeof responseModeSchema>;
/**
* A response builder that uses the query to ask the LLM generate a better response using multiple text chunks.
*/
class Refine extends BaseSynthesizer {
export class Refine extends BaseSynthesizer {
textQATemplate: TextQAPrompt;
refineTemplate: RefinePrompt;
@@ -213,7 +213,7 @@ class Refine extends BaseSynthesizer {
/**
* CompactAndRefine is a slight variation of Refine that first compacts the text chunks into the smallest possible number of chunks.
*/
class CompactAndRefine extends Refine {
export class CompactAndRefine extends Refine {
async getResponse(
query: MessageContent,
nodes: NodeWithScore[],
@@ -267,7 +267,7 @@ class CompactAndRefine extends Refine {
/**
* TreeSummarize repacks the text chunks into the smallest possible number of chunks and then summarizes them, then recursively does so until there's one chunk left.
*/
class TreeSummarize extends BaseSynthesizer {
export class TreeSummarize extends BaseSynthesizer {
summaryTemplate: TreeSummarizePrompt;
constructor(
@@ -370,7 +370,7 @@ class TreeSummarize extends BaseSynthesizer {
}
}
class MultiModal extends BaseSynthesizer {
export class MultiModal extends BaseSynthesizer {
metadataMode: MetadataMode;
textQATemplate: TextQAPrompt;
@@ -2,7 +2,15 @@ export {
BaseSynthesizer,
type BaseSynthesizerOptions,
} from "./base-synthesizer";
export { getResponseSynthesizer, type ResponseMode } from "./factory";
export {
CompactAndRefine,
MultiModal,
Refine,
TreeSummarize,
getResponseSynthesizer,
responseModeSchema,
type ResponseMode,
} from "./factory";
export type {
SynthesizeEndEvent,
SynthesizeQuery,
+1
View File
@@ -35,6 +35,7 @@ export class MockLLM extends ToolCallLLM {
topP: 0.5,
contextWindow: 1024,
tokenizer: undefined,
structuredOutput: false,
};
}
@@ -126,6 +126,7 @@ describe("sentence splitter", () => {
id_: docId,
text: "This is a test sentence. This is another test sentence.",
});
const nodes = sentenceSplitter.getNodesFromDocuments([doc]);
nodes.forEach((node) => {
// test node id should match uuid regex
+1
View File
@@ -191,6 +191,7 @@ export class Anthropic extends ToolCallLLM<
].contextWindow
: 200000,
tokenizer: undefined,
structuredOutput: false,
};
}
+10 -4
View File
@@ -6,6 +6,7 @@ import {
type ModelParams as GoogleModelParams,
type RequestOptions as GoogleRequestOptions,
type GenerateContentStreamResult as GoogleStreamGenerateContentResult,
type SafetySetting,
} from "@google/generative-ai";
import { wrapLLMEvent } from "@llamaindex/core/decorator";
@@ -88,6 +89,7 @@ const DEFAULT_GEMINI_PARAMS = {
export type GeminiConfig = Partial<typeof DEFAULT_GEMINI_PARAMS> & {
session?: IGeminiSession;
requestOptions?: GoogleRequestOptions;
safetySettings?: SafetySetting[];
};
/**
@@ -112,7 +114,7 @@ export class GeminiSession implements IGeminiSession {
): GoogleGenerativeModel {
return this.gemini.getGenerativeModel(
{
safetySettings: DEFAULT_SAFETY_SETTINGS,
safetySettings: metadata.safetySettings ?? DEFAULT_SAFETY_SETTINGS,
...metadata,
},
requestOpts,
@@ -218,6 +220,7 @@ export class Gemini extends ToolCallLLM<GeminiAdditionalChatOptions> {
maxTokens?: number | undefined;
#requestOptions?: GoogleRequestOptions | undefined;
session: IGeminiSession;
safetySettings: SafetySetting[];
constructor(init?: GeminiConfig) {
super();
@@ -227,13 +230,14 @@ export class Gemini extends ToolCallLLM<GeminiAdditionalChatOptions> {
this.maxTokens = init?.maxTokens ?? undefined;
this.session = init?.session ?? GeminiSessionStore.get();
this.#requestOptions = init?.requestOptions ?? undefined;
this.safetySettings = init?.safetySettings ?? DEFAULT_SAFETY_SETTINGS;
}
get supportToolCall(): boolean {
return SUPPORT_TOOL_CALL_MODELS.includes(this.model);
}
get metadata(): LLMMetadata {
get metadata(): LLMMetadata & { safetySettings: SafetySetting[] } {
return {
model: this.model,
temperature: this.temperature,
@@ -241,6 +245,8 @@ export class Gemini extends ToolCallLLM<GeminiAdditionalChatOptions> {
maxTokens: this.maxTokens,
contextWindow: GEMINI_MODEL_INFO_MAP[this.model].contextWindow,
tokenizer: undefined,
structuredOutput: false,
safetySettings: this.safetySettings,
};
}
@@ -250,7 +256,7 @@ export class Gemini extends ToolCallLLM<GeminiAdditionalChatOptions> {
const context = getChatContext(params);
const common = {
history: context.history,
safetySettings: DEFAULT_SAFETY_SETTINGS,
safetySettings: this.safetySettings,
};
return params.tools?.length
@@ -264,7 +270,7 @@ export class Gemini extends ToolCallLLM<GeminiAdditionalChatOptions> {
),
},
],
safetySettings: DEFAULT_SAFETY_SETTINGS,
safetySettings: this.safetySettings,
}
: common;
}
+3 -2
View File
@@ -59,14 +59,15 @@ export class GeminiVertexSession implements IGeminiSession {
getGenerativeModel(
metadata: VertexModelParams,
): VertexGenerativeModelPreview | VertexGenerativeModel {
const safetySettings = metadata.safetySettings ?? DEFAULT_SAFETY_SETTINGS;
if (this.preview) {
return this.vertex.preview.getGenerativeModel({
safetySettings: DEFAULT_SAFETY_SETTINGS,
safetySettings,
...metadata,
});
}
return this.vertex.getGenerativeModel({
safetySettings: DEFAULT_SAFETY_SETTINGS,
safetySettings,
...metadata,
});
}
@@ -57,6 +57,7 @@ export class HuggingFaceLLM extends BaseLLM {
maxTokens: this.maxTokens,
contextWindow: this.contextWindow,
tokenizer: undefined,
structuredOutput: false,
};
}
@@ -123,6 +123,7 @@ export class HuggingFaceInferenceAPI extends BaseLLM {
maxTokens: this.maxTokens,
contextWindow: this.contextWindow,
tokenizer: undefined,
structuredOutput: false,
};
}
+4 -2
View File
@@ -27,10 +27,12 @@
},
"scripts": {
"build": "bunchee",
"dev": "bunchee --watch"
"dev": "bunchee --watch",
"test": "vitest run"
},
"devDependencies": {
"bunchee": "6.4.0"
"bunchee": "6.4.0",
"vitest": "^2.1.5"
},
"dependencies": {
"@llamaindex/core": "workspace:*",
+189 -19
View File
@@ -1,21 +1,51 @@
import { wrapEventCaller } from "@llamaindex/core/decorator";
import {
BaseLLM,
ToolCallLLM,
type BaseTool,
type ChatMessage,
type ChatResponse,
type ChatResponseChunk,
type LLMChatParamsNonStreaming,
type LLMChatParamsStreaming,
type PartialToolCall,
type ToolCallLLMMessageOptions,
} from "@llamaindex/core/llms";
import { extractText } from "@llamaindex/core/utils";
import { getEnv } from "@llamaindex/env";
import { type Mistral } from "@mistralai/mistralai";
import type { ContentChunk } from "@mistralai/mistralai/models/components";
import type {
AssistantMessage,
ChatCompletionRequest,
ChatCompletionStreamRequest,
ContentChunk,
Tool,
ToolMessage,
} from "@mistralai/mistralai/models/components";
export const ALL_AVAILABLE_MISTRAL_MODELS = {
"mistral-tiny": { contextWindow: 32000 },
"mistral-small": { contextWindow: 32000 },
"mistral-medium": { contextWindow: 32000 },
"mistral-small-latest": { contextWindow: 32000 },
"mistral-large-latest": { contextWindow: 131000 },
"codestral-latest": { contextWindow: 256000 },
"pixtral-large-latest": { contextWindow: 131000 },
"mistral-saba-latest": { contextWindow: 32000 },
"ministral-3b-latest": { contextWindow: 131000 },
"ministral-8b-latest": { contextWindow: 131000 },
"mistral-embed": { contextWindow: 8000 },
"mistral-moderation-latest": { contextWindow: 8000 },
};
export const TOOL_CALL_MISTRAL_MODELS = [
"mistral-small-latest",
"mistral-large-latest",
"codestral-latest",
"pixtral-large-latest",
"ministral-8b-latest",
"ministral-3b-latest",
];
export class MistralAISession {
apiKey: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -46,7 +76,7 @@ export class MistralAISession {
/**
* MistralAI LLM implementation
*/
export class MistralAI extends BaseLLM {
export class MistralAI extends ToolCallLLM<ToolCallLLMMessageOptions> {
// Per completion MistralAI params
model: keyof typeof ALL_AVAILABLE_MISTRAL_MODELS;
temperature: number;
@@ -60,7 +90,7 @@ export class MistralAI extends BaseLLM {
constructor(init?: Partial<MistralAI>) {
super();
this.model = init?.model ?? "mistral-small";
this.model = init?.model ?? "mistral-small-latest";
this.temperature = init?.temperature ?? 0.1;
this.topP = init?.topP ?? 1;
this.maxTokens = init?.maxTokens ?? undefined;
@@ -77,11 +107,55 @@ export class MistralAI extends BaseLLM {
maxTokens: this.maxTokens,
contextWindow: ALL_AVAILABLE_MISTRAL_MODELS[this.model].contextWindow,
tokenizer: undefined,
structuredOutput: false,
};
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private buildParams(messages: ChatMessage[]): any {
get supportToolCall() {
return TOOL_CALL_MISTRAL_MODELS.includes(this.metadata.model);
}
formatMessages(messages: ChatMessage<ToolCallLLMMessageOptions>[]) {
return messages.map((message) => {
const options = message.options ?? {};
//tool call message
if ("toolCall" in options) {
return {
role: "assistant",
content: extractText(message.content),
toolCalls: options.toolCall.map((toolCall) => {
return {
id: toolCall.id,
type: "function",
function: {
name: toolCall.name,
arguments: toolCall.input,
},
};
}),
} satisfies AssistantMessage;
}
//tool result message
if ("toolResult" in options) {
return {
role: "tool",
content: extractText(message.content),
toolCallId: options.toolResult.id,
} satisfies ToolMessage;
}
return {
role: message.role,
content: extractText(message.content),
};
});
}
private buildParams(
messages: ChatMessage<ToolCallLLMMessageOptions>[],
tools?: BaseTool[],
) {
return {
model: this.model,
temperature: this.temperature,
@@ -89,25 +163,49 @@ export class MistralAI extends BaseLLM {
topP: this.topP,
safeMode: this.safeMode,
randomSeed: this.randomSeed,
messages,
messages: this.formatMessages(messages),
tools: tools?.map(MistralAI.toTool),
};
}
static toTool(tool: BaseTool): Tool {
if (!tool.metadata.parameters) {
throw new Error("Tool parameters are required");
}
return {
type: "function",
function: {
name: tool.metadata.name,
description: tool.metadata.description,
parameters: tool.metadata.parameters,
},
};
}
chat(
params: LLMChatParamsStreaming,
): Promise<AsyncIterable<ChatResponseChunk>>;
chat(params: LLMChatParamsNonStreaming): Promise<ChatResponse>;
chat(
params: LLMChatParamsNonStreaming<ToolCallLLMMessageOptions>,
): Promise<ChatResponse>;
async chat(
params: LLMChatParamsNonStreaming | LLMChatParamsStreaming,
): Promise<ChatResponse | AsyncIterable<ChatResponseChunk>> {
const { messages, stream } = params;
): Promise<
| ChatResponse<ToolCallLLMMessageOptions>
| AsyncIterable<ChatResponseChunk<ToolCallLLMMessageOptions>>
> {
const { messages, stream, tools } = params;
// Streaming
if (stream) {
return this.streamChat(params);
return this.streamChat(messages, tools);
}
// Non-streaming
const client = await this.session.getClient();
const response = await client.chat.complete(this.buildParams(messages));
const buildParams = this.buildParams(messages, tools);
const response = await client.chat.complete(
buildParams as ChatCompletionRequest,
);
if (!response || !response.choices || !response.choices[0]) {
throw new Error("Unexpected response format from Mistral API");
@@ -121,28 +219,100 @@ export class MistralAI extends BaseLLM {
message: {
role: "assistant",
content: this.extractContentAsString(content),
options: response.choices[0]!.message?.toolCalls
? {
toolCall: response.choices[0]!.message.toolCalls.map(
(toolCall) => ({
id: toolCall.id,
name: toolCall.function.name,
input: this.extractArgumentsAsString(
toolCall.function.arguments,
),
}),
),
}
: {},
},
};
}
protected async *streamChat({
messages,
}: LLMChatParamsStreaming): AsyncIterable<ChatResponseChunk> {
@wrapEventCaller
protected async *streamChat(
messages: ChatMessage[],
tools?: BaseTool[],
): AsyncIterable<ChatResponseChunk<ToolCallLLMMessageOptions>> {
const client = await this.session.getClient();
const chunkStream = await client.chat.stream(this.buildParams(messages));
const buildParams = this.buildParams(
messages,
tools,
) as ChatCompletionStreamRequest;
const chunkStream = await client.chat.stream(buildParams);
let currentToolCall: PartialToolCall | null = null;
const toolCallMap = new Map<string, PartialToolCall>();
for await (const chunk of chunkStream) {
if (!chunk.data || !chunk.data.choices || !chunk.data.choices.length)
continue;
if (!chunk.data?.choices?.[0]?.delta) continue;
const choice = chunk.data.choices[0];
if (!choice) continue;
if (!(choice.delta.content || choice.delta.toolCalls)) continue;
let shouldEmitToolCall: PartialToolCall | null = null;
if (choice.delta.toolCalls?.[0]) {
const toolCall = choice.delta.toolCalls[0];
if (toolCall.id) {
if (currentToolCall && toolCall.id !== currentToolCall.id) {
shouldEmitToolCall = {
...currentToolCall,
input: JSON.parse(currentToolCall.input),
};
}
currentToolCall = {
id: toolCall.id,
name: toolCall.function!.name!,
input: this.extractArgumentsAsString(toolCall.function!.arguments),
};
toolCallMap.set(toolCall.id, currentToolCall!);
} else if (currentToolCall && toolCall.function?.arguments) {
currentToolCall.input += this.extractArgumentsAsString(
toolCall.function.arguments,
);
}
}
const isDone: boolean = choice.finishReason !== null;
if (isDone && currentToolCall) {
//emitting last tool call
shouldEmitToolCall = {
...currentToolCall,
input: JSON.parse(currentToolCall.input),
};
}
yield {
raw: chunk.data,
delta: this.extractContentAsString(choice.delta.content),
options: shouldEmitToolCall
? { toolCall: [shouldEmitToolCall] }
: currentToolCall
? { toolCall: [currentToolCall] }
: {},
};
}
toolCallMap.clear();
}
private extractArgumentsAsString(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
args: string | { [k: string]: any } | null | undefined,
): string {
return typeof args === "string" ? args : JSON.stringify(args) || "";
}
private extractContentAsString(
@@ -0,0 +1,116 @@
import type { ChatMessage } from "@llamaindex/core/llms";
import { setEnvs } from "@llamaindex/env";
import { beforeAll, describe, expect, test } from "vitest";
import { MistralAI } from "../src/index";
beforeAll(() => {
setEnvs({
MISTRAL_API_KEY: "valid",
});
});
describe("Message Formatting", () => {
describe("Basic Message Formatting", () => {
test("Mistral formats basic messages correctly", () => {
const mistral = new MistralAI();
const inputMessages: ChatMessage[] = [
{
content: "You are a helpful assistant.",
role: "assistant",
},
{
content: "Hello?",
role: "user",
},
];
const expectedOutput = [
{
content: "You are a helpful assistant.",
role: "assistant",
},
{
content: "Hello?",
role: "user",
},
];
expect(mistral.formatMessages(inputMessages)).toEqual(expectedOutput);
});
test("Mistral handles multi-turn conversation correctly", () => {
const mistral = new MistralAI();
const inputMessages: ChatMessage[] = [
{ content: "Hi", role: "user" },
{ content: "Hello! How can I help?", role: "assistant" },
{ content: "What's the weather?", role: "user" },
];
const expectedOutput = [
{ content: "Hi", role: "user" },
{ content: "Hello! How can I help?", role: "assistant" },
{ content: "What's the weather?", role: "user" },
];
expect(mistral.formatMessages(inputMessages)).toEqual(expectedOutput);
});
});
describe("Tool Message Formatting", () => {
const toolCallMessages: ChatMessage[] = [
{
role: "user",
content: "What's the weather in London?",
},
{
role: "assistant",
content: "Let me check the weather.",
options: {
toolCall: [
{
id: "call_123",
name: "weather",
input: JSON.stringify({ location: "London" }),
},
],
},
},
{
role: "assistant",
content: "The weather in London is sunny, +20°C",
options: {
toolResult: {
id: "call_123",
},
},
},
];
test("Mistral formats tool calls correctly", () => {
const mistral = new MistralAI();
const expectedOutput = [
{
role: "user",
content: "What's the weather in London?",
},
{
role: "assistant",
content: "Let me check the weather.",
toolCalls: [
{
type: "function",
id: "call_123",
function: {
name: "weather",
arguments: '{"location":"London"}',
},
},
],
},
{
role: "tool",
content: "The weather in London is sunny, +20°C",
toolCallId: "call_123",
},
];
expect(mistral.formatMessages(toolCallMessages)).toEqual(expectedOutput);
});
});
});
+12
View File
@@ -37,5 +37,17 @@
"@llamaindex/env": "workspace:*",
"ollama": "^0.5.10",
"remeda": "^2.17.3"
},
"peerDependencies": {
"zod": "^3.24.2",
"zod-to-json-schema": "^3.23.3"
},
"peerDependenciesMeta": {
"zod": {
"optional": true
},
"zod-to-json-schema": {
"optional": true
}
}
}
+29 -1
View File
@@ -57,6 +57,22 @@ export type OllamaParams = {
options?: Partial<Options>;
};
async function getZod() {
try {
return await import("zod");
} catch (e) {
throw new Error("zod is required for structured output");
}
}
async function getZodToJsonSchema() {
try {
return await import("zod-to-json-schema");
} catch (e) {
throw new Error("zod-to-json-schema is required for structured output");
}
}
export class Ollama extends ToolCallLLM {
supportToolCall: boolean = true;
public readonly ollama: OllamaBase;
@@ -92,6 +108,7 @@ export class Ollama extends ToolCallLLM {
maxTokens: this.options.num_ctx,
contextWindow: num_ctx,
tokenizer: undefined,
structuredOutput: true,
};
}
@@ -109,7 +126,7 @@ export class Ollama extends ToolCallLLM {
): Promise<
ChatResponse<ToolCallLLMMessageOptions> | AsyncIterable<ChatResponseChunk>
> {
const { messages, stream, tools } = params;
const { messages, stream, tools, responseFormat } = params;
const payload: ChatRequest = {
model: this.model,
messages: messages.map((message) => {
@@ -130,9 +147,20 @@ export class Ollama extends ToolCallLLM {
...this.options,
},
};
if (tools) {
payload.tools = tools.map((tool) => Ollama.toTool(tool));
}
if (responseFormat && this.metadata.structuredOutput) {
const [{ zodToJsonSchema }, { z }] = await Promise.all([
getZodToJsonSchema(),
getZod(),
]);
if (responseFormat instanceof z.ZodType)
payload.format = zodToJsonSchema(responseFormat);
}
if (!stream) {
const chatResponse = await this.ollama.chat({
...payload,
+2 -1
View File
@@ -35,6 +35,7 @@
"dependencies": {
"@llamaindex/core": "workspace:*",
"@llamaindex/env": "workspace:*",
"openai": "^4.86.0"
"openai": "^4.86.0",
"zod": "^3.24.2"
}
}
+24 -2
View File
@@ -22,6 +22,7 @@ import type {
ClientOptions as OpenAIClientOptions,
OpenAI as OpenAILLM,
} from "openai";
import { zodResponseFormat } from "openai/helpers/zod";
import type { ChatModel } from "openai/resources/chat/chat";
import type {
ChatCompletionAssistantMessageParam,
@@ -32,7 +33,12 @@ import type {
ChatCompletionToolMessageParam,
ChatCompletionUserMessageParam,
} from "openai/resources/chat/completions";
import type { ChatCompletionMessageParam } from "openai/resources/index.js";
import type {
ChatCompletionMessageParam,
ResponseFormatJSONObject,
ResponseFormatJSONSchema,
} from "openai/resources/index.js";
import { z } from "zod";
import {
AzureOpenAIWithUserAgent,
getAzureConfigFromEnv,
@@ -292,6 +298,7 @@ export class OpenAI extends ToolCallLLM<OpenAIAdditionalChatOptions> {
maxTokens: this.maxTokens,
contextWindow,
tokenizer: Tokenizers.CL100K_BASE,
structuredOutput: true,
};
}
@@ -385,7 +392,8 @@ export class OpenAI extends ToolCallLLM<OpenAIAdditionalChatOptions> {
| ChatResponse<ToolCallLLMMessageOptions>
| AsyncIterable<ChatResponseChunk<ToolCallLLMMessageOptions>>
> {
const { messages, stream, tools, additionalChatOptions } = params;
const { messages, stream, tools, responseFormat, additionalChatOptions } =
params;
const baseRequestParams = <OpenAILLM.Chat.ChatCompletionCreateParams>{
model: this.model,
temperature: this.temperature,
@@ -408,6 +416,20 @@ export class OpenAI extends ToolCallLLM<OpenAIAdditionalChatOptions> {
if (!isTemperatureSupported(baseRequestParams.model))
delete baseRequestParams.temperature;
//add response format for the structured output
if (responseFormat && this.metadata.structuredOutput) {
if (responseFormat instanceof z.ZodType)
baseRequestParams.response_format = zodResponseFormat(
responseFormat,
"response_format",
);
else {
baseRequestParams.response_format = responseFormat as
| ResponseFormatJSONObject
| ResponseFormatJSONSchema;
}
}
// Streaming
if (stream) {
return this.streamChat(baseRequestParams);
+1
View File
@@ -64,6 +64,7 @@ export class Perplexity extends OpenAI {
contextWindow:
PERPLEXITY_MODELS[this.model as PerplexityModelName]?.contextWindow,
tokenizer: Tokenizers.CL100K_BASE,
structuredOutput: false,
};
}
}
+1
View File
@@ -145,6 +145,7 @@ export class ReplicateLLM extends BaseLLM {
maxTokens: this.maxTokens,
contextWindow: ALL_AVAILABLE_REPLICATE_MODELS[this.model].contextWindow,
tokenizer: undefined,
structuredOutput: false,
};
}
@@ -117,7 +117,15 @@ export class PineconeVectorStore extends BaseVectorStore {
}
const idx: Index = await this.index();
const nodes = embeddingResults.map(this.nodeToRecord);
const nodes = embeddingResults.map((node) => {
const nodeRecord = this.nodeToRecord(node);
if (nodeRecord.metadata.ref_doc_id) {
// adding refDoc id as prefix to the chunk to find them using refDoc id
nodeRecord.id = `${nodeRecord.metadata.ref_doc_id}_chunk_${nodeRecord.id}`;
}
return nodeRecord;
});
for (let i = 0; i < nodes.length; i += this.chunkSize) {
const chunk = nodes.slice(i, i + this.chunkSize);
@@ -148,8 +156,43 @@ export class PineconeVectorStore extends BaseVectorStore {
* @returns Promise that resolves if the delete query did not throw an error.
*/
async delete(refDocId: string, deleteKwargs?: object): Promise<void> {
const idx = await this.index();
return idx.deleteOne(refDocId);
const [idx, index] = await Promise.all([
this.index(),
//to get the information about the index
this.db?.describeIndex(this.indexName),
]);
if (index?.spec?.pod) {
//if the index is a pod, delete the document by the metadata
await idx.deleteMany({
metadata: {
ref_doc_id: refDocId,
},
});
} else if (index?.spec?.serverless) {
// filtering on metadata is not supported in serverless indexes
// for serverless indexes, we can delete document by ID prefix
// ref:https://docs.pinecone.io/guides/data/delete-data#delete-records-by-metadata
// get the list of ids with the prefix (not supportered in non serverless indexes)
let list = await idx.listPaginated({
prefix: refDocId,
});
//do while loop to delete the document if there is no next paginationToken
do {
const ids = list?.vectors?.map((v) => v.id);
if (ids && ids.length > 0) {
await idx.deleteMany(ids);
}
if (list.pagination?.next) {
list = await idx.listPaginated({
prefix: refDocId,
paginationToken: list.pagination?.next,
});
}
} while (list.pagination?.next);
}
}
/**
+1
View File
@@ -41,6 +41,7 @@ export class VercelLLM extends ToolCallLLM<VercelAdditionalChatOptions> {
topP: 1,
contextWindow: 128000,
tokenizer: undefined,
structuredOutput: false,
};
}
-1
View File
@@ -230,7 +230,6 @@
"mammoth": "^1.7.2",
"mongodb": "^6.7.0",
"notion-md-crawler": "^1.0.0",
"papaparse": "^5.4.1",
"unpdf": "^0.12.1"
}
}
+1 -1
View File
@@ -60,7 +60,7 @@ export class SimpleCosmosDBReader implements BaseReader {
const metadataFields = config.metadataFields;
try {
let res = await container.items.query(query).fetchAll();
const res = await container.items.query(query).fetchAll();
const documents: Document[] = [];
for (const item of res.resources) {
+1
View File
@@ -0,0 +1 @@
output/
+54
View File
@@ -0,0 +1,54 @@
{
"name": "@llamaindex/tools",
"description": "LlamaIndex Tools",
"version": "0.0.1",
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"exports": {
".": {
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
},
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
}
},
"files": [
"dist"
],
"repository": {
"type": "git",
"url": "git+https://github.com/run-llama/LlamaIndexTS.git",
"directory": "packages/tools"
},
"scripts": {
"build": "bunchee",
"dev": "bunchee --watch",
"test": "vitest run",
"test:watch": "vitest watch"
},
"devDependencies": {
"bunchee": "6.4.0",
"vitest": "^2.1.5",
"@types/node": "^22.9.0",
"@types/papaparse": "^5.3.15"
},
"dependencies": {
"@apidevtools/swagger-parser": "^10.1.0",
"@llamaindex/core": "workspace:*",
"@llamaindex/env": "workspace:*",
"@e2b/code-interpreter": "^1.0.4",
"duck-duck-scrape": "^2.2.5",
"papaparse": "^5.4.1",
"marked": "^14.1.2",
"got": "^14.4.1",
"formdata-node": "^6.0.3",
"ajv": "^8.12.0",
"wikipedia": "^2.1.2",
"zod": "^3.23.8"
}
}
+35
View File
@@ -0,0 +1,35 @@
import fs from "node:fs";
import path from "node:path";
export async function saveDocument(filePath: string, content: string | Buffer) {
if (path.isAbsolute(filePath)) {
throw new Error("Absolute file paths are not allowed.");
}
const dirPath = path.dirname(filePath);
await fs.promises.mkdir(dirPath, { recursive: true });
if (typeof content === "string") {
await fs.promises.writeFile(filePath, content, "utf-8");
} else {
await fs.promises.writeFile(filePath, content);
}
}
export function getFileUrl(
filePath: string,
options?: {
fileServerURLPrefix?: string | undefined;
},
): string {
const fileServerURLPrefix =
options?.fileServerURLPrefix || process.env.FILESERVER_URL_PREFIX;
if (!fileServerURLPrefix) {
throw new Error(
"To get a file URL, please provide a fileServerURLPrefix or set FILESERVER_URL_PREFIX environment variable.",
);
}
return `${fileServerURLPrefix}/${filePath}`;
}
+9
View File
@@ -0,0 +1,9 @@
export * from "./tools/code-generator";
export * from "./tools/document-generator";
export * from "./tools/duckduckgo";
export * from "./tools/form-filling";
export * from "./tools/img-gen";
export * from "./tools/interpreter";
export * from "./tools/openapi-action";
export * from "./tools/weather";
export * from "./tools/wiki";
+126
View File
@@ -0,0 +1,126 @@
import { Settings, type JSONValue } from "@llamaindex/core/global";
import type { ChatMessage } from "@llamaindex/core/llms";
import { tool } from "@llamaindex/core/tools";
import { z } from "zod";
// prompt based on https://github.com/e2b-dev/ai-artifacts
const CODE_GENERATION_PROMPT = `You are a skilled software engineer. You do not make mistakes. Generate an artifact. You can install additional dependencies. You can use one of the following templates:\n
1. code-interpreter-multilang: "Runs code as a Jupyter notebook cell. Strong data analysis angle. Can use complex visualisation to explain results.". File: script.py. Dependencies installed: python, jupyter, numpy, pandas, matplotlib, seaborn, plotly. Port: none.
2. nextjs-developer: "A Next.js 13+ app that reloads automatically. Using the pages router.". File: pages/index.tsx. Dependencies installed: nextjs@14.2.5, typescript, @types/node, @types/react, @types/react-dom, postcss, tailwindcss, shadcn. Port: 3000.
3. vue-developer: "A Vue.js 3+ app that reloads automatically. Only when asked specifically for a Vue app.". File: app.vue. Dependencies installed: vue@latest, nuxt@3.13.0, tailwindcss. Port: 3000.
4. streamlit-developer: "A streamlit app that reloads automatically.". File: app.py. Dependencies installed: streamlit, pandas, numpy, matplotlib, request, seaborn, plotly. Port: 8501.
5. gradio-developer: "A gradio app. Gradio Blocks/Interface should be called demo.". File: app.py. Dependencies installed: gradio, pandas, numpy, matplotlib, request, seaborn, plotly. Port: 7860.
Provide detail information about the artifact you're about to generate in the following JSON format with the following keys:
commentary: Describe what you're about to do and the steps you want to take for generating the artifact in great detail.
template: Name of the template used to generate the artifact.
title: Short title of the artifact. Max 3 words.
description: Short description of the artifact. Max 1 sentence.
additional_dependencies: Additional dependencies required by the artifact. Do not include dependencies that are already included in the template.
has_additional_dependencies: Detect if additional dependencies that are not included in the template are required by the artifact.
install_dependencies_command: Command to install additional dependencies required by the artifact.
port: Port number used by the resulted artifact. Null when no ports are exposed.
file_path: Relative path to the file, including the file name.
code: Code generated by the artifact. Only runnable code is allowed.
Make sure to use the correct syntax for the programming language you're using. Make sure to generate only one code file. If you need to use CSS, make sure to include the CSS in the code file using Tailwind CSS syntax.
`;
// detail information to execute code
export type CodeArtifact = {
commentary: string;
template: string;
title: string;
description: string;
additional_dependencies: string[];
has_additional_dependencies: boolean;
install_dependencies_command: string;
port: number | null;
file_path: string;
code: string;
files?: string[];
};
export type CodeGeneratorToolOutput = {
isError: boolean;
artifact?: CodeArtifact;
};
// Helper function
async function generateArtifact(
query: string,
oldCode?: string,
attachments?: string[],
): Promise<CodeArtifact> {
const userMessage = `
${query}
${oldCode ? `The existing code is: \n\`\`\`${oldCode}\`\`\`` : ""}
${attachments ? `The attachments are: \n${attachments.join("\n")}` : ""}
`;
const messages: ChatMessage[] = [
{ role: "system", content: CODE_GENERATION_PROMPT },
{ role: "user", content: userMessage },
];
try {
const response = await Settings.llm.chat({ messages });
const content = response.message.content.toString();
const jsonContent = content
.replace(/^```json\s*|\s*```$/g, "")
.replace(/^`+|`+$/g, "")
.trim();
const artifact = JSON.parse(jsonContent) as CodeArtifact;
return artifact;
} catch (error) {
console.log("Failed to generate artifact", error);
throw error;
}
}
export const codeGenerator = () => {
return tool({
name: "artifact",
description:
"Generate a code artifact based on the input. Don't call this tool if the user has not asked for code generation. E.g. if the user asks to write a description or specification, don't call this tool.",
parameters: z.object({
requirement: z
.string()
.describe("The description of the application you want to build."),
oldCode: z
.string()
.optional()
.describe("The existing code to be modified"),
sandboxFiles: z
.array(z.string())
.optional()
.describe(
"A list of sandbox file paths. Include these files if the code requires them.",
),
}),
execute: async ({ requirement, oldCode, sandboxFiles }) => {
try {
const artifact = await generateArtifact(
requirement,
oldCode,
sandboxFiles, // help the generated code use exact files
);
if (sandboxFiles) {
artifact.files = sandboxFiles;
}
return {
isError: false,
artifact,
} as JSONValue;
} catch (error) {
return {
isError: true,
};
}
},
});
};
@@ -0,0 +1,112 @@
import { tool } from "@llamaindex/core/tools";
import { marked } from "marked";
import path from "node:path";
import { z } from "zod";
import { getFileUrl, saveDocument } from "../helper";
const COMMON_STYLES = `
body {
font-family: Arial, sans-serif;
line-height: 1.3;
color: #333;
}
h1, h2, h3, h4, h5, h6 {
margin-top: 1em;
margin-bottom: 0.5em;
}
p {
margin-bottom: 0.7em;
}
code {
background-color: #f4f4f4;
padding: 2px 4px;
border-radius: 4px;
}
pre {
background-color: #f4f4f4;
padding: 10px;
border-radius: 4px;
overflow-x: auto;
}
table {
border-collapse: collapse;
width: 100%;
margin-bottom: 1em;
}
th, td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
th {
background-color: #f2f2f2;
font-weight: bold;
}
img {
max-width: 90%;
height: auto;
display: block;
margin: 1em auto;
border-radius: 10px;
}
`;
const HTML_SPECIFIC_STYLES = `
body {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
`;
const HTML_TEMPLATE = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
${COMMON_STYLES}
${HTML_SPECIFIC_STYLES}
</style>
</head>
<body>
{{content}}
</body>
</html>
`;
export type DocumentGeneratorParams = {
/** Directory where generated documents will be saved */
outputDir: string;
/** Prefix for the file server URL */
fileServerURLPrefix?: string;
};
export const documentGenerator = (params: DocumentGeneratorParams) => {
return tool({
name: "document_generator",
description:
"Generate HTML document from markdown content. Return a file url to the document",
parameters: z.object({
originalContent: z
.string()
.describe("The original markdown content to convert"),
fileName: z
.string()
.describe("The name of the document file (without extension)"),
}),
execute: async ({ originalContent, fileName }): Promise<string> => {
const { outputDir, fileServerURLPrefix } = params;
const htmlContent = await marked(originalContent);
const fileContent = HTML_TEMPLATE.replace("{{content}}", htmlContent);
const filePath = path.join(outputDir, `${fileName}.html`);
await saveDocument(filePath, fileContent);
const fileUrl = getFileUrl(filePath, { fileServerURLPrefix });
return `URL: ${fileUrl}`;
},
});
};
+48
View File
@@ -0,0 +1,48 @@
import { tool } from "@llamaindex/core/tools";
import { search } from "duck-duck-scrape";
import { z } from "zod";
export type DuckDuckGoToolOutput = Array<{
title: string;
description: string;
url: string;
}>;
export const duckduckgo = () => {
return tool({
name: "duckduckgo_search",
description:
"Use this function to search for information (only text) in the internet using DuckDuckGo.",
parameters: z.object({
query: z.string().describe("The query to search in DuckDuckGo."),
region: z
.string()
.optional()
.describe(
"Optional, The region to be used for the search in [country-language] convention, ex us-en, uk-en, ru-ru, etc...",
),
maxResults: z
.number()
.default(10)
.optional()
.describe(
"Optional, The maximum number of results to be returned. Default is 10.",
),
}),
execute: async ({
query,
region,
maxResults = 10,
}): Promise<DuckDuckGoToolOutput> => {
const options = region ? { region } : {};
const searchResults = await search(query, options);
return searchResults.results.slice(0, maxResults).map((result) => {
return {
title: result.title,
description: result.description,
url: result.url,
};
});
},
});
};
+234
View File
@@ -0,0 +1,234 @@
import { Settings } from "@llamaindex/core/global";
import { tool } from "@llamaindex/core/tools";
import fs from "fs";
import Papa from "papaparse";
import path from "path";
import { z } from "zod";
import { getFileUrl, saveDocument } from "../helper";
export type MissingCell = {
rowIndex: number;
columnIndex: number;
question: string;
};
const CSV_EXTRACTION_PROMPT = `You are a data analyst. You are given a table with missing cells.
Your task is to identify the missing cells and the questions needed to fill them.
IMPORTANT: Column indices should be 0-based
# Instructions:
- Understand the entire content of the table and the topics of the table.
- Identify the missing cells and the meaning of the data in the cells.
- For each missing cell, provide the row index and the correct column index (remember: first data column is 1).
- For each missing cell, provide the question needed to fill the cell (it's important to provide the question that is relevant to the topic of the table).
- Since the cell's value should be concise, the question should request a numerical answer or a specific value.
- Finally, only return the answer in JSON format with the following schema:
{
"missing_cells": [
{
"rowIndex": number,
"columnIndex": number,
"question": string
}
]
}
- If there are no missing cells, return an empty array.
- The answer is only the JSON object, nothing else and don't wrap it inside markdown code block.
# Example:
# | | Name | Age | City |
# |----|------|-----|------|
# | 0 | John | | Paris|
# | 1 | Mary | | |
# | 2 | | 30 | |
#
# Your thoughts:
# - The table is about people's names, ages, and cities.
# - Row: 1, Column: 2 (Age column), Question: "How old is Mary? Please provide only the numerical answer."
# - Row: 1, Column: 3 (City column), Question: "In which city does Mary live? Please provide only the city name."
# Your answer:
# {
# "missing_cells": [
# {
# "rowIndex": 1,
# "columnIndex": 2,
# "question": "How old is Mary? Please provide only the numerical answer."
# },
# {
# "rowIndex": 1,
# "columnIndex": 3,
# "question": "In which city does Mary live? Please provide only the city name."
# }
# ]
# }
# Here is your task:
- Table content:
{table_content}
- Your answer:
`;
export const extractMissingCells = () => {
return tool({
name: "extract_missing_cells",
description:
"Use this tool to extract missing cells in a CSV file and generate questions to fill them. This tool only works with local file path.",
parameters: z.object({
filePath: z.string().describe("The local file path to the CSV file."),
}),
execute: async ({ filePath }): Promise<MissingCell[]> => {
let tableContent: string[][];
try {
tableContent = await readCsvFile(filePath);
} catch (error) {
throw new Error(
"Failed to read CSV file. Make sure that you are reading a local file path (not a sandbox path).",
);
}
const prompt = CSV_EXTRACTION_PROMPT.replace(
"{table_content}",
formatToMarkdownTable(tableContent),
);
const llm = Settings.llm;
const response = await llm.complete({ prompt });
const parsedResponse = JSON.parse(response.text) as {
missing_cells: MissingCell[];
};
if (!parsedResponse.missing_cells) {
throw new Error(
"The answer is not in the correct format. There should be a missing_cells array.",
);
}
return parsedResponse.missing_cells;
},
});
};
export type FillMissingCellsParams = {
/** Directory where generated documents will be saved */
outputDir: string;
/** Prefix for the file server URL */
fileServerURLPrefix?: string;
};
export type FillMissingCellsToolOutput = {
isSuccess: boolean;
errorMessage?: string;
fileUrl?: string;
};
export const fillMissingCells = (params: FillMissingCellsParams) => {
return tool({
name: "fill_missing_cells",
description:
"Use this tool to fill missing cells in a CSV file with provided answers. This tool only works with local file path.",
parameters: z.object({
filePath: z.string().describe("The local file path to the CSV file."),
cells: z
.array(
z.object({
rowIndex: z.number(),
columnIndex: z.number(),
answer: z.string(),
}),
)
.describe("Array of cells to fill with their answers"),
}),
execute: async ({ filePath, cells }): Promise<string> => {
const { outputDir, fileServerURLPrefix } = params;
// Read the CSV file
const fileContent = await fs.promises.readFile(filePath, "utf8");
// Parse CSV with PapaParse
const parseResult = Papa.parse<string[]>(fileContent, {
header: false, // Ensure the header is not treated as a separate object
skipEmptyLines: false, // Ensure empty lines are not skipped
});
if (parseResult.errors.length) {
throw new Error(
"Failed to parse CSV file: " + parseResult.errors[0]?.message,
);
}
const rows = parseResult.data;
// Fill the cells with answers
for (const cell of cells) {
// Adjust rowIndex to start from 1 for data rows
const adjustedRowIndex = cell.rowIndex + 1;
if (
adjustedRowIndex < rows.length &&
cell.columnIndex < (rows[adjustedRowIndex]?.length ?? 0) &&
rows[adjustedRowIndex]
) {
rows[adjustedRowIndex][cell.columnIndex] = cell.answer;
}
}
// Convert back to CSV format
const updatedContent = Papa.unparse(rows, {
delimiter: parseResult.meta.delimiter,
});
// Use the helper function to write the file
const parsedPath = path.parse(filePath);
const newFileName = `${parsedPath.name}-filled${parsedPath.ext}`;
const newFilePath = path.join(outputDir, newFileName);
await saveDocument(newFilePath, updatedContent);
const newFileUrl = getFileUrl(newFilePath, { fileServerURLPrefix });
return (
"Successfully filled missing cells in the CSV file. File URL to show to the user: " +
newFileUrl
);
},
});
};
async function readCsvFile(filePath: string): Promise<string[][]> {
return new Promise((resolve, reject) => {
fs.readFile(filePath, "utf8", (err, data) => {
if (err) {
reject(err);
return;
}
const parsedData = Papa.parse<string[]>(data, {
skipEmptyLines: false,
});
if (parsedData.errors.length) {
reject(parsedData.errors);
return;
}
// Ensure all rows have the same number of columns as the header
const maxColumns = parsedData.data[0]?.length ?? 0;
const paddedRows = parsedData.data.map((row) => {
return [...row, ...Array(maxColumns - row.length).fill("")];
});
resolve(paddedRows);
});
});
}
function formatToMarkdownTable(data: string[][]): string {
if (data.length === 0) return "";
const maxColumns = data[0]?.length ?? 0;
const headerRow = `| ${data[0]?.join(" | ") ?? ""} |`;
const separatorRow = `| ${Array(maxColumns).fill("---").join(" | ")} |`;
const dataRows = data.slice(1).map((row) => `| ${row.join(" | ")} |`);
return [headerRow, separatorRow, ...dataRows].join("\n");
}
+76
View File
@@ -0,0 +1,76 @@
import { tool } from "@llamaindex/core/tools";
import { FormData } from "formdata-node";
import got from "got";
import path from "path";
import { Readable } from "stream";
import { z } from "zod";
import { getFileUrl, saveDocument } from "../helper";
export type ImgGeneratorToolOutput = {
isSuccess: boolean;
imageUrl?: string;
errorMessage?: string;
};
export type ImgGeneratorToolParams = {
/** Directory where generated images will be saved */
outputDir: string;
/** STABILITY_API_KEY key is required to run image generator. Get it here: https://platform.stability.ai/account/keys */
apiKey: string;
/** Output format of the generated image */
outputFormat?: string;
/** Prefix for the file server URL */
fileServerURLPrefix?: string | undefined;
};
export const imageGenerator = (params: ImgGeneratorToolParams) => {
return tool({
name: "image_generator",
description: "Use this function to generate an image based on the prompt.",
parameters: z.object({
prompt: z.string().describe("The prompt to generate the image"),
}),
execute: async ({ prompt }): Promise<ImgGeneratorToolOutput> => {
const { outputDir, apiKey, fileServerURLPrefix } = params;
const outputFormat = params.outputFormat ?? "webp";
try {
const buffer = await promptToImgBuffer(prompt, apiKey, outputFormat);
const filename = `${crypto.randomUUID()}.${outputFormat}`;
const filePath = path.join(outputDir, filename);
await saveDocument(filePath, buffer);
const imageUrl = getFileUrl(filePath, { fileServerURLPrefix });
return { isSuccess: true, imageUrl };
} catch (error) {
console.error(error);
return {
isSuccess: false,
errorMessage: "Failed to generate image. Please try again.",
};
}
},
});
};
async function promptToImgBuffer(
prompt: string,
apiKey: string,
outputFormat: string,
): Promise<Buffer> {
const form = new FormData();
form.append("prompt", prompt);
form.append("output_format", outputFormat);
const apiUrl = "https://api.stability.ai/v2beta/stable-image/generate/core";
const buffer = await got
.post(apiUrl, {
body: form as unknown as Buffer | Readable | string,
headers: {
Authorization: `Bearer ${apiKey}`,
Accept: "image/*",
},
})
.buffer();
return buffer;
}
+161
View File
@@ -0,0 +1,161 @@
import { type Logs, Result, Sandbox } from "@e2b/code-interpreter";
import { tool } from "@llamaindex/core/tools";
import fs from "fs";
import path from "node:path";
import { z } from "zod";
import { getFileUrl, saveDocument } from "../helper";
export type InterpreterExtraType =
| "html"
| "markdown"
| "svg"
| "png"
| "jpeg"
| "pdf"
| "latex"
| "json"
| "javascript";
export type InterpreterExtraResult = {
type: InterpreterExtraType;
content?: string;
filename?: string;
url?: string;
};
export type InterpreterToolOutput = {
isError: boolean;
logs: Logs;
text?: string;
extraResult: InterpreterExtraResult[];
retryCount?: number;
};
export type InterpreterToolParams = {
/** E2B API key required for authentication. Get yours at https://e2b.dev/docs/legacy/getting-started/api-key */
apiKey: string;
/** Directory where output files (charts, images, etc.) will be saved when code is executed */
outputDir: string;
/** Local directory containing files that need to be uploaded to the sandbox environment before code execution */
uploadedFilesDir: string;
/** Prefix for the file server URL */
fileServerURLPrefix?: string;
};
export const interpreter = (params: InterpreterToolParams) => {
const { apiKey, outputDir, uploadedFilesDir, fileServerURLPrefix } = params;
return tool({
name: "interpreter",
description:
"Execute python code in a Jupyter notebook cell and return any result, stdout, stderr, display_data, and error.",
parameters: z.object({
code: z.string().describe("The python code to execute in a single cell"),
sandboxFiles: z
.array(z.string())
.optional()
.describe("List of local file paths to be used by the code"),
retryCount: z
.number()
.default(0)
.optional()
.describe("The number of times the tool has been retried"),
}),
execute: async ({ code, sandboxFiles, retryCount = 0 }) => {
if (retryCount >= 3) {
return {
isError: true,
logs: { stdout: [], stderr: [] },
text: "Max retries reached",
extraResult: [],
};
}
const interpreter = await Sandbox.create({ apiKey });
await uploadFilesToSandbox(interpreter, uploadedFilesDir, sandboxFiles);
const exec = await interpreter.runCode(code);
const extraResult = await getExtraResult(
outputDir,
exec.results[0],
fileServerURLPrefix,
);
return {
isError: !!exec.error,
logs: exec.logs,
text: exec.text,
extraResult,
retryCount: retryCount + 1,
} as InterpreterToolOutput;
},
});
};
async function uploadFilesToSandbox(
codeInterpreter: Sandbox,
uploadedFilesDir: string,
sandboxFiles: string[] = [],
) {
try {
for (const filePath of sandboxFiles) {
const fileName = path.basename(filePath);
const localFilePath = path.join(uploadedFilesDir, fileName);
const content = fs.readFileSync(localFilePath);
const arrayBuffer = new Uint8Array(content).buffer;
await codeInterpreter.files.write(filePath, arrayBuffer);
}
} catch (error) {
console.error("Got error when uploading files to sandbox", error);
}
}
async function getExtraResult(
outputDir: string,
res?: Result,
fileServerURLPrefix?: string,
): Promise<InterpreterExtraResult[]> {
if (!res) return [];
const output: InterpreterExtraResult[] = [];
try {
const formats = res.formats();
const results = formats.map((f) => res[f as keyof Result]);
for (let i = 0; i < formats.length; i++) {
const ext = formats[i];
const data = results[i];
switch (ext) {
case "png":
case "jpeg":
case "svg":
case "pdf": {
const { filename, filePath } = await saveToDisk(outputDir, data, ext);
const fileUrl = getFileUrl(filePath, { fileServerURLPrefix });
output.push({
type: ext as InterpreterExtraType,
filename,
url: fileUrl,
});
break;
}
default:
output.push({
type: ext as InterpreterExtraType,
content: data,
});
break;
}
}
} catch (error) {
console.error("Error when parsing e2b response", error);
}
return output;
}
async function saveToDisk(outputDir: string, base64Data: string, ext: string) {
const filename = `${crypto.randomUUID()}.${ext}`;
const buffer = Buffer.from(base64Data, "base64");
const filePath = path.join(outputDir, filename);
await saveDocument(filePath, buffer);
return { filename, filePath };
}
+177
View File
@@ -0,0 +1,177 @@
import SwaggerParser from "@apidevtools/swagger-parser";
import type { JSONValue } from "@llamaindex/core/global";
import type { ToolMetadata } from "@llamaindex/core/llms";
import { FunctionTool } from "@llamaindex/core/tools";
import { type JSONSchemaType } from "ajv";
import got from "got";
interface DomainHeaders {
[key: string]: { [header: string]: string };
}
type Input = {
url: string;
params: object;
};
type APIInfo = {
description: string;
title: string;
};
export class OpenAPIActionTool {
// cache the loaded specs by URL
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private static specs: Record<string, any> = {};
private readonly INVALID_URL_PROMPT =
"This url did not include a hostname or scheme. Please determine the complete URL and try again.";
private createLoadSpecMetaData = (info: APIInfo) => {
return {
name: "load_openapi_spec",
description: `Use this to retrieve the OpenAPI spec for the API named ${info.title} with the following description: ${info.description}. Call it before making any requests to the API.`,
};
};
private readonly createMethodCallMetaData = (
method: "POST" | "PATCH" | "GET",
info: APIInfo,
) => {
return {
name: `${method.toLowerCase()}_request`,
description: `Use this to call the ${method} method on the API named ${info.title}`,
parameters: {
type: "object",
properties: {
url: {
type: "string",
description: `The url to make the ${method} request against`,
},
params: {
type: "object",
description:
method === "GET"
? "the URL parameters to provide with the get request"
: `the key-value pairs to provide with the ${method} request`,
},
},
required: ["url"],
},
} as ToolMetadata<JSONSchemaType<Input>>;
};
constructor(
public openapi_uri: string,
public domainHeaders: DomainHeaders = {},
) {}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async loadOpenapiSpec(url: string): Promise<any> {
const api = await SwaggerParser.validate(url);
return {
servers: "servers" in api ? api.servers : "",
info: { description: api.info.description, title: api.info.title },
endpoints: api.paths,
};
}
async getRequest(input: Input): Promise<JSONValue> {
if (!this.validUrl(input.url)) {
return this.INVALID_URL_PROMPT;
}
try {
const data = await got
.get(input.url, {
headers: this.getHeadersForUrl(input.url),
searchParams: input.params as URLSearchParams,
})
.json();
return data as JSONValue;
} catch (error) {
return error as JSONValue;
}
}
async postRequest(input: Input): Promise<JSONValue> {
if (!this.validUrl(input.url)) {
return this.INVALID_URL_PROMPT;
}
try {
const res = await got.post(input.url, {
headers: this.getHeadersForUrl(input.url),
json: input.params,
});
return res.body as JSONValue;
} catch (error) {
return error as JSONValue;
}
}
async patchRequest(input: Input): Promise<JSONValue> {
if (!this.validUrl(input.url)) {
return this.INVALID_URL_PROMPT;
}
try {
const res = await got.patch(input.url, {
headers: this.getHeadersForUrl(input.url),
json: input.params,
});
return res.body as JSONValue;
} catch (error) {
return error as JSONValue;
}
}
public async toToolFunctions() {
if (!OpenAPIActionTool.specs[this.openapi_uri]) {
console.log(`Loading spec for URL: ${this.openapi_uri}`);
const spec = await this.loadOpenapiSpec(this.openapi_uri);
OpenAPIActionTool.specs[this.openapi_uri] = spec;
}
const spec = OpenAPIActionTool.specs[this.openapi_uri];
// TODO: read endpoints with parameters from spec and create one tool for each endpoint
// For now, we just create a tool for each HTTP method which does not work well for passing parameters
return [
FunctionTool.from(() => {
return spec;
}, this.createLoadSpecMetaData(spec.info)),
FunctionTool.from(
this.getRequest.bind(this),
this.createMethodCallMetaData("GET", spec.info),
),
FunctionTool.from(
this.postRequest.bind(this),
this.createMethodCallMetaData("POST", spec.info),
),
FunctionTool.from(
this.patchRequest.bind(this),
this.createMethodCallMetaData("PATCH", spec.info),
),
];
}
private validUrl(url: string): boolean {
const parsed = new URL(url);
return !!parsed.protocol && !!parsed.hostname;
}
private getDomain(url: string): string {
const parsed = new URL(url);
return parsed.hostname;
}
private getHeadersForUrl(url: string): { [header: string]: string } {
const domain = this.getDomain(url);
return this.domainHeaders[domain] || {};
}
}
export const getOpenAPIActionTools = async (params: {
openapiUri: string;
domainHeaders: DomainHeaders;
}) => {
const { openapiUri, domainHeaders } = params;
const openAPIActionTool = new OpenAPIActionTool(openapiUri, domainHeaders);
return await openAPIActionTool.toToolFunctions();
};
+122
View File
@@ -0,0 +1,122 @@
import { tool } from "@llamaindex/core/tools";
import { z } from "zod";
export type WeatherToolOutput = {
latitude: number;
longitude: number;
generationtime_ms: number;
utc_offset_seconds: number;
timezone: string;
timezone_abbreviation: string;
elevation: number;
current_units: {
time: string;
interval: string;
temperature_2m: string;
weather_code: string;
};
current: {
time: string;
interval: number;
temperature_2m: number;
weather_code: number;
};
hourly_units: {
time: string;
temperature_2m: string;
weather_code: string;
};
hourly: {
time: string[];
temperature_2m: number[];
weather_code: number[];
};
daily_units: {
time: string;
weather_code: string;
};
daily: {
time: string[];
weather_code: number[];
};
};
export const weather = () => {
return tool({
name: "weather",
description: `
Use this function to get the weather of any given location.
Note that the weather code should follow WMO Weather interpretation codes (WW):
0: Clear sky
1, 2, 3: Mainly clear, partly cloudy, and overcast
45, 48: Fog and depositing rime fog
51, 53, 55: Drizzle: Light, moderate, and dense intensity
56, 57: Freezing Drizzle: Light and dense intensity
61, 63, 65: Rain: Slight, moderate and heavy intensity
66, 67: Freezing Rain: Light and heavy intensity
71, 73, 75: Snow fall: Slight, moderate, and heavy intensity
77: Snow grains
80, 81, 82: Rain showers: Slight, moderate, and violent
85, 86: Snow showers slight and heavy
95: Thunderstorm: Slight or moderate
96, 99: Thunderstorm with slight and heavy hail
`,
parameters: z.object({
location: z.string().describe("The location to get the weather"),
}),
execute: async ({ location }): Promise<WeatherToolOutput> => {
return await getWeatherByLocation(location);
},
});
};
async function getWeatherByLocation(
location: string,
): Promise<WeatherToolOutput> {
const { latitude, longitude } = await getGeoLocation(location);
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
const params = new URLSearchParams({
latitude: latitude.toString(),
longitude: longitude.toString(),
current: "temperature_2m,weather_code",
hourly: "temperature_2m,weather_code",
daily: "weather_code",
timezone,
});
const apiUrl = `https://api.open-meteo.com/v1/forecast?${params}`;
const response = await fetch(apiUrl);
if (!response.ok) {
throw new Error(`Weather API request failed: ${response.statusText}`);
}
return (await response.json()) as WeatherToolOutput;
}
async function getGeoLocation(
location: string,
): Promise<{ latitude: number; longitude: number }> {
const params = new URLSearchParams({
name: location,
count: "10",
language: "en",
format: "json",
});
const apiUrl = `https://geocoding-api.open-meteo.com/v1/search?${params}`;
const response = await fetch(apiUrl);
if (!response.ok) {
throw new Error(`Geocoding API request failed: ${response.statusText}`);
}
const data = await response.json();
if (!data.results?.length) {
throw new Error(`No location found for: ${location}`);
}
const { latitude, longitude } = data.results[0];
return { latitude, longitude };
}
+27
View File
@@ -0,0 +1,27 @@
import { tool } from "@llamaindex/core/tools";
import { default as wikipedia } from "wikipedia";
import { z } from "zod";
export type WikiToolOutput = {
title: string;
content: string;
};
export const wiki = () => {
return tool({
name: "wikipedia",
description: "Use this function to search Wikipedia",
parameters: z.object({
query: z.string().describe("The query to search for"),
lang: z.string().describe("The language to search in").default("en"),
}),
execute: async ({ query, lang }): Promise<WikiToolOutput> => {
wikipedia.setLang(lang);
const searchResult = await wikipedia.search(query);
const pageTitle = searchResult?.results[0]?.title;
if (!pageTitle) return { title: "No search results.", content: "" };
const result = await wikipedia.page(pageTitle, { autoSuggest: false });
return { title: pageTitle, content: await result.content() };
},
});
};
@@ -0,0 +1,37 @@
import { Settings } from "@llamaindex/core/global";
import { MockLLM } from "@llamaindex/core/utils";
import { describe, expect, test } from "vitest";
import {
codeGenerator,
type CodeGeneratorToolOutput,
} from "../src/tools/code-generator";
Settings.llm = new MockLLM({
responseMessage: `{
"commentary": "Creating a simple Next.js page with a hello world message",
"template": "nextjs-developer",
"title": "Hello World App",
"description": "A simple Next.js hello world application",
"additional_dependencies": [],
"has_additional_dependencies": false,
"install_dependencies_command": "",
"port": 3000,
"file_path": "pages/index.tsx",
"code": "export default function Home() { return <h1>Hello World</h1> }"
}`,
});
describe("Code Generator Tool", () => {
test("generates Next.js application code", async () => {
const generator = codeGenerator();
const result = (await generator.call({
requirement: "Create a simple Next.js hello world page",
})) as CodeGeneratorToolOutput;
expect(result.isError).toBe(false);
expect(result.artifact).toBeDefined();
expect(result.artifact?.template).toBe("nextjs-developer");
expect(result.artifact?.file_path).toBe("pages/index.tsx");
expect(result.artifact?.code).toContain("export default");
});
});
@@ -0,0 +1,24 @@
import path from "path";
import { describe, expect, test, vi } from "vitest";
import { documentGenerator } from "../src/tools/document-generator";
// Mock the helper functions
vi.mock("../src/helper", () => ({
saveDocument: vi.fn().mockResolvedValue(undefined),
getFileUrl: vi.fn().mockReturnValue("http://example.com/test-doc.html"),
}));
describe("Document Generator Tool", () => {
test("converts markdown to html document", async () => {
const docGen = documentGenerator({
outputDir: path.join(__dirname, "output"),
});
const result = await docGen.call({
originalContent: "# Hello World\nThis is a test",
fileName: "test-doc",
});
expect(result).toBe("URL: http://example.com/test-doc.html");
});
});
+19
View File
@@ -0,0 +1,19 @@
import { describe, expect, test } from "vitest";
import { duckduckgo, type DuckDuckGoToolOutput } from "../src/tools/duckduckgo";
describe("DuckDuckGo Tool", () => {
test("performs search and returns results", async () => {
const searchTool = duckduckgo();
const results = (await searchTool.call({
query: "OpenAI ChatGPT",
maxResults: 3,
})) as DuckDuckGoToolOutput;
expect(Array.isArray(results)).toBe(true);
expect(results.length).toBeLessThanOrEqual(3);
const firstResult = results[0];
expect(firstResult).toHaveProperty("title");
expect(firstResult).toHaveProperty("description");
expect(firstResult).toHaveProperty("url");
});
});
+44
View File
@@ -0,0 +1,44 @@
import fs from "fs";
import path from "path";
import { describe, expect, test, vi } from "vitest";
import { fillMissingCells } from "../src/tools/form-filling";
vi.mock("fs", () => ({
default: {
readFile: vi.fn(),
promises: {
readFile: vi.fn(),
},
},
}));
vi.mock("../src/helper", () => ({
saveDocument: vi.fn(),
getFileUrl: vi.fn().mockReturnValue("http://example.com/filled.csv"),
}));
describe("Form Filling Tools", () => {
test("fillMissingCells fills cells with provided answers", async () => {
// Mock CSV content
const mockCsvContent = "Name,Age,City\nJohn,,Paris\nMary,,";
vi.mocked(fs.promises.readFile).mockResolvedValue(mockCsvContent);
const filler = fillMissingCells({
outputDir: path.join(__dirname, "output"),
});
const result = await filler.call({
filePath: "test.csv",
cells: [
{
rowIndex: 1,
columnIndex: 1,
answer: "25",
},
],
});
expect(result).toContain("Successfully filled missing cells");
expect(result).toContain("http://example.com/filled.csv");
});
});
+45
View File
@@ -0,0 +1,45 @@
import { describe, expect, test, vi } from "vitest";
import {
imageGenerator,
type ImgGeneratorToolOutput,
} from "../src/tools/img-gen";
vi.mock("got", () => ({
default: {
post: vi.fn().mockReturnValue({
buffer: vi.fn().mockResolvedValue(Buffer.from("mock-image-data")),
}),
},
}));
describe("Image Generator Tool", () => {
test("generates image from prompt", async () => {
const imgTool = imageGenerator({
apiKey: process.env.STABILITY_API_KEY!,
outputDir: "output",
fileServerURLPrefix: "http://localhost:3000",
});
const result = (await imgTool.call({
prompt: "a cute cat playing with yarn",
})) as ImgGeneratorToolOutput;
expect(result.isSuccess).toBe(true);
expect(result.imageUrl).toBeDefined();
expect(result.errorMessage).toBeUndefined();
});
test("does not throw error on valid prompt", async () => {
const imgTool = imageGenerator({
apiKey: process.env.STABILITY_API_KEY!,
outputDir: "output",
fileServerURLPrefix: "http://localhost:3000",
});
expect(async () => {
await imgTool.call({
prompt: "a scenic mountain landscape",
});
}).not.toThrow();
});
});
+50
View File
@@ -0,0 +1,50 @@
import { Sandbox } from "@e2b/code-interpreter";
import path from "path";
import { describe, expect, test, vi } from "vitest";
import {
interpreter,
type InterpreterToolOutput,
} from "../src/tools/interpreter";
vi.mock("@e2b/code-interpreter", () => ({
Sandbox: {
create: vi.fn().mockImplementation(() => ({
runCode: vi.fn().mockResolvedValue({
error: null,
logs: {
stdout: ["Hello, World!", "x = 2"],
stderr: [],
},
text: "Hello, World!\nx = 2",
results: [
{
formats: () => [],
},
],
}),
files: {
write: vi.fn().mockResolvedValue(undefined),
},
})),
},
}));
describe("Code Interpreter Tool", () => {
test("executes simple python code", async () => {
const interpreterTool = interpreter({
apiKey: "mock-api-key",
outputDir: path.join(__dirname, "output"),
uploadedFilesDir: path.join(__dirname, "files"),
});
const result = (await interpreterTool.call({
code: "print('Hello, World!')\nx = 1 + 1\nprint(f'x = {x}')",
})) as InterpreterToolOutput;
expect(Sandbox.create).toHaveBeenCalledWith({ apiKey: "mock-api-key" });
expect(result.isError).toBe(false);
expect(result.logs.stdout).toEqual(["Hello, World!", "x = 2"]);
expect(result.retryCount).toBe(1);
expect(result.extraResult).toEqual([]);
});
});
@@ -0,0 +1,70 @@
import SwaggerParser from "@apidevtools/swagger-parser";
import got from "got";
import { describe, expect, test, vi } from "vitest";
import { OpenAPIActionTool } from "../src/tools/openapi-action";
// Mock SwaggerParser and got
vi.mock("@apidevtools/swagger-parser", () => ({
default: {
validate: vi.fn(),
},
}));
vi.mock("got", () => ({
default: {
get: vi.fn(),
post: vi.fn(),
patch: vi.fn(),
},
}));
describe("OpenAPI Action Tool", () => {
test("loads and executes API requests", async () => {
// Mock swagger spec
vi.mocked(SwaggerParser.validate).mockResolvedValue({
openapi: "3.0.0",
info: {
title: "Test API",
description: "Test API Description",
version: "1.0.0",
},
paths: {
"/test": {
get: {
description: "Test endpoint",
responses: {
"200": {
description: "Successful response",
},
},
},
},
},
});
// Mock API response
vi.mocked(got.get).mockReturnValue({
json: () => Promise.resolve({ data: "test response" }),
} as ReturnType<typeof got.get>);
const tool = new OpenAPIActionTool("https://api.test.com/openapi.json");
const tools = await tool.toToolFunctions();
// Verify tools were created
expect(tools).toHaveLength(4); // load_spec, get, post, patch
// Test GET request
const result = await tool.getRequest({
url: "https://api.test.com/test",
params: { key: "value" },
});
expect(got.get).toHaveBeenCalledWith(
"https://api.test.com/test",
expect.objectContaining({
searchParams: { key: "value" },
}),
);
expect(result).toEqual({ data: "test response" });
});
});
+15
View File
@@ -0,0 +1,15 @@
import { describe, expect, test } from "vitest";
import { weather } from "../src/tools/weather";
describe("Weather Tool", () => {
test("weather tool returns data for valid location", async () => {
const weatherTool = weather();
const result = await weatherTool.call({
location: "London",
});
expect(result).toHaveProperty("current");
expect(result).toHaveProperty("hourly");
expect(result).toHaveProperty("daily");
});
});
+15
View File
@@ -0,0 +1,15 @@
import { describe, expect, test } from "vitest";
import { wiki } from "../src/tools/wiki";
describe("Wikipedia Tool", () => {
test("wiki tool returns content for valid query", async () => {
const wikipediaTool = wiki();
const result = await wikipediaTool.call({
query: "Albert Einstein",
lang: "en",
});
expect(result).toHaveProperty("title");
expect(result).toHaveProperty("content");
});
});
+15
View File
@@ -0,0 +1,15 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./dist/type",
"tsBuildInfoFile": "./dist/.tsbuildinfo",
"emitDeclarationOnly": true,
"moduleResolution": "Bundler",
"skipLibCheck": true,
"strict": true,
"types": ["node"]
},
"include": ["./src"],
"exclude": ["node_modules"]
}
+300 -19
View File
@@ -7,6 +7,10 @@ settings:
importers:
.:
dependencies:
p-retry:
specifier: ^6.2.1
version: 6.2.1
devDependencies:
'@changesets/cli':
specifier: ^2.27.5
@@ -680,6 +684,9 @@ importers:
'@llamaindex/together':
specifier: ^0.0.5
version: link:../packages/providers/together
'@llamaindex/tools':
specifier: ^0.0.1
version: link:../packages/tools
'@llamaindex/upstash':
specifier: ^0.0.13
version: link:../packages/providers/storage/upstash
@@ -1266,6 +1273,9 @@ importers:
bunchee:
specifier: 6.4.0
version: 6.4.0(typescript@5.7.3)
vitest:
specifier: ^2.1.5
version: 2.1.5(@edge-runtime/vm@4.0.4)(@types/node@22.13.5)(happy-dom@15.11.7)(lightningcss@1.29.1)(msw@2.7.0(@types/node@22.13.5)(typescript@5.7.3))(terser@5.38.2)
packages/providers/mixedbread:
dependencies:
@@ -1297,6 +1307,12 @@ importers:
remeda:
specifier: ^2.17.3
version: 2.20.1
zod:
specifier: ^3.24.2
version: 3.24.2
zod-to-json-schema:
specifier: ^3.23.3
version: 3.24.1(zod@3.24.2)
devDependencies:
bunchee:
specifier: 6.4.0
@@ -1313,6 +1329,9 @@ importers:
openai:
specifier: ^4.86.0
version: 4.86.0(ws@8.18.0(bufferutil@4.0.9))(zod@3.24.2)
zod:
specifier: ^3.24.2
version: 3.24.2
devDependencies:
bunchee:
specifier: 6.4.0
@@ -1683,9 +1702,6 @@ importers:
notion-md-crawler:
specifier: ^1.0.0
version: 1.0.1
papaparse:
specifier: ^5.4.1
version: 5.5.2
unpdf:
specifier: ^0.12.1
version: 0.12.1
@@ -1709,6 +1725,58 @@ importers:
specifier: ^13.4.8
version: 13.4.8
packages/tools:
dependencies:
'@apidevtools/swagger-parser':
specifier: ^10.1.0
version: 10.1.1(openapi-types@12.1.3)
'@e2b/code-interpreter':
specifier: ^1.0.4
version: 1.0.4
'@llamaindex/core':
specifier: workspace:*
version: link:../core
'@llamaindex/env':
specifier: workspace:*
version: link:../env
ajv:
specifier: ^8.12.0
version: 8.17.1
duck-duck-scrape:
specifier: ^2.2.5
version: 2.2.7
formdata-node:
specifier: ^6.0.3
version: 6.0.3
got:
specifier: ^14.4.1
version: 14.4.6
marked:
specifier: ^14.1.2
version: 14.1.4
papaparse:
specifier: ^5.4.1
version: 5.5.2
wikipedia:
specifier: ^2.1.2
version: 2.1.2
zod:
specifier: ^3.23.8
version: 3.24.2
devDependencies:
'@types/node':
specifier: ^22.9.0
version: 22.9.0
'@types/papaparse':
specifier: ^5.3.15
version: 5.3.15
bunchee:
specifier: 6.4.0
version: 6.4.0(typescript@5.7.3)
vitest:
specifier: ^2.1.5
version: 2.1.5(@edge-runtime/vm@4.0.4)(@types/node@22.9.0)(happy-dom@15.11.7)(lightningcss@1.29.1)(msw@2.7.0(@types/node@22.9.0)(typescript@5.7.3))(terser@5.38.2)
packages/wasm-tools:
dependencies:
'@assemblyscript/loader':
@@ -1928,10 +1996,26 @@ packages:
'@anthropic-ai/sdk@0.37.0':
resolution: {integrity: sha512-tHjX2YbkUBwEgg0JZU3EFSSAQPoK4qQR/NFYa8Vtzd5UAyXzZksCw2In69Rml4R/TyHPBfRYaLK35XiOe33pjw==}
'@apidevtools/json-schema-ref-parser@11.7.2':
resolution: {integrity: sha512-4gY54eEGEstClvEkGnwVkTkrx0sqwemEFG5OSRRn3tD91XH0+Q8XIkYIfo7IwEWPpJZwILb9GUXeShtplRc/eA==}
engines: {node: '>= 16'}
'@apidevtools/json-schema-ref-parser@11.9.3':
resolution: {integrity: sha512-60vepv88RwcJtSHrD6MjIL6Ta3SOYbgfnkHb+ppAVK+o9mXprRtulx7VlRl3lN3bbvysAfCS7WMVfhUYemB0IQ==}
engines: {node: '>= 16'}
'@apidevtools/openapi-schemas@2.1.0':
resolution: {integrity: sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==}
engines: {node: '>=10'}
'@apidevtools/swagger-methods@3.0.2':
resolution: {integrity: sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==}
'@apidevtools/swagger-parser@10.1.1':
resolution: {integrity: sha512-u/kozRnsPO/x8QtKYJOqoGtC4kH6yg1lfYkB9Au0WhYB0FNLpyFusttQtvhlwjtG3rOwiRz4D8DnnXa8iEpIKA==}
peerDependencies:
openapi-types: '>=7'
'@assemblyscript/loader@0.27.34':
resolution: {integrity: sha512-aUc12nBRN04dHn439RiI0G9X8CeOzbVuKspiVL1O8yS82neU3zTYjVFi9mz2+ZpSD2Laa7H0sUJ91jfhzQERMw==}
@@ -2234,6 +2318,9 @@ packages:
resolution: {integrity: sha512-eUuWapzEGWFEpHFxgEaBG8e3n6S8L3MSu0oda755rOfabWPnh0Our1AozNFVUxGFIhbKgd1ksprsoDGMinTOTA==}
engines: {node: '>=6.9.0'}
'@bufbuild/protobuf@2.2.3':
resolution: {integrity: sha512-tFQoXHJdkEOSwj5tRIZSPNUuXK3RaR7T1nUrPgbYX1pUbvqqaaZAsfo+NXBPsz5rZMSKVFrgK1WL8Q/MSLvprg==}
'@bundled-es-modules/cookie@2.0.1':
resolution: {integrity: sha512-8o+5fRPLNbjbdGRRmJj3h6Hh1AQJf2dk3qQ/5ZFb+PXkRNiSoMGGUKlsgLfrxneb72axVJyIYji64E2+nNfYyw==}
@@ -2415,6 +2502,17 @@ packages:
resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==}
engines: {node: '>=0.1.90'}
'@connectrpc/connect-web@2.0.0-rc.3':
resolution: {integrity: sha512-w88P8Lsn5CCsA7MFRl2e6oLY4J/5toiNtJns/YJrlyQaWOy3RO8pDgkz+iIkG98RPMhj2thuBvsd3Cn4DKKCkw==}
peerDependencies:
'@bufbuild/protobuf': ^2.2.0
'@connectrpc/connect': 2.0.0-rc.3
'@connectrpc/connect@2.0.0-rc.3':
resolution: {integrity: sha512-ARBt64yEyKbanyRETTjcjJuHr2YXorzQo0etyS5+P6oSeW8xEuzajA9g+zDnMcj1hlX2dQE93foIWQGfpru7gQ==}
peerDependencies:
'@bufbuild/protobuf': ^2.2.0
'@cspotcode/source-map-support@0.8.1':
resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
engines: {node: '>=12'}
@@ -2446,6 +2544,10 @@ packages:
resolution: {integrity: sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==}
engines: {node: '>=14.17.0'}
'@e2b/code-interpreter@1.0.4':
resolution: {integrity: sha512-8y82UMXBdf/hye8bX2Fn04JlL72rvOenVgsvMZ+cAJqo6Ijdl4EmzzuFpM4mz9s+EJ29+34lGHBp277tiLWuiA==}
engines: {node: '>=18'}
'@edge-runtime/primitives@5.1.1':
resolution: {integrity: sha512-osrHE4ObQ3XFkvd1sGBLkheV2mcHUqJI/Bum2AWA0R3U78h9lif3xZAdl6eLD/XnW4xhsdwjPUejLusXbjvI4Q==}
engines: {node: '>=16'}
@@ -4753,6 +4855,10 @@ packages:
resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==}
engines: {node: '>=14.16'}
'@sindresorhus/is@7.0.1':
resolution: {integrity: sha512-QWLl2P+rsCJeofkDNIT3WFmb6NrRud1SUYW8dIhXK/46XFV8Q/g7Bsvib0Askb0reRLe+WYPeeE+l5cH7SlkuQ==}
engines: {node: '>=18'}
'@sitespeed.io/tracium@0.3.3':
resolution: {integrity: sha512-dNZafjM93Y+F+sfwTO5gTpsGXlnc/0Q+c2+62ViqP3gkMWvHEMSKkaEHgVJLcLg3i/g19GSIPziiKpgyne07Bw==}
engines: {node: '>=8'}
@@ -5397,6 +5503,9 @@ packages:
'@types/node@22.9.0':
resolution: {integrity: sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==}
'@types/papaparse@5.3.15':
resolution: {integrity: sha512-JHe6vF6x/8Z85nCX4yFdDslN11d+1pr12E526X8WAfhadOeaOTx5AuIkvDKIBopfvlzpzkdMx4YyvSKCM9oqtw==}
'@types/pg@8.11.11':
resolution: {integrity: sha512-kGT1qKM8wJQ5qlawUrEkXgvMSXoV213KfMGXcwfDwUIfUHXqXYXOfS1nE1LINRJVVVx5wCm70XnFlMHaIcQAfw==}
@@ -5423,6 +5532,9 @@ packages:
'@types/resolve@1.20.2':
resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==}
'@types/retry@0.12.2':
resolution: {integrity: sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==}
'@types/statuses@2.0.5':
resolution: {integrity: sha512-jmIUGWrAiwu3dZpxntxieC+1n/5c3mjrImkmOSQ2NC5uP6cYO4aAZDdSmRcI5C1oiTmqlZGHC+/NmJrKogbP5A==}
@@ -6249,6 +6361,10 @@ packages:
resolution: {integrity: sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==}
engines: {node: '>=14.16'}
cacheable-request@12.0.1:
resolution: {integrity: sha512-Yo9wGIQUaAfIbk+qY0X4cDQgCosecfBe3V9NSyeY4qPC2SAkbCS4Xj79VP8WOzitpJUZKc/wsRCYF5ariDIwkg==}
engines: {node: '>=18'}
call-bind-apply-helpers@1.0.1:
resolution: {integrity: sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==}
engines: {node: '>= 0.4'}
@@ -6261,6 +6377,9 @@ packages:
resolution: {integrity: sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==}
engines: {node: '>= 0.4'}
call-me-maybe@1.0.2:
resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==}
callguard@2.0.0:
resolution: {integrity: sha512-I3nd+fuj20FK1qu00ImrbH+II+8ULS6ioYr9igqR1xyqySoqc3DiHEyUM0mkoAdKeLGg2CtGnO8R3VRQX5krpQ==}
@@ -6514,6 +6633,9 @@ packages:
commondir@1.0.1:
resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==}
compare-versions@6.1.1:
resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==}
compute-scroll-into-view@3.1.1:
resolution: {integrity: sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==}
@@ -6837,6 +6959,9 @@ packages:
resolution: {integrity: sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==}
engines: {node: '>=12'}
duck-duck-scrape@2.2.7:
resolution: {integrity: sha512-BEcglwnfx5puJl90KQfX+Q2q5vCguqyMpZcSRPBWk8OY55qWwV93+E+7DbIkrGDW4qkqPfUvtOUdi0lXz6lEMQ==}
duck@0.1.12:
resolution: {integrity: sha512-wkctla1O6VfP89gQ+J/yDesM0S7B7XLXjKGzXxMDVFg7uEn706niAtyYovKbyq1oT9YwDcly721/iUWoc8MVRg==}
@@ -6847,6 +6972,10 @@ packages:
duplexify@4.1.3:
resolution: {integrity: sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==}
e2b@1.0.7:
resolution: {integrity: sha512-7msagBbQ8tm51qaGp+hdaaaMjGG3zCzZtUS8bnz+LK7wdwtVTA1PmX+1Br9E3R7v6XIchnNWRpei+VjvGcfidA==}
engines: {node: '>=18'}
eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
@@ -7715,6 +7844,10 @@ packages:
resolution: {integrity: sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA==}
engines: {node: '>=16'}
got@14.4.6:
resolution: {integrity: sha512-rnhwfM/PhMNJ1i17k3DuDqgj0cKx3IHxBKVv/WX1uDKqrhi2Gv3l7rhPThR/Cc6uU++dD97W9c8Y0qyw9x0jag==}
engines: {node: '>=20'}
gpt-tokenizer@2.8.1:
resolution: {integrity: sha512-8+a9ojzqfgiF3TK4oivGYjlycD8g5igLt8NQw3ndOIgLVKSGJDhUDNAfYSbtyyuTkha3R/R9F8XrwC7/B5TKfQ==}
@@ -8135,6 +8268,10 @@ packages:
is-module@1.0.0:
resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==}
is-network-error@1.1.0:
resolution: {integrity: sha512-tUdRRAnhT+OtCZR/LxZelH/C7QtjtFrTu5tXCA8pl55eTUElUHT+GPYV8MBMBvea/j+NxQqVt3LbWMRir7Gx9g==}
engines: {node: '>=16'}
is-node-process@1.2.0:
resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==}
@@ -8718,6 +8855,11 @@ packages:
markdown-table@3.0.4:
resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==}
marked@14.1.4:
resolution: {integrity: sha512-vkVZ8ONmUdPnjCKc5uTRvmkRbx4EAi2OkTOXmfTDhZz3OFqMNBM1oTTWwTr4HY4uAEojhzPf+Fy8F1DWa3Sndg==}
engines: {node: '>= 18'}
hasBin: true
math-intrinsics@1.1.0:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'}
@@ -9322,6 +9464,11 @@ packages:
resolution: {integrity: sha512-VVw8O5KrfvwqAFeNZEgBbdgA+AQaBlHcXEootWU7TWDaFWFI0VLfzyKMsRjnfdS3cVCpWmI04xLJonCvEv11VQ==}
engines: {node: '>=0.4.10'}
needle@3.3.1:
resolution: {integrity: sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==}
engines: {node: '>= 4.4.x'}
hasBin: true
negotiator@1.0.0:
resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==}
engines: {node: '>= 0.6'}
@@ -9602,9 +9749,18 @@ packages:
zod:
optional: true
openapi-fetch@0.9.8:
resolution: {integrity: sha512-zM6elH0EZStD/gSiNlcPrzXcVQ/pZo3BDvC6CDwRDUt1dDzxlshpmQnpD6cZaJ39THaSmwVCxxRrPKNM1hHrDg==}
openapi-sampler@1.6.1:
resolution: {integrity: sha512-s1cIatOqrrhSj2tmJ4abFYZQK6l5v+V4toO5q1Pa0DyN8mtyqy2I+Qrj5W9vOELEtybIMQs/TBZGVO/DtTFK8w==}
openapi-types@12.1.3:
resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==}
openapi-typescript-helpers@0.0.8:
resolution: {integrity: sha512-1eNjQtbfNi5Z/kFhagDIaIRj6qqDzhjNJKz8cmMW0CVdGwT6e1GLbAfgI0d28VTJa1A8jz82jm/4dG8qNoNS8g==}
opener@1.5.2:
resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==}
hasBin: true
@@ -9649,6 +9805,10 @@ packages:
resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==}
engines: {node: '>=12.20'}
p-cancelable@4.0.1:
resolution: {integrity: sha512-wBowNApzd45EIKdO1LaU+LrMBwAcjfPaYtVzV3lmfM3gf8Z4CHZsiIqlM8TZZ8okYvh5A1cP6gTfCRQtwUpaUg==}
engines: {node: '>=14.16'}
p-filter@2.1.0:
resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==}
engines: {node: '>=8'}
@@ -9677,6 +9837,10 @@ packages:
resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==}
engines: {node: '>=6'}
p-retry@6.2.1:
resolution: {integrity: sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ==}
engines: {node: '>=16.17'}
p-try@2.2.0:
resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
engines: {node: '>=6'}
@@ -10527,6 +10691,10 @@ packages:
resolution: {integrity: sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==}
engines: {node: '>=14'}
retry@0.13.1:
resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==}
engines: {node: '>= 4'}
reusify@1.0.4:
resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
@@ -12233,12 +12401,33 @@ snapshots:
transitivePeerDependencies:
- encoding
'@apidevtools/json-schema-ref-parser@11.7.2':
dependencies:
'@jsdevtools/ono': 7.1.3
'@types/json-schema': 7.0.15
js-yaml: 4.1.0
'@apidevtools/json-schema-ref-parser@11.9.3':
dependencies:
'@jsdevtools/ono': 7.1.3
'@types/json-schema': 7.0.15
js-yaml: 4.1.0
'@apidevtools/openapi-schemas@2.1.0': {}
'@apidevtools/swagger-methods@3.0.2': {}
'@apidevtools/swagger-parser@10.1.1(openapi-types@12.1.3)':
dependencies:
'@apidevtools/json-schema-ref-parser': 11.7.2
'@apidevtools/openapi-schemas': 2.1.0
'@apidevtools/swagger-methods': 3.0.2
'@jsdevtools/ono': 7.1.3
ajv: 8.17.1
ajv-draft-04: 1.0.0(ajv@8.17.1)
call-me-maybe: 1.0.2
openapi-types: 12.1.3
'@assemblyscript/loader@0.27.34': {}
'@aws-crypto/crc32@3.0.0':
@@ -13031,6 +13220,8 @@ snapshots:
'@babel/helper-string-parser': 7.25.9
'@babel/helper-validator-identifier': 7.25.9
'@bufbuild/protobuf@2.2.3': {}
'@bundled-es-modules/cookie@2.0.1':
dependencies:
cookie: 0.7.2
@@ -13342,6 +13533,15 @@ snapshots:
'@colors/colors@1.6.0': {}
'@connectrpc/connect-web@2.0.0-rc.3(@bufbuild/protobuf@2.2.3)(@connectrpc/connect@2.0.0-rc.3(@bufbuild/protobuf@2.2.3))':
dependencies:
'@bufbuild/protobuf': 2.2.3
'@connectrpc/connect': 2.0.0-rc.3(@bufbuild/protobuf@2.2.3)
'@connectrpc/connect@2.0.0-rc.3(@bufbuild/protobuf@2.2.3)':
dependencies:
'@bufbuild/protobuf': 2.2.3
'@cspotcode/source-map-support@0.8.1':
dependencies:
'@jridgewell/trace-mapping': 0.3.9
@@ -13382,6 +13582,10 @@ snapshots:
'@discoveryjs/json-ext@0.6.3': {}
'@e2b/code-interpreter@1.0.4':
dependencies:
e2b: 1.0.7
'@edge-runtime/primitives@5.1.1': {}
'@edge-runtime/vm@4.0.4':
@@ -15632,6 +15836,8 @@ snapshots:
'@sindresorhus/is@5.6.0': {}
'@sindresorhus/is@7.0.1': {}
'@sitespeed.io/tracium@0.3.3':
dependencies:
debug: 4.4.0
@@ -16377,6 +16583,10 @@ snapshots:
dependencies:
undici-types: 6.19.8
'@types/papaparse@5.3.15':
dependencies:
'@types/node': 22.9.0
'@types/pg@8.11.11':
dependencies:
'@types/node': 22.9.0
@@ -16413,6 +16623,8 @@ snapshots:
'@types/resolve@1.20.2': {}
'@types/retry@0.12.2': {}
'@types/statuses@2.0.5': {}
'@types/tough-cookie@4.0.5': {}
@@ -16438,10 +16650,10 @@ snapshots:
'@types/node': 22.9.0
optional: true
'@typescript-eslint/eslint-plugin@8.24.0(@typescript-eslint/parser@8.24.0(eslint@9.16.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.16.0(jiti@2.4.2))(typescript@5.7.3)':
'@typescript-eslint/eslint-plugin@8.24.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.16.0(jiti@2.4.2))(typescript@5.7.3)':
dependencies:
'@eslint-community/regexpp': 4.12.1
'@typescript-eslint/parser': 8.24.0(eslint@9.16.0(jiti@2.4.2))(typescript@5.7.3)
'@typescript-eslint/parser': 8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3)
'@typescript-eslint/scope-manager': 8.24.0
'@typescript-eslint/type-utils': 8.24.0(eslint@9.16.0(jiti@2.4.2))(typescript@5.7.3)
'@typescript-eslint/utils': 8.24.0(eslint@9.16.0(jiti@2.4.2))(typescript@5.7.3)
@@ -17466,6 +17678,16 @@ snapshots:
normalize-url: 8.0.1
responselike: 3.0.0
cacheable-request@12.0.1:
dependencies:
'@types/http-cache-semantics': 4.0.4
get-stream: 9.0.1
http-cache-semantics: 4.1.1
keyv: 4.5.4
mimic-response: 4.0.0
normalize-url: 8.0.1
responselike: 3.0.0
call-bind-apply-helpers@1.0.1:
dependencies:
es-errors: 1.3.0
@@ -17483,6 +17705,8 @@ snapshots:
call-bind-apply-helpers: 1.0.1
get-intrinsic: 1.2.7
call-me-maybe@1.0.2: {}
callguard@2.0.0: {}
callsites@3.1.0: {}
@@ -17740,6 +17964,8 @@ snapshots:
commondir@1.0.1: {}
compare-versions@6.1.1: {}
compute-scroll-into-view@3.1.1: {}
concat-map@0.0.1: {}
@@ -18028,6 +18254,11 @@ snapshots:
dotenv@16.4.7: {}
duck-duck-scrape@2.2.7:
dependencies:
html-entities: 2.5.2
needle: 3.3.1
duck@0.1.12:
dependencies:
underscore: 1.13.7
@@ -18045,6 +18276,15 @@ snapshots:
readable-stream: 3.6.2
stream-shift: 1.0.3
e2b@1.0.7:
dependencies:
'@bufbuild/protobuf': 2.2.3
'@connectrpc/connect': 2.0.0-rc.3(@bufbuild/protobuf@2.2.3)
'@connectrpc/connect-web': 2.0.0-rc.3(@bufbuild/protobuf@2.2.3)(@connectrpc/connect@2.0.0-rc.3(@bufbuild/protobuf@2.2.3))
compare-versions: 6.1.1
openapi-fetch: 0.9.8
platform: 1.3.6
eastasianwidth@0.2.0: {}
ecdsa-sig-formatter@1.0.11:
@@ -18331,12 +18571,12 @@ snapshots:
dependencies:
'@next/eslint-plugin-next': 15.1.0
'@rushstack/eslint-patch': 1.10.5
'@typescript-eslint/eslint-plugin': 8.24.0(@typescript-eslint/parser@8.24.0(eslint@9.16.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.16.0(jiti@2.4.2))(typescript@5.7.3)
'@typescript-eslint/eslint-plugin': 8.24.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.16.0(jiti@2.4.2))(typescript@5.7.3)
'@typescript-eslint/parser': 8.24.0(eslint@9.16.0(jiti@2.4.2))(typescript@5.7.3)
eslint: 9.16.0(jiti@2.4.2)
eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0)(eslint@9.16.0(jiti@2.4.2))
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.16.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.16.0(jiti@2.4.2))
eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.22.0(jiti@2.4.2)))(eslint@9.16.0(jiti@2.4.2))
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.22.0(jiti@2.4.2)))(eslint@9.16.0(jiti@2.4.2)))(eslint@9.16.0(jiti@2.4.2))
eslint-plugin-jsx-a11y: 6.10.2(eslint@9.16.0(jiti@2.4.2))
eslint-plugin-react: 7.37.2(eslint@9.16.0(jiti@2.4.2))
eslint-plugin-react-hooks: 5.1.0(eslint@9.16.0(jiti@2.4.2))
@@ -18356,7 +18596,7 @@ snapshots:
eslint: 9.22.0(jiti@2.4.2)
eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0)(eslint@9.22.0(jiti@2.4.2))
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-typescript@3.7.0)(eslint@9.22.0(jiti@2.4.2))
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.22.0(jiti@2.4.2))
eslint-plugin-jsx-a11y: 6.10.2(eslint@9.22.0(jiti@2.4.2))
eslint-plugin-react: 7.37.2(eslint@9.22.0(jiti@2.4.2))
eslint-plugin-react-hooks: 5.1.0(eslint@9.22.0(jiti@2.4.2))
@@ -18385,7 +18625,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0)(eslint@9.16.0(jiti@2.4.2)):
eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.22.0(jiti@2.4.2)))(eslint@9.16.0(jiti@2.4.2)):
dependencies:
'@nolyfill/is-core-module': 1.0.39
debug: 4.4.0
@@ -18397,7 +18637,7 @@ snapshots:
is-glob: 4.0.3
stable-hash: 0.0.4
optionalDependencies:
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.16.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.16.0(jiti@2.4.2))
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.22.0(jiti@2.4.2))
transitivePeerDependencies:
- supports-color
@@ -18413,18 +18653,18 @@ snapshots:
is-glob: 4.0.3
stable-hash: 0.0.4
optionalDependencies:
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-typescript@3.7.0)(eslint@9.22.0(jiti@2.4.2))
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.22.0(jiti@2.4.2))
transitivePeerDependencies:
- supports-color
eslint-module-utils@2.12.0(@typescript-eslint/parser@8.24.0(eslint@9.16.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@9.16.0(jiti@2.4.2)):
eslint-module-utils@2.12.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.22.0(jiti@2.4.2)))(eslint@9.16.0(jiti@2.4.2)))(eslint@9.16.0(jiti@2.4.2)):
dependencies:
debug: 3.2.7
optionalDependencies:
'@typescript-eslint/parser': 8.24.0(eslint@9.16.0(jiti@2.4.2))(typescript@5.7.3)
'@typescript-eslint/parser': 8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3)
eslint: 9.16.0(jiti@2.4.2)
eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0)(eslint@9.16.0(jiti@2.4.2))
eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.22.0(jiti@2.4.2)))(eslint@9.16.0(jiti@2.4.2))
transitivePeerDependencies:
- supports-color
@@ -18439,7 +18679,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.16.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.16.0(jiti@2.4.2)):
eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.22.0(jiti@2.4.2)))(eslint@9.16.0(jiti@2.4.2)))(eslint@9.16.0(jiti@2.4.2)):
dependencies:
'@rtsao/scc': 1.1.0
array-includes: 3.1.8
@@ -18450,7 +18690,7 @@ snapshots:
doctrine: 2.1.0
eslint: 9.16.0(jiti@2.4.2)
eslint-import-resolver-node: 0.3.9
eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.24.0(eslint@9.16.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@9.16.0(jiti@2.4.2))
eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.22.0(jiti@2.4.2)))(eslint@9.16.0(jiti@2.4.2)))(eslint@9.16.0(jiti@2.4.2))
hasown: 2.0.2
is-core-module: 2.16.1
is-glob: 4.0.3
@@ -18462,13 +18702,13 @@ snapshots:
string.prototype.trimend: 1.0.9
tsconfig-paths: 3.15.0
optionalDependencies:
'@typescript-eslint/parser': 8.24.0(eslint@9.16.0(jiti@2.4.2))(typescript@5.7.3)
'@typescript-eslint/parser': 8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3)
transitivePeerDependencies:
- eslint-import-resolver-typescript
- eslint-import-resolver-webpack
- supports-color
eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-typescript@3.7.0)(eslint@9.22.0(jiti@2.4.2)):
eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.22.0(jiti@2.4.2)):
dependencies:
'@rtsao/scc': 1.1.0
array-includes: 3.1.8
@@ -19478,6 +19718,20 @@ snapshots:
p-cancelable: 3.0.0
responselike: 3.0.0
got@14.4.6:
dependencies:
'@sindresorhus/is': 7.0.1
'@szmarczak/http-timer': 5.0.1
cacheable-lookup: 7.0.0
cacheable-request: 12.0.1
decompress-response: 6.0.0
form-data-encoder: 4.0.2
http2-wrapper: 2.2.1
lowercase-keys: 3.0.0
p-cancelable: 4.0.1
responselike: 3.0.0
type-fest: 4.34.1
gpt-tokenizer@2.8.1: {}
graceful-fs@4.2.11: {}
@@ -20070,6 +20324,8 @@ snapshots:
is-module@1.0.0: {}
is-network-error@1.1.0: {}
is-node-process@1.2.0: {}
is-number-object@1.1.1:
@@ -20639,6 +20895,8 @@ snapshots:
markdown-table@3.0.4: {}
marked@14.1.4: {}
math-intrinsics@1.1.0: {}
md-utils-ts@2.0.0: {}
@@ -21728,6 +21986,11 @@ snapshots:
- socks
- supports-color
needle@3.3.1:
dependencies:
iconv-lite: 0.6.3
sax: 1.4.1
negotiator@1.0.0: {}
neo-async@2.6.2: {}
@@ -22042,12 +22305,20 @@ snapshots:
transitivePeerDependencies:
- encoding
openapi-fetch@0.9.8:
dependencies:
openapi-typescript-helpers: 0.0.8
openapi-sampler@1.6.1:
dependencies:
'@types/json-schema': 7.0.15
fast-xml-parser: 4.5.3
json-pointer: 0.6.2
openapi-types@12.1.3: {}
openapi-typescript-helpers@0.0.8: {}
opener@1.5.2: {}
option@0.2.4: {}
@@ -22119,6 +22390,8 @@ snapshots:
p-cancelable@3.0.0: {}
p-cancelable@4.0.1: {}
p-filter@2.1.0:
dependencies:
p-map: 2.1.0
@@ -22145,6 +22418,12 @@ snapshots:
p-map@2.1.0: {}
p-retry@6.2.1:
dependencies:
'@types/retry': 0.12.2
is-network-error: 1.1.0
retry: 0.13.1
p-try@2.2.0: {}
pac-proxy-agent@7.1.0:
@@ -23143,6 +23422,8 @@ snapshots:
- encoding
- supports-color
retry@0.13.1: {}
reusify@1.0.4: {}
rfdc@1.4.1: {}
+3
View File
@@ -190,6 +190,9 @@
},
{
"path": "./packages/providers/perplexity/tsconfig.json"
},
{
"path": "./packages/tools/tsconfig.json"
}
]
}