mirror of
https://github.com/run-llama/LlamaIndexTS.git
synced 2026-07-16 07:14:29 -04:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d99b1d61d7 | |||
| 844029d8e5 | |||
| 9492cc64b5 | |||
| 5773f97e88 | |||
| 0784dc3a0a | |||
| 7993be7d0d | |||
| f22ce6e757 | |||
| 3c4347b247 | |||
| 977f2840b9 | |||
| 5d3bb6642e | |||
| 2001eb7ffb | |||
| f18c9f69d4 | |||
| 8e124e5b63 | |||
| 4ed5e544b0 | |||
| b185bda5b1 | |||
| d79804e271 | |||
| 2b356c8613 | |||
| 2e6b36ef4b | |||
| edd0f66234 | |||
| 2da407d66c | |||
| fa574f709e | |||
| 1e6171521b |
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"create-llama": patch
|
||||
---
|
||||
|
||||
Added option to automatically install dependencies (for Python and TS)
|
||||
@@ -8,6 +8,9 @@ on:
|
||||
- ".github/workflows/e2e.yml"
|
||||
branches: [main]
|
||||
|
||||
env:
|
||||
POETRY_VERSION: "1.6.1"
|
||||
|
||||
jobs:
|
||||
e2e:
|
||||
name: create-llama
|
||||
@@ -16,10 +19,19 @@ jobs:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
node-version: [18, 20]
|
||||
python-version: ["3.11"]
|
||||
os: [macos-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install Poetry
|
||||
uses: snok/install-poetry@v1
|
||||
with:
|
||||
version: ${{ env.POETRY_VERSION }}
|
||||
- uses: pnpm/action-setup@v2
|
||||
- name: Setup Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
|
||||
@@ -18,3 +18,21 @@ jobs:
|
||||
run: pnpm install
|
||||
- name: Run tests
|
||||
run: pnpm run test
|
||||
typecheck:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v2
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: "pnpm"
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
- name: Build
|
||||
run: pnpm run build
|
||||
working-directory: ./packages/core
|
||||
- name: Run Type Check
|
||||
run: pnpm run type-check
|
||||
|
||||
@@ -37,6 +37,7 @@ yarn-error.log*
|
||||
.vercel
|
||||
|
||||
dist/
|
||||
lib/
|
||||
|
||||
# vs code
|
||||
.vscode/launch.json
|
||||
|
||||
+2
-1
@@ -1,3 +1,4 @@
|
||||
apps/docs/i18n
|
||||
pnpm-lock.yaml
|
||||
|
||||
lib/
|
||||
dist/
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
# LlamaIndex.TS
|
||||
|
||||
[](https://www.npmjs.com/package/llamaindex)
|
||||
[](https://www.npmjs.com/package/llamaindex)
|
||||
[](https://www.npmjs.com/package/llamaindex)
|
||||
[](https://discord.com/invite/eN6D2HQ4aX)
|
||||
|
||||
LlamaIndex is a data framework for your LLM application.
|
||||
|
||||
Use your own data with large language models (LLMs, OpenAI ChatGPT and others) in Typescript and Javascript.
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
# Generated files
|
||||
.docusaurus
|
||||
.cache-loader
|
||||
lib
|
||||
|
||||
# Misc
|
||||
.DS_Store
|
||||
|
||||
@@ -11,7 +11,16 @@ const retriever = index.asRetriever();
|
||||
const chatEngine = new ContextChatEngine({ retriever });
|
||||
|
||||
// start chatting
|
||||
const response = await chatEngine.chat(query);
|
||||
const response = await chatEngine.chat({ message: query });
|
||||
```
|
||||
|
||||
The `chat` function also supports streaming, just add `stream: true` as an option:
|
||||
|
||||
```typescript
|
||||
const stream = await chatEngine.chat({ message: query, stream: true });
|
||||
for await (const chunk of stream) {
|
||||
process.stdout.write(chunk.response);
|
||||
}
|
||||
```
|
||||
|
||||
## Api References
|
||||
|
||||
@@ -8,7 +8,16 @@ A query engine wraps a `Retriever` and a `ResponseSynthesizer` into a pipeline,
|
||||
|
||||
```typescript
|
||||
const queryEngine = index.asQueryEngine();
|
||||
const response = await queryEngine.query("query string");
|
||||
const response = await queryEngine.query({ query: "query string" });
|
||||
```
|
||||
|
||||
The `query` function also supports streaming, just add `stream: true` as an option:
|
||||
|
||||
```typescript
|
||||
const stream = await queryEngine.query({ query: "query string", stream: true });
|
||||
for await (const chunk of stream) {
|
||||
process.stdout.write(chunk.response);
|
||||
}
|
||||
```
|
||||
|
||||
## Sub Question Query Engine
|
||||
|
||||
@@ -35,13 +35,26 @@ const nodesWithScore: NodeWithScore[] = [
|
||||
},
|
||||
];
|
||||
|
||||
const response = await responseSynthesizer.synthesize(
|
||||
"What age am I?",
|
||||
const response = await responseSynthesizer.synthesize({
|
||||
query: "What age am I?",
|
||||
nodesWithScore,
|
||||
);
|
||||
});
|
||||
console.log(response.response);
|
||||
```
|
||||
|
||||
The `synthesize` function also supports streaming, just add `stream: true` as an option:
|
||||
|
||||
```typescript
|
||||
const stream = await responseSynthesizer.synthesize({
|
||||
query: "What age am I?",
|
||||
nodesWithScore,
|
||||
stream: true,
|
||||
});
|
||||
for await (const chunk of stream) {
|
||||
process.stdout.write(chunk.response);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- [ResponseSynthesizer](../../api/classes/ResponseSynthesizer.md)
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
label: Observability
|
||||
@@ -0,0 +1,35 @@
|
||||
# Observability
|
||||
|
||||
LlamaIndex provides **one-click observability** 🔭 to allow you to build principled LLM applications in a production setting.
|
||||
|
||||
A key requirement for principled development of LLM applications over your data (RAG systems, agents) is being able to observe, debug, and evaluate
|
||||
your system - both as a whole and for each component.
|
||||
|
||||
This feature allows you to seamlessly integrate the LlamaIndex library with powerful observability/evaluation tools offered by our partners.
|
||||
Configure a variable once, and you'll be able to do things like the following:
|
||||
|
||||
- View LLM/prompt inputs/outputs
|
||||
- Ensure that the outputs of any component (LLMs, embeddings) are performing as expected
|
||||
- View call traces for both indexing and querying
|
||||
|
||||
Each provider has similarities and differences. Take a look below for the full set of guides for each one!
|
||||
|
||||
## OpenLLMetry
|
||||
|
||||
[OpenLLMetry](https://github.com/traceloop/openllmetry-js) is an open-source project based on OpenTelemetry for tracing and monitoring
|
||||
LLM applications. It connects to [all major observability platforms](https://www.traceloop.com/docs/openllmetry/integrations/introduction) and installs in minutes.
|
||||
|
||||
### Usage Pattern
|
||||
|
||||
```bash
|
||||
npm install @traceloop/node-server-sdk
|
||||
```
|
||||
|
||||
```js
|
||||
import * as traceloop from "@traceloop/node-server-sdk";
|
||||
|
||||
traceloop.initialize({
|
||||
apiKey: process.env.TRACELOOP_API_KEY,
|
||||
disableBatch: true,
|
||||
});
|
||||
```
|
||||
@@ -28,8 +28,10 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/module-type-aliases": "2.4.3",
|
||||
"@docusaurus/theme-classic": "^2.4.3",
|
||||
"@docusaurus/types": "^2.4.3",
|
||||
"@tsconfig/docusaurus": "^2.0.1",
|
||||
"@types/node": "^18.19.6",
|
||||
"docusaurus-plugin-typedoc": "^0.19.2",
|
||||
"typedoc": "^0.24.8",
|
||||
"typedoc-plugin-markdown": "^3.16.0",
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
{
|
||||
// This file is not used in compilation. It is here just for a nice editor experience.
|
||||
"extends": "@tsconfig/docusaurus/tsconfig.json",
|
||||
"extends": "./node_modules/@tsconfig/docusaurus/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": "."
|
||||
"baseUrl": ".",
|
||||
"composite": true,
|
||||
"incremental": true,
|
||||
"outDir": "./lib",
|
||||
"tsBuildInfoFile": "./lib/.tsbuildinfo"
|
||||
}
|
||||
}
|
||||
|
||||
+10
-8
@@ -2,13 +2,15 @@ import { Anthropic } from "llamaindex";
|
||||
|
||||
(async () => {
|
||||
const anthropic = new Anthropic();
|
||||
const result = await anthropic.chat([
|
||||
{ content: "You want to talk in rhymes.", role: "system" },
|
||||
{
|
||||
content:
|
||||
"How much wood would a woodchuck chuck if a woodchuck could chuck wood?",
|
||||
role: "user",
|
||||
},
|
||||
]);
|
||||
const result = await anthropic.chat({
|
||||
messages: [
|
||||
{ content: "You want to talk in rhymes.", role: "system" },
|
||||
{
|
||||
content:
|
||||
"How much wood would a woodchuck chuck if a woodchuck could chuck wood?",
|
||||
role: "user",
|
||||
},
|
||||
],
|
||||
});
|
||||
console.log(result);
|
||||
})();
|
||||
|
||||
@@ -18,7 +18,9 @@ async function main() {
|
||||
|
||||
const queryEngine = await index.asQueryEngine({ retriever });
|
||||
|
||||
const results = await queryEngine.query("What is the best reviewed movie?");
|
||||
const results = await queryEngine.query({
|
||||
query: "What is the best reviewed movie?",
|
||||
});
|
||||
|
||||
console.log(results.response);
|
||||
} catch (e) {
|
||||
|
||||
@@ -25,8 +25,11 @@ async function main() {
|
||||
|
||||
while (true) {
|
||||
const query = await rl.question("Query: ");
|
||||
const response = await chatEngine.chat(query);
|
||||
console.log(response.toString());
|
||||
const stream = await chatEngine.chat({ message: query, stream: true });
|
||||
console.log();
|
||||
for await (const chunk of stream) {
|
||||
process.stdout.write(chunk.response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,9 +28,9 @@ async function main() {
|
||||
|
||||
console.log("Querying index");
|
||||
const queryEngine = index.asQueryEngine();
|
||||
const response = await queryEngine.query(
|
||||
"Tell me about Godfrey Cheshire's rating of La Sapienza.",
|
||||
);
|
||||
const response = await queryEngine.query({
|
||||
query: "Tell me about Godfrey Cheshire's rating of La Sapienza.",
|
||||
});
|
||||
console.log(response.toString());
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
|
||||
@@ -25,12 +25,12 @@ import { ChatMessage, LlamaDeuce, OpenAI } from "llamaindex";
|
||||
|
||||
while (true) {
|
||||
const next = history.length % 2 === 1 ? gpt4 : l2;
|
||||
const r = await next.chat(
|
||||
history.map(({ content, role }) => ({
|
||||
const r = await next.chat({
|
||||
messages: history.map(({ content, role }) => ({
|
||||
content,
|
||||
role: next === l2 ? role : role === "user" ? "assistant" : "user",
|
||||
})),
|
||||
);
|
||||
});
|
||||
history.push({
|
||||
content: r.message.content,
|
||||
role: next === l2 ? "assistant" : "user",
|
||||
|
||||
@@ -32,12 +32,15 @@ async function main() {
|
||||
|
||||
// Query the index
|
||||
const queryEngine = index.asQueryEngine();
|
||||
const response = await queryEngine.query(
|
||||
"What did the author do in college?",
|
||||
);
|
||||
const stream = await queryEngine.query({
|
||||
query: "What did the author do in college?",
|
||||
stream: true,
|
||||
});
|
||||
|
||||
// Output response
|
||||
console.log(response.toString());
|
||||
for await (const chunk of stream) {
|
||||
process.stdout.write(chunk.response);
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
|
||||
+14
-12
@@ -21,18 +21,20 @@ async function main() {
|
||||
action_items: ["action item 1", "action item 2"],
|
||||
};
|
||||
|
||||
const response = await llm.chat([
|
||||
{
|
||||
role: "system",
|
||||
content: `You are an expert assistant for summarizing and extracting insights from sales call transcripts.\n\nGenerate a valid JSON in the following format:\n\n${JSON.stringify(
|
||||
example,
|
||||
)}`,
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
content: `Here is the transcript: \n------\n${transcript}\n------`,
|
||||
},
|
||||
]);
|
||||
const response = await llm.chat({
|
||||
messages: [
|
||||
{
|
||||
role: "system",
|
||||
content: `You are an expert assistant for summarizing and extracting insights from sales call transcripts.\n\nGenerate a valid JSON in the following format:\n\n${JSON.stringify(
|
||||
example,
|
||||
)}`,
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
content: `Here is the transcript: \n------\n${transcript}\n------`,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const json = JSON.parse(response.message.content);
|
||||
|
||||
|
||||
@@ -20,9 +20,9 @@ async function main() {
|
||||
mode,
|
||||
}),
|
||||
});
|
||||
const response = await queryEngine.query(
|
||||
"What did the author do growing up?",
|
||||
);
|
||||
const response = await queryEngine.query({
|
||||
query: "What did the author do growing up?",
|
||||
});
|
||||
console.log(response.toString());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ import { DeuceChatStrategy, LlamaDeuce } from "llamaindex";
|
||||
|
||||
(async () => {
|
||||
const deuce = new LlamaDeuce({ chatStrategy: DeuceChatStrategy.META });
|
||||
const result = await deuce.chat([{ content: "Hello, world!", role: "user" }]);
|
||||
const result = await deuce.chat({
|
||||
messages: [{ content: "Hello, world!", role: "user" }],
|
||||
});
|
||||
console.log(result);
|
||||
})();
|
||||
|
||||
@@ -27,9 +27,12 @@ import {
|
||||
},
|
||||
];
|
||||
|
||||
const response = await responseSynthesizer.synthesize(
|
||||
"What age am I?",
|
||||
const stream = await responseSynthesizer.synthesize({
|
||||
query: "What age am I?",
|
||||
nodesWithScore,
|
||||
);
|
||||
console.log(response.response);
|
||||
stream: true,
|
||||
});
|
||||
for await (const chunk of stream) {
|
||||
process.stdout.write(chunk.response);
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -11,7 +11,9 @@ async function main() {
|
||||
// Query the index
|
||||
const queryEngine = index.asQueryEngine();
|
||||
|
||||
const response = await queryEngine.query("What does the example code do?");
|
||||
const response = await queryEngine.query({
|
||||
query: "What does the example code do?",
|
||||
});
|
||||
|
||||
// Output response
|
||||
console.log(response.toString());
|
||||
|
||||
+11
-10
@@ -27,7 +27,7 @@ async function rag(llm: LLM, embedModel: BaseEmbedding, query: string) {
|
||||
|
||||
// Query the index
|
||||
const queryEngine = index.asQueryEngine();
|
||||
const response = await queryEngine.query(query);
|
||||
const response = await queryEngine.query({ query });
|
||||
return response.response;
|
||||
}
|
||||
|
||||
@@ -43,19 +43,20 @@ async function rag(llm: LLM, embedModel: BaseEmbedding, query: string) {
|
||||
|
||||
// chat api (non-streaming)
|
||||
const llm = new MistralAI({ model: "mistral-tiny" });
|
||||
const response = await llm.chat([
|
||||
{ content: "What is the best French cheese?", role: "user" },
|
||||
]);
|
||||
const response = await llm.chat({
|
||||
messages: [{ content: "What is the best French cheese?", role: "user" }],
|
||||
});
|
||||
console.log(response.message.content);
|
||||
|
||||
// chat api (streaming)
|
||||
const stream = await llm.chat(
|
||||
[{ content: "Who is the most renowned French painter?", role: "user" }],
|
||||
undefined,
|
||||
true,
|
||||
);
|
||||
const stream = await llm.chat({
|
||||
messages: [
|
||||
{ content: "Who is the most renowned French painter?", role: "user" },
|
||||
],
|
||||
stream: true,
|
||||
});
|
||||
for await (const chunk of stream) {
|
||||
process.stdout.write(chunk);
|
||||
process.stdout.write(chunk.delta);
|
||||
}
|
||||
|
||||
// rag
|
||||
|
||||
+1
-1
@@ -54,7 +54,7 @@ async function main() {
|
||||
break;
|
||||
}
|
||||
|
||||
const response = await queryEngine.query(query);
|
||||
const response = await queryEngine.query({ query });
|
||||
|
||||
// Output response
|
||||
console.log(response.toString());
|
||||
|
||||
@@ -24,9 +24,9 @@ async function query() {
|
||||
|
||||
const retriever = index.asRetriever({ similarityTopK: 20 });
|
||||
const queryEngine = index.asQueryEngine({ retriever });
|
||||
const result = await queryEngine.query(
|
||||
"What does the author think of web frameworks?",
|
||||
);
|
||||
const result = await queryEngine.query({
|
||||
query: "What does the author think of web frameworks?",
|
||||
});
|
||||
console.log(result.response);
|
||||
await client.close();
|
||||
}
|
||||
|
||||
@@ -46,9 +46,9 @@ async function main() {
|
||||
responseSynthesizer: new MultiModalResponseSynthesizer({ serviceContext }),
|
||||
retriever: index.asRetriever({ similarityTopK: 3, imageSimilarityTopK: 1 }),
|
||||
});
|
||||
const result = await queryEngine.query(
|
||||
"Tell me more about Vincent van Gogh's famous paintings",
|
||||
);
|
||||
const result = await queryEngine.query({
|
||||
query: "Tell me more about Vincent van Gogh's famous paintings",
|
||||
});
|
||||
console.log(result.response, "\n");
|
||||
images.forEach((image) =>
|
||||
console.log(`Image retrieved and used in inference: ${image.toString()}`),
|
||||
|
||||
+15
-13
@@ -3,32 +3,34 @@ import { Ollama } from "llamaindex";
|
||||
(async () => {
|
||||
const llm = new Ollama({ model: "llama2", temperature: 0.75 });
|
||||
{
|
||||
const response = await llm.chat([
|
||||
{ content: "Tell me a joke.", role: "user" },
|
||||
]);
|
||||
const response = await llm.chat({
|
||||
messages: [{ content: "Tell me a joke.", role: "user" }],
|
||||
});
|
||||
console.log("Response 1:", response.message.content);
|
||||
}
|
||||
{
|
||||
const response = await llm.complete("How are you?");
|
||||
console.log("Response 2:", response.message.content);
|
||||
const response = await llm.complete({ prompt: "How are you?" });
|
||||
console.log("Response 2:", response.text);
|
||||
}
|
||||
{
|
||||
const response = await llm.chat(
|
||||
[{ content: "Tell me a joke.", role: "user" }],
|
||||
undefined,
|
||||
true,
|
||||
);
|
||||
const response = await llm.chat({
|
||||
messages: [{ content: "Tell me a joke.", role: "user" }],
|
||||
stream: true,
|
||||
});
|
||||
console.log("Response 3:");
|
||||
for await (const message of response) {
|
||||
process.stdout.write(message); // no newline
|
||||
process.stdout.write(message.delta); // no newline
|
||||
}
|
||||
console.log(); // newline
|
||||
}
|
||||
{
|
||||
const response = await llm.complete("How are you?", undefined, true);
|
||||
const response = await llm.complete({
|
||||
prompt: "How are you?",
|
||||
stream: true,
|
||||
});
|
||||
console.log("Response 4:");
|
||||
for await (const message of response) {
|
||||
process.stdout.write(message); // no newline
|
||||
process.stdout.write(message.text); // no newline
|
||||
}
|
||||
console.log(); // newline
|
||||
}
|
||||
|
||||
+5
-5
@@ -4,12 +4,12 @@ import { OpenAI } from "llamaindex";
|
||||
const llm = new OpenAI({ model: "gpt-4-1106-preview", temperature: 0.1 });
|
||||
|
||||
// complete api
|
||||
const response1 = await llm.complete("How are you?");
|
||||
console.log(response1.message.content);
|
||||
const response1 = await llm.complete({ prompt: "How are you?" });
|
||||
console.log(response1.text);
|
||||
|
||||
// chat api
|
||||
const response2 = await llm.chat([
|
||||
{ content: "Tell me a joke.", role: "user" },
|
||||
]);
|
||||
const response2 = await llm.chat({
|
||||
messages: [{ content: "Tell me a joke.", role: "user" }],
|
||||
});
|
||||
console.log(response2.message.content);
|
||||
})();
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"name": "examples",
|
||||
"dependencies": {
|
||||
"@datastax/astra-db-ts": "^0.1.2",
|
||||
"@notionhq/client": "^2.2.13",
|
||||
"@notionhq/client": "^2.2.14",
|
||||
"@pinecone-database/pinecone": "^1.1.2",
|
||||
"chromadb": "^1.7.3",
|
||||
"commander": "^11.1.0",
|
||||
|
||||
@@ -31,7 +31,7 @@ async function main() {
|
||||
}
|
||||
|
||||
try {
|
||||
const answer = await queryEngine.query(question);
|
||||
const answer = await queryEngine.query({ query: question });
|
||||
console.log(answer.response);
|
||||
} catch (error) {
|
||||
console.error("Error:", error);
|
||||
|
||||
@@ -29,7 +29,7 @@ async function main() {
|
||||
}
|
||||
|
||||
try {
|
||||
const answer = await queryEngine.query(question);
|
||||
const answer = await queryEngine.query({ query: question });
|
||||
console.log(answer.response);
|
||||
} catch (error) {
|
||||
console.error("Error:", error);
|
||||
|
||||
+8
-6
@@ -1,7 +1,6 @@
|
||||
import { Portkey } from "llamaindex";
|
||||
|
||||
(async () => {
|
||||
const llms = [{}];
|
||||
const portkey = new Portkey({
|
||||
mode: "single",
|
||||
llms: [
|
||||
@@ -13,11 +12,14 @@ import { Portkey } from "llamaindex";
|
||||
},
|
||||
],
|
||||
});
|
||||
const result = portkey.stream_chat([
|
||||
{ role: "system", content: "You are a helpful assistant." },
|
||||
{ role: "user", content: "Tell me a joke." },
|
||||
]);
|
||||
const result = await portkey.chat({
|
||||
messages: [
|
||||
{ role: "system", content: "You are a helpful assistant." },
|
||||
{ role: "user", content: "Tell me a joke." },
|
||||
],
|
||||
stream: true,
|
||||
});
|
||||
for await (const res of result) {
|
||||
process.stdout.write(res);
|
||||
process.stdout.write(res.delta);
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -48,7 +48,7 @@ program
|
||||
break;
|
||||
}
|
||||
|
||||
const response = await queryEngine.query(query);
|
||||
const response = await queryEngine.query({ query });
|
||||
|
||||
console.log(response.toString());
|
||||
}
|
||||
|
||||
@@ -38,9 +38,9 @@ Given the CSV file, generate me Typescript code to answer the question: ${query}
|
||||
const queryEngine = index.asQueryEngine({ responseSynthesizer });
|
||||
|
||||
// Query the index
|
||||
const response = await queryEngine.query(
|
||||
"What is the correlation between survival and age?",
|
||||
);
|
||||
const response = await queryEngine.query({
|
||||
query: "What is the correlation between survival and age?",
|
||||
});
|
||||
|
||||
// Output response
|
||||
console.log(response.toString());
|
||||
|
||||
@@ -15,7 +15,7 @@ async function main() {
|
||||
|
||||
// Test query
|
||||
const queryEngine = index.asQueryEngine();
|
||||
const response = await queryEngine.query(SAMPLE_QUERY);
|
||||
const response = await queryEngine.query({ query: SAMPLE_QUERY });
|
||||
console.log(`Test query > ${SAMPLE_QUERY}:\n`, response.toString());
|
||||
}
|
||||
|
||||
|
||||
@@ -10,9 +10,9 @@ async function main() {
|
||||
|
||||
// Query the index
|
||||
const queryEngine = index.asQueryEngine();
|
||||
const response = await queryEngine.query(
|
||||
"What were the notable changes in 18.1?",
|
||||
);
|
||||
const response = await queryEngine.query({
|
||||
query: "What were the notable changes in 18.1?",
|
||||
});
|
||||
|
||||
// Output response
|
||||
console.log(response.toString());
|
||||
|
||||
@@ -15,7 +15,7 @@ async function main() {
|
||||
|
||||
// Test query
|
||||
const queryEngine = index.asQueryEngine();
|
||||
const response = await queryEngine.query(SAMPLE_QUERY);
|
||||
const response = await queryEngine.query({ query: SAMPLE_QUERY });
|
||||
console.log(`Test query > ${SAMPLE_QUERY}:\n`, response.toString());
|
||||
}
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ program
|
||||
break;
|
||||
}
|
||||
|
||||
const response = await queryEngine.query(query);
|
||||
const response = await queryEngine.query({ query });
|
||||
|
||||
// Output response
|
||||
console.log(response.toString());
|
||||
|
||||
@@ -10,7 +10,9 @@ async function main() {
|
||||
|
||||
// Query the index
|
||||
const queryEngine = index.asQueryEngine();
|
||||
const response = await queryEngine.query("What mistakes did they make?");
|
||||
const response = await queryEngine.query({
|
||||
query: "What mistakes did they make?",
|
||||
});
|
||||
|
||||
// Output response
|
||||
console.log(response.toString());
|
||||
|
||||
@@ -31,9 +31,9 @@ async function main() {
|
||||
const queryEngine = index.asQueryEngine({
|
||||
nodePostprocessors: [new MetadataReplacementPostProcessor("window")],
|
||||
});
|
||||
const response = await queryEngine.query(
|
||||
"What did the author do in college?",
|
||||
);
|
||||
const response = await queryEngine.query({
|
||||
query: "What did the author do in college?",
|
||||
});
|
||||
|
||||
// Output response
|
||||
console.log(response.toString());
|
||||
|
||||
@@ -20,9 +20,9 @@ async function main() {
|
||||
|
||||
// Query the index
|
||||
const queryEngine = index.asQueryEngine();
|
||||
const response = await queryEngine.query(
|
||||
"What did the author do in college?",
|
||||
);
|
||||
const response = await queryEngine.query({
|
||||
query: "What did the author do in college?",
|
||||
});
|
||||
|
||||
// Output response
|
||||
console.log(response.toString());
|
||||
@@ -35,9 +35,9 @@ async function main() {
|
||||
storageContext: secondStorageContext,
|
||||
});
|
||||
const loadedQueryEngine = loadedIndex.asQueryEngine();
|
||||
const loadedResponse = await loadedQueryEngine.query(
|
||||
"What did the author do growing up?",
|
||||
);
|
||||
const loadedResponse = await loadedQueryEngine.query({
|
||||
query: "What did the author do growing up?",
|
||||
});
|
||||
console.log(loadedResponse.toString());
|
||||
}
|
||||
|
||||
|
||||
@@ -18,9 +18,9 @@ import essay from "./essay";
|
||||
],
|
||||
});
|
||||
|
||||
const response = await queryEngine.query(
|
||||
"How was Paul Grahams life different before and after YC?",
|
||||
);
|
||||
const response = await queryEngine.query({
|
||||
query: "How was Paul Grahams life different before and after YC?",
|
||||
});
|
||||
|
||||
console.log(response.toString());
|
||||
})();
|
||||
|
||||
@@ -20,9 +20,9 @@ async function main() {
|
||||
const queryEngine = index.asQueryEngine({
|
||||
retriever: index.asRetriever({ mode: SummaryRetrieverMode.LLM }),
|
||||
});
|
||||
const response = await queryEngine.query(
|
||||
"What did the author do growing up?",
|
||||
);
|
||||
const response = await queryEngine.query({
|
||||
query: "What did the author do growing up?",
|
||||
});
|
||||
console.log(response.toString());
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import { TogetherEmbedding, TogetherLLM } from "llamaindex";
|
||||
|
||||
// process.env.TOGETHER_API_KEY is required
|
||||
const together = new TogetherLLM({
|
||||
model: "mistralai/Mixtral-8x7B-Instruct-v0.1",
|
||||
});
|
||||
|
||||
(async () => {
|
||||
const generator = await together.chat({
|
||||
messages: [
|
||||
{
|
||||
role: "system",
|
||||
content: "You are an AI assistant",
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
content: "Tell me about San Francisco",
|
||||
},
|
||||
],
|
||||
stream: true,
|
||||
});
|
||||
console.log("Chatting with Together AI...");
|
||||
for await (const message of generator) {
|
||||
process.stdout.write(message.delta);
|
||||
}
|
||||
const embedding = new TogetherEmbedding();
|
||||
const vector = await embedding.getTextEmbedding("Hello world!");
|
||||
console.log("vector:", vector);
|
||||
})();
|
||||
@@ -0,0 +1,39 @@
|
||||
import fs from "node:fs/promises";
|
||||
|
||||
import {
|
||||
Document,
|
||||
TogetherEmbedding,
|
||||
TogetherLLM,
|
||||
VectorStoreIndex,
|
||||
serviceContextFromDefaults,
|
||||
} from "llamaindex";
|
||||
|
||||
async function main() {
|
||||
const apiKey = process.env.TOGETHER_API_KEY;
|
||||
if (!apiKey) {
|
||||
throw new Error("Missing TOGETHER_API_KEY");
|
||||
}
|
||||
const path = require.resolve("llamaindex/examples/abramov.txt");
|
||||
const essay = await fs.readFile(path, "utf-8");
|
||||
|
||||
const document = new Document({ text: essay, id_: path });
|
||||
|
||||
const serviceContext = serviceContextFromDefaults({
|
||||
llm: new TogetherLLM({ model: "mistralai/Mixtral-8x7B-Instruct-v0.1" }),
|
||||
embedModel: new TogetherEmbedding(),
|
||||
});
|
||||
|
||||
const index = await VectorStoreIndex.fromDocuments([document], {
|
||||
serviceContext,
|
||||
});
|
||||
|
||||
const queryEngine = index.asQueryEngine();
|
||||
|
||||
const response = await queryEngine.query({
|
||||
query: "What did the author do in college?",
|
||||
});
|
||||
|
||||
console.log(response.toString());
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"target": "es2016",
|
||||
"module": "commonjs",
|
||||
|
||||
@@ -16,9 +16,9 @@ async function main() {
|
||||
|
||||
// Query the index
|
||||
const queryEngine = index.asQueryEngine();
|
||||
const response = await queryEngine.query(
|
||||
"What did the author do in college?",
|
||||
);
|
||||
const response = await queryEngine.query({
|
||||
query: "What did the author do in college?",
|
||||
});
|
||||
|
||||
// Output response
|
||||
console.log(response.toString());
|
||||
|
||||
@@ -35,9 +35,9 @@ async function main() {
|
||||
|
||||
// Query the index
|
||||
const queryEngine = index.asQueryEngine({ responseSynthesizer });
|
||||
const response = await queryEngine.query(
|
||||
"What did the author do in college?",
|
||||
);
|
||||
const response = await queryEngine.query({
|
||||
query: "What did the author do in college?",
|
||||
});
|
||||
|
||||
// Output response
|
||||
console.log(response.toString());
|
||||
|
||||
@@ -33,9 +33,9 @@ async function main() {
|
||||
[nodePostprocessor],
|
||||
);
|
||||
|
||||
const response = await queryEngine.query(
|
||||
"What did the author do growing up?",
|
||||
);
|
||||
const response = await queryEngine.query({
|
||||
query: "What did the author do growing up?",
|
||||
});
|
||||
console.log(response.response);
|
||||
}
|
||||
|
||||
|
||||
@@ -189,7 +189,9 @@ async function main() {
|
||||
},
|
||||
});
|
||||
|
||||
const response = await queryEngine.query("How many results do you have?");
|
||||
const response = await queryEngine.query({
|
||||
query: "How many results do you have?",
|
||||
});
|
||||
|
||||
console.log(response.toString());
|
||||
}
|
||||
|
||||
@@ -25,9 +25,9 @@ async function main() {
|
||||
|
||||
// Query the index
|
||||
const queryEngine = index.asQueryEngine();
|
||||
const response = await queryEngine.query(
|
||||
"What did the author do in college?",
|
||||
);
|
||||
const response = await queryEngine.query({
|
||||
query: "What did the author do in college?",
|
||||
});
|
||||
|
||||
// Output response
|
||||
console.log(response.toString());
|
||||
|
||||
+5
-5
@@ -4,12 +4,12 @@ import { OpenAI } from "llamaindex";
|
||||
const llm = new OpenAI({ model: "gpt-4-vision-preview", temperature: 0.1 });
|
||||
|
||||
// complete api
|
||||
const response1 = await llm.complete("How are you?");
|
||||
console.log(response1.message.content);
|
||||
const response1 = await llm.complete({ prompt: "How are you?" });
|
||||
console.log(response1.text);
|
||||
|
||||
// chat api
|
||||
const response2 = await llm.chat([
|
||||
{ content: "Tell me a joke!", role: "user" },
|
||||
]);
|
||||
const response2 = await llm.chat({
|
||||
messages: [{ content: "Tell me a joke!", role: "user" }],
|
||||
});
|
||||
console.log(response2.message.content);
|
||||
})();
|
||||
|
||||
+3
-1
@@ -8,6 +8,7 @@
|
||||
"lint": "turbo run lint",
|
||||
"prepare": "husky install",
|
||||
"test": "turbo run test",
|
||||
"type-check": "tsc -b --diagnostics",
|
||||
"new-version": "turbo run build lint test --filter=\"!docs\" && changeset version",
|
||||
"new-snapshot": "turbo run build lint test --filter=\"!docs\" && changeset version --snapshot"
|
||||
},
|
||||
@@ -23,7 +24,8 @@
|
||||
"prettier": "^3.1.1",
|
||||
"prettier-plugin-organize-imports": "^3.2.4",
|
||||
"ts-jest": "^29.1.1",
|
||||
"turbo": "^1.11.2"
|
||||
"turbo": "^1.11.2",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"packageManager": "pnpm@8.10.5+sha256.a4bd9bb7b48214bbfcd95f264bd75bb70d100e5d4b58808f5cd6ab40c6ac21c5",
|
||||
"pnpm": {
|
||||
|
||||
@@ -1,5 +1,26 @@
|
||||
# llamaindex
|
||||
|
||||
## 0.0.47
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 844029d: Add streaming support for QueryEngine (and unify streaming interface with ChatEngine)
|
||||
- 844029d: Breaking: Use parameter object for query and chat methods of ChatEngine and QueryEngine
|
||||
|
||||
## 0.0.46
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 977f284: fixing import statement
|
||||
- 5d3bb66: fix: class SimpleKVStore might throw error in ES module
|
||||
- f18c9f6: refactor: Updated low-level streaming interface
|
||||
|
||||
## 0.0.45
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 2e6b36e: feat: support together AI
|
||||
|
||||
## 0.0.44
|
||||
|
||||
### Patch Changes
|
||||
|
||||
+33
-1
@@ -1,5 +1,10 @@
|
||||
# LlamaIndex.TS
|
||||
|
||||
[](https://www.npmjs.com/package/llamaindex)
|
||||
[](https://www.npmjs.com/package/llamaindex)
|
||||
[](https://www.npmjs.com/package/llamaindex)
|
||||
[](https://discord.com/invite/eN6D2HQ4aX)
|
||||
|
||||
LlamaIndex is a data framework for your LLM application.
|
||||
|
||||
Use your own data with large language models (LLMs, OpenAI ChatGPT and others) in Typescript and Javascript.
|
||||
@@ -12,7 +17,7 @@ LlamaIndex.TS aims to be a lightweight, easy to use set of libraries to help you
|
||||
|
||||
## Getting started with an example:
|
||||
|
||||
LlamaIndex.TS requries Node v18 or higher. You can download it from https://nodejs.org or use https://nvm.sh (our preferred option).
|
||||
LlamaIndex.TS requires Node v18 or higher. You can download it from https://nodejs.org or use https://nvm.sh (our preferred option).
|
||||
|
||||
In a new folder:
|
||||
|
||||
@@ -84,11 +89,38 @@ Check out our NextJS playground at https://llama-playground.vercel.app/. The sou
|
||||
|
||||
- [SimplePrompt](/packages/core/src/Prompt.ts): A simple standardized function call definition that takes in inputs and formats them in a template literal. SimplePrompts can be specialized using currying and combined using other SimplePrompt functions.
|
||||
|
||||
## Note: NextJS:
|
||||
|
||||
If you're using NextJS App Router, you'll need to use the NodeJS runtime (default) and add the following config to your next.config.js to have it use imports/exports in the same way Node does.
|
||||
|
||||
```js
|
||||
export const runtime = "nodejs"; // default
|
||||
```
|
||||
|
||||
```js
|
||||
// next.config.js
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
webpack: (config) => {
|
||||
config.resolve.alias = {
|
||||
...config.resolve.alias,
|
||||
sharp$: false,
|
||||
"onnxruntime-node$": false,
|
||||
mongodb$: false,
|
||||
};
|
||||
return config;
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = nextConfig;
|
||||
```
|
||||
|
||||
## Supported LLMs:
|
||||
|
||||
- OpenAI GPT-3.5-turbo and GPT-4
|
||||
- Anthropic Claude Instant and Claude 2
|
||||
- Llama2 Chat LLMs (70B, 13B, and 7B parameters)
|
||||
- MistralAI Chat LLMs
|
||||
|
||||
## Contributing:
|
||||
|
||||
|
||||
+26
-10
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "llamaindex",
|
||||
"version": "0.0.44",
|
||||
"version": "0.0.47",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/sdk": "^0.9.1",
|
||||
@@ -27,31 +27,47 @@
|
||||
"rake-modified": "^1.0.8",
|
||||
"replicate": "^0.21.1",
|
||||
"string-strip-html": "^13.4.3",
|
||||
"uuid": "^9.0.1",
|
||||
"wink-nlp": "^1.14.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.5.11",
|
||||
"@types/lodash": "^4.14.202",
|
||||
"@types/node": "^18.19.2",
|
||||
"@types/node": "^18.19.6",
|
||||
"@types/papaparse": "^5.3.14",
|
||||
"@types/pg": "^8.10.9",
|
||||
"@types/uuid": "^9.0.7",
|
||||
"bunchee": "^4.3.3",
|
||||
"node-stdlib-browser": "^1.2.0",
|
||||
"tsup": "^7.2.0",
|
||||
"typescript": "^5.3.2"
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"types": "./dist/index.d.ts",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.mjs",
|
||||
"repository": "run-llama/LlamaIndexTS",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.mts",
|
||||
"import": "./dist/index.mjs",
|
||||
"require": "./dist/index.js"
|
||||
},
|
||||
"./examples/*": "./examples/*"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"examples",
|
||||
"src",
|
||||
"types",
|
||||
"CHANGELOG.md"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/run-llama/LlamaIndexTS.git",
|
||||
"directory": "packages/core"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint .",
|
||||
"test": "jest",
|
||||
"build": "tsup src/index.ts --format esm,cjs --dts",
|
||||
"dev": "tsup src/index.ts --format esm,cjs --dts --watch"
|
||||
"build": "bunchee",
|
||||
"dev": "bunchee -w"
|
||||
}
|
||||
}
|
||||
|
||||
+146
-178
@@ -1,5 +1,5 @@
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { ChatHistory } from "./ChatHistory";
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { ChatHistory, SimpleChatHistory } from "./ChatHistory";
|
||||
import { NodeWithScore, TextNode } from "./Node";
|
||||
import {
|
||||
CondenseQuestionPrompt,
|
||||
@@ -13,27 +13,40 @@ import { Response } from "./Response";
|
||||
import { BaseRetriever } from "./Retriever";
|
||||
import { ServiceContext, serviceContextFromDefaults } from "./ServiceContext";
|
||||
import { Event } from "./callbacks/CallbackManager";
|
||||
import { ChatMessage, LLM, OpenAI } from "./llm";
|
||||
import { ChatMessage, ChatResponseChunk, LLM, OpenAI } from "./llm";
|
||||
import { streamConverter, streamReducer } from "./llm/utils";
|
||||
import { BaseNodePostprocessor } from "./postprocessors";
|
||||
|
||||
/**
|
||||
* Represents the base parameters for ChatEngine.
|
||||
*/
|
||||
export interface ChatEngineParamsBase {
|
||||
message: MessageContent;
|
||||
/**
|
||||
* Optional chat history if you want to customize the chat history.
|
||||
*/
|
||||
chatHistory?: ChatMessage[];
|
||||
history?: ChatHistory;
|
||||
}
|
||||
|
||||
export interface ChatEngineParamsStreaming extends ChatEngineParamsBase {
|
||||
stream: true;
|
||||
}
|
||||
|
||||
export interface ChatEngineParamsNonStreaming extends ChatEngineParamsBase {
|
||||
stream?: false | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A ChatEngine is used to handle back and forth chats between the application and the LLM.
|
||||
*/
|
||||
export interface ChatEngine {
|
||||
/**
|
||||
* Send message along with the class's current chat history to the LLM.
|
||||
* @param message
|
||||
* @param chatHistory optional chat history if you want to customize the chat history
|
||||
* @param streaming optional streaming flag, which auto-sets the return value if True.
|
||||
* @param params
|
||||
*/
|
||||
chat<
|
||||
T extends boolean | undefined = undefined,
|
||||
R = T extends true ? AsyncGenerator<string, void, unknown> : Response,
|
||||
>(
|
||||
message: MessageContent,
|
||||
chatHistory?: ChatMessage[],
|
||||
streaming?: T,
|
||||
): Promise<R>;
|
||||
chat(params: ChatEngineParamsStreaming): Promise<AsyncIterable<Response>>;
|
||||
chat(params: ChatEngineParamsNonStreaming): Promise<Response>;
|
||||
|
||||
/**
|
||||
* Resets the chat history so that it's empty.
|
||||
@@ -53,49 +66,37 @@ export class SimpleChatEngine implements ChatEngine {
|
||||
this.llm = init?.llm ?? new OpenAI();
|
||||
}
|
||||
|
||||
async chat<
|
||||
T extends boolean | undefined = undefined,
|
||||
R = T extends true ? AsyncGenerator<string, void, unknown> : Response,
|
||||
>(
|
||||
message: MessageContent,
|
||||
chatHistory?: ChatMessage[],
|
||||
streaming?: T,
|
||||
): Promise<R> {
|
||||
//Streaming option
|
||||
if (streaming) {
|
||||
return this.streamChat(message, chatHistory) as R;
|
||||
chat(params: ChatEngineParamsStreaming): Promise<AsyncIterable<Response>>;
|
||||
chat(params: ChatEngineParamsNonStreaming): Promise<Response>;
|
||||
async chat(
|
||||
params: ChatEngineParamsStreaming | ChatEngineParamsNonStreaming,
|
||||
): Promise<Response | AsyncIterable<Response>> {
|
||||
const { message, stream } = params;
|
||||
|
||||
const chatHistory = params.chatHistory ?? this.chatHistory;
|
||||
chatHistory.push({ content: message, role: "user" });
|
||||
|
||||
if (stream) {
|
||||
const stream = await this.llm.chat({
|
||||
messages: chatHistory,
|
||||
stream: true,
|
||||
});
|
||||
return streamConverter(
|
||||
streamReducer({
|
||||
stream,
|
||||
initialValue: "",
|
||||
reducer: (accumulator, part) => (accumulator += part.delta),
|
||||
finished: (accumulator) => {
|
||||
chatHistory.push({ content: accumulator, role: "assistant" });
|
||||
},
|
||||
}),
|
||||
(r: ChatResponseChunk) => new Response(r.delta),
|
||||
);
|
||||
}
|
||||
|
||||
//Non-streaming option
|
||||
chatHistory = chatHistory ?? this.chatHistory;
|
||||
chatHistory.push({ content: message, role: "user" });
|
||||
const response = await this.llm.chat(chatHistory, undefined);
|
||||
const response = await this.llm.chat({ messages: chatHistory });
|
||||
chatHistory.push(response.message);
|
||||
this.chatHistory = chatHistory;
|
||||
return new Response(response.message.content) as R;
|
||||
}
|
||||
|
||||
protected async *streamChat(
|
||||
message: MessageContent,
|
||||
chatHistory?: ChatMessage[],
|
||||
): AsyncGenerator<string, void, unknown> {
|
||||
chatHistory = chatHistory ?? this.chatHistory;
|
||||
chatHistory.push({ content: message, role: "user" });
|
||||
const response_generator = await this.llm.chat(
|
||||
chatHistory,
|
||||
undefined,
|
||||
true,
|
||||
);
|
||||
|
||||
var accumulator: string = "";
|
||||
for await (const part of response_generator) {
|
||||
accumulator += part;
|
||||
yield part;
|
||||
}
|
||||
|
||||
chatHistory.push({ content: accumulator, role: "assistant" });
|
||||
this.chatHistory = chatHistory;
|
||||
return;
|
||||
return new Response(response.message.content);
|
||||
}
|
||||
|
||||
reset() {
|
||||
@@ -116,7 +117,7 @@ export class SimpleChatEngine implements ChatEngine {
|
||||
export class CondenseQuestionChatEngine implements ChatEngine {
|
||||
queryEngine: BaseQueryEngine;
|
||||
chatHistory: ChatMessage[];
|
||||
serviceContext: ServiceContext;
|
||||
llm: LLM;
|
||||
condenseMessagePrompt: CondenseQuestionPrompt;
|
||||
|
||||
constructor(init: {
|
||||
@@ -127,8 +128,7 @@ export class CondenseQuestionChatEngine implements ChatEngine {
|
||||
}) {
|
||||
this.queryEngine = init.queryEngine;
|
||||
this.chatHistory = init?.chatHistory ?? [];
|
||||
this.serviceContext =
|
||||
init?.serviceContext ?? serviceContextFromDefaults({});
|
||||
this.llm = init?.serviceContext?.llm ?? serviceContextFromDefaults().llm;
|
||||
this.condenseMessagePrompt =
|
||||
init?.condenseMessagePrompt ?? defaultCondenseQuestionPrompt;
|
||||
}
|
||||
@@ -136,34 +136,47 @@ export class CondenseQuestionChatEngine implements ChatEngine {
|
||||
private async condenseQuestion(chatHistory: ChatMessage[], question: string) {
|
||||
const chatHistoryStr = messagesToHistoryStr(chatHistory);
|
||||
|
||||
return this.serviceContext.llm.complete(
|
||||
defaultCondenseQuestionPrompt({
|
||||
return this.llm.complete({
|
||||
prompt: defaultCondenseQuestionPrompt({
|
||||
question: question,
|
||||
chatHistory: chatHistoryStr,
|
||||
}),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
async chat<
|
||||
T extends boolean | undefined = undefined,
|
||||
R = T extends true ? AsyncGenerator<string, void, unknown> : Response,
|
||||
>(
|
||||
message: MessageContent,
|
||||
chatHistory?: ChatMessage[] | undefined,
|
||||
streaming?: T,
|
||||
): Promise<R> {
|
||||
chatHistory = chatHistory ?? this.chatHistory;
|
||||
chat(params: ChatEngineParamsStreaming): Promise<AsyncIterable<Response>>;
|
||||
chat(params: ChatEngineParamsNonStreaming): Promise<Response>;
|
||||
async chat(
|
||||
params: ChatEngineParamsStreaming | ChatEngineParamsNonStreaming,
|
||||
): Promise<Response | AsyncIterable<Response>> {
|
||||
const { message, stream } = params;
|
||||
const chatHistory = params.chatHistory ?? this.chatHistory;
|
||||
|
||||
const condensedQuestion = (
|
||||
await this.condenseQuestion(chatHistory, extractText(message))
|
||||
).message.content;
|
||||
|
||||
const response = await this.queryEngine.query(condensedQuestion);
|
||||
|
||||
).text;
|
||||
chatHistory.push({ content: message, role: "user" });
|
||||
|
||||
if (stream) {
|
||||
const stream = await this.queryEngine.query({
|
||||
query: condensedQuestion,
|
||||
stream: true,
|
||||
});
|
||||
return streamReducer({
|
||||
stream,
|
||||
initialValue: "",
|
||||
reducer: (accumulator, part) => (accumulator += part.response),
|
||||
finished: (accumulator) => {
|
||||
chatHistory.push({ content: accumulator, role: "assistant" });
|
||||
},
|
||||
});
|
||||
}
|
||||
const response = await this.queryEngine.query({
|
||||
query: condensedQuestion,
|
||||
});
|
||||
chatHistory.push({ content: response.response, role: "assistant" });
|
||||
|
||||
return response as R;
|
||||
return response;
|
||||
}
|
||||
|
||||
reset() {
|
||||
@@ -206,7 +219,7 @@ export class DefaultContextGenerator implements ContextGenerator {
|
||||
async generate(message: string, parentEvent?: Event): Promise<Context> {
|
||||
if (!parentEvent) {
|
||||
parentEvent = {
|
||||
id: uuidv4(),
|
||||
id: randomUUID(),
|
||||
type: "wrapper",
|
||||
tags: ["final"],
|
||||
};
|
||||
@@ -256,23 +269,15 @@ export class ContextChatEngine implements ChatEngine {
|
||||
});
|
||||
}
|
||||
|
||||
async chat<
|
||||
T extends boolean | undefined = undefined,
|
||||
R = T extends true ? AsyncGenerator<string, void, unknown> : Response,
|
||||
>(
|
||||
message: MessageContent,
|
||||
chatHistory?: ChatMessage[] | undefined,
|
||||
streaming?: T,
|
||||
): Promise<R> {
|
||||
chatHistory = chatHistory ?? this.chatHistory;
|
||||
|
||||
//Streaming option
|
||||
if (streaming) {
|
||||
return this.streamChat(message, chatHistory) as R;
|
||||
}
|
||||
|
||||
chat(params: ChatEngineParamsStreaming): Promise<AsyncIterable<Response>>;
|
||||
chat(params: ChatEngineParamsNonStreaming): Promise<Response>;
|
||||
async chat(
|
||||
params: ChatEngineParamsStreaming | ChatEngineParamsNonStreaming,
|
||||
): Promise<Response | AsyncIterable<Response>> {
|
||||
const { message, stream } = params;
|
||||
const chatHistory = params.chatHistory ?? this.chatHistory;
|
||||
const parentEvent: Event = {
|
||||
id: uuidv4(),
|
||||
id: randomUUID(),
|
||||
type: "wrapper",
|
||||
tags: ["final"],
|
||||
};
|
||||
@@ -280,57 +285,33 @@ export class ContextChatEngine implements ChatEngine {
|
||||
extractText(message),
|
||||
parentEvent,
|
||||
);
|
||||
|
||||
const nodes = context.nodes.map((r) => r.node);
|
||||
chatHistory.push({ content: message, role: "user" });
|
||||
|
||||
const response = await this.chatModel.chat(
|
||||
[context.message, ...chatHistory],
|
||||
if (stream) {
|
||||
const stream = await this.chatModel.chat({
|
||||
messages: [context.message, ...chatHistory],
|
||||
parentEvent,
|
||||
stream: true,
|
||||
});
|
||||
return streamConverter(
|
||||
streamReducer({
|
||||
stream,
|
||||
initialValue: "",
|
||||
reducer: (accumulator, part) => (accumulator += part.delta),
|
||||
finished: (accumulator) => {
|
||||
chatHistory.push({ content: accumulator, role: "assistant" });
|
||||
},
|
||||
}),
|
||||
(r: ChatResponseChunk) => new Response(r.delta, nodes),
|
||||
);
|
||||
}
|
||||
const response = await this.chatModel.chat({
|
||||
messages: [context.message, ...chatHistory],
|
||||
parentEvent,
|
||||
);
|
||||
});
|
||||
chatHistory.push(response.message);
|
||||
|
||||
this.chatHistory = chatHistory;
|
||||
|
||||
return new Response(
|
||||
response.message.content,
|
||||
context.nodes.map((r) => r.node),
|
||||
) as R;
|
||||
}
|
||||
|
||||
protected async *streamChat(
|
||||
message: MessageContent,
|
||||
chatHistory?: ChatMessage[] | undefined,
|
||||
): AsyncGenerator<string, void, unknown> {
|
||||
chatHistory = chatHistory ?? this.chatHistory;
|
||||
|
||||
const parentEvent: Event = {
|
||||
id: uuidv4(),
|
||||
type: "wrapper",
|
||||
tags: ["final"],
|
||||
};
|
||||
const context = await this.contextGenerator.generate(
|
||||
extractText(message),
|
||||
parentEvent,
|
||||
);
|
||||
|
||||
chatHistory.push({ content: message, role: "user" });
|
||||
|
||||
const response_stream = await this.chatModel.chat(
|
||||
[context.message, ...chatHistory],
|
||||
parentEvent,
|
||||
true,
|
||||
);
|
||||
var accumulator: string = "";
|
||||
for await (const part of response_stream) {
|
||||
accumulator += part;
|
||||
yield part;
|
||||
}
|
||||
|
||||
chatHistory.push({ content: accumulator, role: "assistant" });
|
||||
|
||||
this.chatHistory = chatHistory;
|
||||
|
||||
return;
|
||||
return new Response(response.message.content, nodes);
|
||||
}
|
||||
|
||||
reset() {
|
||||
@@ -383,51 +364,38 @@ export class HistoryChatEngine {
|
||||
this.contextGenerator = init?.contextGenerator;
|
||||
}
|
||||
|
||||
async chat<
|
||||
T extends boolean | undefined = undefined,
|
||||
R = T extends true ? AsyncGenerator<string, void, unknown> : Response,
|
||||
>(
|
||||
message: MessageContent,
|
||||
chatHistory: ChatHistory,
|
||||
streaming?: T,
|
||||
): Promise<R> {
|
||||
//Streaming option
|
||||
if (streaming) {
|
||||
return this.streamChat(message, chatHistory) as R;
|
||||
}
|
||||
chat(params: ChatEngineParamsStreaming): Promise<AsyncIterable<Response>>;
|
||||
chat(params: ChatEngineParamsNonStreaming): Promise<Response>;
|
||||
async chat(
|
||||
params: ChatEngineParamsStreaming | ChatEngineParamsNonStreaming,
|
||||
): Promise<Response | AsyncIterable<Response>> {
|
||||
const { message, stream, history } = params;
|
||||
const chatHistory = history ?? new SimpleChatHistory();
|
||||
const requestMessages = await this.prepareRequestMessages(
|
||||
message,
|
||||
chatHistory,
|
||||
);
|
||||
const response = await this.llm.chat(requestMessages);
|
||||
|
||||
if (stream) {
|
||||
const stream = await this.llm.chat({
|
||||
messages: requestMessages,
|
||||
stream: true,
|
||||
});
|
||||
return streamConverter(
|
||||
streamReducer({
|
||||
stream,
|
||||
initialValue: "",
|
||||
reducer: (accumulator, part) => (accumulator += part.delta),
|
||||
finished: (accumulator) => {
|
||||
chatHistory.addMessage({ content: accumulator, role: "assistant" });
|
||||
},
|
||||
}),
|
||||
(r: ChatResponseChunk) => new Response(r.delta),
|
||||
);
|
||||
}
|
||||
const response = await this.llm.chat({ messages: requestMessages });
|
||||
chatHistory.addMessage(response.message);
|
||||
return new Response(response.message.content) as R;
|
||||
}
|
||||
|
||||
protected async *streamChat(
|
||||
message: MessageContent,
|
||||
chatHistory: ChatHistory,
|
||||
): AsyncGenerator<string, void, unknown> {
|
||||
const requestMessages = await this.prepareRequestMessages(
|
||||
message,
|
||||
chatHistory,
|
||||
);
|
||||
const response_stream = await this.llm.chat(
|
||||
requestMessages,
|
||||
undefined,
|
||||
true,
|
||||
);
|
||||
|
||||
var accumulator = "";
|
||||
for await (const part of response_stream) {
|
||||
accumulator += part;
|
||||
yield part;
|
||||
}
|
||||
chatHistory.addMessage({
|
||||
content: accumulator,
|
||||
role: "assistant",
|
||||
});
|
||||
return;
|
||||
return new Response(response.message.content);
|
||||
}
|
||||
|
||||
private async prepareRequestMessages(
|
||||
|
||||
@@ -99,7 +99,7 @@ export class SummaryChatHistory implements ChatHistory {
|
||||
messagesToSummarize.shift();
|
||||
} while (this.llm.tokens(promptMessages) > this.tokensToSummarize);
|
||||
|
||||
const response = await this.llm.chat(promptMessages);
|
||||
const response = await this.llm.chat({ messages: promptMessages });
|
||||
return { content: response.message.content, role: "memory" };
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { encodingForModel } from "js-tiktoken";
|
||||
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { Event, EventTag, EventType } from "./callbacks/CallbackManager";
|
||||
|
||||
export enum Tokenizers {
|
||||
@@ -64,7 +64,7 @@ class GlobalsHelper {
|
||||
tags?: EventTag[];
|
||||
}): Event {
|
||||
return {
|
||||
id: uuidv4(),
|
||||
id: randomUUID(),
|
||||
type,
|
||||
// inherit parent tags if tags not set
|
||||
tags: tags || parentEvent?.tags,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import _ from "lodash";
|
||||
import { createHash } from "node:crypto";
|
||||
import path from "path";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { createHash, randomUUID } from "node:crypto";
|
||||
import path from "node:path";
|
||||
|
||||
export enum NodeRelationship {
|
||||
SOURCE = "SOURCE",
|
||||
@@ -49,7 +48,7 @@ export abstract class BaseNode<T extends Metadata = Metadata> {
|
||||
*
|
||||
* Set to a UUID by default.
|
||||
*/
|
||||
id_: string = uuidv4();
|
||||
id_: string = randomUUID();
|
||||
embedding?: number[];
|
||||
|
||||
// Metadata fields
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { NodeWithScore, TextNode } from "./Node";
|
||||
import {
|
||||
BaseQuestionGenerator,
|
||||
@@ -17,16 +17,32 @@ import {
|
||||
ResponseSynthesizer,
|
||||
} from "./synthesizers";
|
||||
|
||||
/**
|
||||
* Parameters for sending a query.
|
||||
*/
|
||||
export interface QueryEngineParamsBase {
|
||||
query: string;
|
||||
parentEvent?: Event;
|
||||
}
|
||||
|
||||
export interface QueryEngineParamsStreaming extends QueryEngineParamsBase {
|
||||
stream: true;
|
||||
}
|
||||
|
||||
export interface QueryEngineParamsNonStreaming extends QueryEngineParamsBase {
|
||||
stream?: false | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A query engine is a question answerer that can use one or more steps.
|
||||
*/
|
||||
export interface BaseQueryEngine {
|
||||
/**
|
||||
* Query the query engine and get a response.
|
||||
* @param query
|
||||
* @param parentEvent
|
||||
* @param params
|
||||
*/
|
||||
query(query: string, parentEvent?: Event): Promise<Response>;
|
||||
query(params: QueryEngineParamsStreaming): Promise<AsyncIterable<Response>>;
|
||||
query(params: QueryEngineParamsNonStreaming): Promise<Response>;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,14 +86,31 @@ export class RetrieverQueryEngine implements BaseQueryEngine {
|
||||
return this.applyNodePostprocessors(nodes);
|
||||
}
|
||||
|
||||
async query(query: string, parentEvent?: Event) {
|
||||
const _parentEvent: Event = parentEvent || {
|
||||
id: uuidv4(),
|
||||
query(params: QueryEngineParamsStreaming): Promise<AsyncIterable<Response>>;
|
||||
query(params: QueryEngineParamsNonStreaming): Promise<Response>;
|
||||
async query(
|
||||
params: QueryEngineParamsStreaming | QueryEngineParamsNonStreaming,
|
||||
): Promise<Response | AsyncIterable<Response>> {
|
||||
const { query, stream } = params;
|
||||
const parentEvent: Event = params.parentEvent || {
|
||||
id: randomUUID(),
|
||||
type: "wrapper",
|
||||
tags: ["final"],
|
||||
};
|
||||
const nodes = await this.retrieve(query, _parentEvent);
|
||||
return this.responseSynthesizer.synthesize(query, nodes, _parentEvent);
|
||||
const nodesWithScore = await this.retrieve(query, parentEvent);
|
||||
if (stream) {
|
||||
return this.responseSynthesizer.synthesize({
|
||||
query,
|
||||
nodesWithScore,
|
||||
parentEvent,
|
||||
stream: true,
|
||||
});
|
||||
}
|
||||
return this.responseSynthesizer.synthesize({
|
||||
query,
|
||||
nodesWithScore,
|
||||
parentEvent,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,19 +164,24 @@ export class SubQuestionQueryEngine implements BaseQueryEngine {
|
||||
});
|
||||
}
|
||||
|
||||
async query(query: string): Promise<Response> {
|
||||
query(params: QueryEngineParamsStreaming): Promise<AsyncIterable<Response>>;
|
||||
query(params: QueryEngineParamsNonStreaming): Promise<Response>;
|
||||
async query(
|
||||
params: QueryEngineParamsStreaming | QueryEngineParamsNonStreaming,
|
||||
): Promise<Response | AsyncIterable<Response>> {
|
||||
const { query, stream } = params;
|
||||
const subQuestions = await this.questionGen.generate(this.metadatas, query);
|
||||
|
||||
// groups final retrieval+synthesis operation
|
||||
const parentEvent: Event = {
|
||||
id: uuidv4(),
|
||||
const parentEvent: Event = params.parentEvent || {
|
||||
id: randomUUID(),
|
||||
type: "wrapper",
|
||||
tags: ["final"],
|
||||
};
|
||||
|
||||
// groups all sub-queries
|
||||
const subQueryParentEvent: Event = {
|
||||
id: uuidv4(),
|
||||
id: randomUUID(),
|
||||
parentId: parentEvent.id,
|
||||
type: "wrapper",
|
||||
tags: ["intermediate"],
|
||||
@@ -153,10 +191,22 @@ export class SubQuestionQueryEngine implements BaseQueryEngine {
|
||||
subQuestions.map((subQ) => this.querySubQ(subQ, subQueryParentEvent)),
|
||||
);
|
||||
|
||||
const nodes = subQNodes
|
||||
const nodesWithScore = subQNodes
|
||||
.filter((node) => node !== null)
|
||||
.map((node) => node as NodeWithScore);
|
||||
return this.responseSynthesizer.synthesize(query, nodes, parentEvent);
|
||||
if (stream) {
|
||||
return this.responseSynthesizer.synthesize({
|
||||
query,
|
||||
nodesWithScore,
|
||||
parentEvent,
|
||||
stream: true,
|
||||
});
|
||||
}
|
||||
return this.responseSynthesizer.synthesize({
|
||||
query,
|
||||
nodesWithScore,
|
||||
parentEvent,
|
||||
});
|
||||
}
|
||||
|
||||
private async querySubQ(
|
||||
@@ -167,7 +217,10 @@ export class SubQuestionQueryEngine implements BaseQueryEngine {
|
||||
const question = subQ.subQuestion;
|
||||
const queryEngine = this.queryEngines[subQ.toolName];
|
||||
|
||||
const response = await queryEngine.query(question, parentEvent);
|
||||
const response = await queryEngine.query({
|
||||
query: question,
|
||||
parentEvent,
|
||||
});
|
||||
const responseText = response.response;
|
||||
const nodeText = `Sub question: ${question}\nResponse: ${responseText}`;
|
||||
const node = new TextNode({ text: nodeText });
|
||||
|
||||
@@ -41,13 +41,13 @@ export class LLMQuestionGenerator implements BaseQuestionGenerator {
|
||||
const toolsStr = buildToolsText(tools);
|
||||
const queryStr = query;
|
||||
const prediction = (
|
||||
await this.llm.complete(
|
||||
this.prompt({
|
||||
await this.llm.complete({
|
||||
prompt: this.prompt({
|
||||
toolsStr,
|
||||
queryStr,
|
||||
}),
|
||||
)
|
||||
).message.content;
|
||||
})
|
||||
).text;
|
||||
|
||||
const structuredOutput = this.outputParser.parse(prediction);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { BaseNode } from "./Node";
|
||||
|
||||
/**
|
||||
* Respone is the output of a LLM
|
||||
* Response is the output of a LLM
|
||||
*/
|
||||
export class Response {
|
||||
response: string;
|
||||
|
||||
@@ -14,7 +14,7 @@ export enum OpenAIEmbeddingModelType {
|
||||
}
|
||||
|
||||
export class OpenAIEmbedding extends BaseEmbedding {
|
||||
model: OpenAIEmbeddingModelType;
|
||||
model: OpenAIEmbeddingModelType | string;
|
||||
|
||||
// OpenAI session params
|
||||
apiKey?: string = undefined;
|
||||
|
||||
@@ -3,5 +3,6 @@ export * from "./HuggingFaceEmbedding";
|
||||
export * from "./MistralAIEmbedding";
|
||||
export * from "./MultiModalEmbedding";
|
||||
export * from "./OpenAIEmbedding";
|
||||
export { TogetherEmbedding } from "./together";
|
||||
export * from "./types";
|
||||
export * from "./utils";
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { OpenAIEmbedding } from "./OpenAIEmbedding";
|
||||
|
||||
export class TogetherEmbedding extends OpenAIEmbedding {
|
||||
override model: string;
|
||||
constructor(init?: Partial<OpenAIEmbedding>) {
|
||||
super({
|
||||
apiKey: process.env.TOGETHER_API_KEY,
|
||||
...init,
|
||||
additionalSessionOptions: {
|
||||
...init?.additionalSessionOptions,
|
||||
baseURL: "https://api.together.xyz/v1",
|
||||
},
|
||||
});
|
||||
this.model = init?.model ?? "togethercomputer/m2-bert-80M-32k-retrieval";
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { BaseNode, Document, jsonToNode } from "../Node";
|
||||
import { BaseQueryEngine } from "../QueryEngine";
|
||||
import { BaseRetriever } from "../Retriever";
|
||||
@@ -16,7 +16,7 @@ export abstract class IndexStruct {
|
||||
indexId: string;
|
||||
summary?: string;
|
||||
|
||||
constructor(indexId = uuidv4(), summary = undefined) {
|
||||
constructor(indexId = randomUUID(), summary = undefined) {
|
||||
this.indexId = indexId;
|
||||
this.summary = summary;
|
||||
}
|
||||
|
||||
@@ -149,12 +149,12 @@ export class KeywordTableIndex extends BaseIndex<KeywordTable> {
|
||||
text: string,
|
||||
serviceContext: ServiceContext,
|
||||
): Promise<Set<string>> {
|
||||
const response = await serviceContext.llm.complete(
|
||||
defaultKeywordExtractPrompt({
|
||||
const response = await serviceContext.llm.complete({
|
||||
prompt: defaultKeywordExtractPrompt({
|
||||
context: text,
|
||||
}),
|
||||
);
|
||||
return extractKeywordsGivenResponse(response.message.content, "KEYWORDS:");
|
||||
});
|
||||
return extractKeywordsGivenResponse(response.text, "KEYWORDS:");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -86,16 +86,13 @@ abstract class BaseKeywordTableRetriever implements BaseRetriever {
|
||||
// Extracts keywords using LLMs.
|
||||
export class KeywordTableLLMRetriever extends BaseKeywordTableRetriever {
|
||||
async getKeywords(query: string): Promise<string[]> {
|
||||
const response = await this.serviceContext.llm.complete(
|
||||
this.queryKeywordExtractTemplate({
|
||||
const response = await this.serviceContext.llm.complete({
|
||||
prompt: this.queryKeywordExtractTemplate({
|
||||
question: query,
|
||||
maxKeywords: this.maxKeywordsPerQuery,
|
||||
}),
|
||||
);
|
||||
const keywords = extractKeywordsGivenResponse(
|
||||
response.message.content,
|
||||
"KEYWORDS:",
|
||||
);
|
||||
});
|
||||
const keywords = extractKeywordsGivenResponse(response.text, "KEYWORDS:");
|
||||
return [...keywords];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,8 +89,10 @@ export class SummaryIndexLLMRetriever implements BaseRetriever {
|
||||
const fmtBatchStr = this.formatNodeBatchFn(nodesBatch);
|
||||
const input = { context: fmtBatchStr, query: query };
|
||||
const rawResponse = (
|
||||
await this.serviceContext.llm.complete(this.choiceSelectPrompt(input))
|
||||
).message.content;
|
||||
await this.serviceContext.llm.complete({
|
||||
prompt: this.choiceSelectPrompt(input),
|
||||
})
|
||||
).text;
|
||||
|
||||
// parseResult is a map from doc number to relevance score
|
||||
const parseResult = this.parseChoiceSelectAnswerFn(
|
||||
|
||||
+166
-171
@@ -10,8 +10,7 @@ import {
|
||||
|
||||
import { ChatCompletionMessageParam } from "openai/resources";
|
||||
import { LLMOptions } from "portkey-ai";
|
||||
import { MessageContent } from "../ChatEngine";
|
||||
import { globalsHelper, Tokenizers } from "../GlobalsHelper";
|
||||
import { Tokenizers, globalsHelper } from "../GlobalsHelper";
|
||||
import {
|
||||
ANTHROPIC_AI_PROMPT,
|
||||
ANTHROPIC_HUMAN_PROMPT,
|
||||
@@ -25,9 +24,10 @@ import {
|
||||
getAzureModel,
|
||||
shouldUseAzure,
|
||||
} from "./azure";
|
||||
import { getOpenAISession, OpenAISession } from "./openai";
|
||||
import { getPortkeySession, PortkeySession } from "./portkey";
|
||||
import { OpenAISession, getOpenAISession } from "./openai";
|
||||
import { PortkeySession, getPortkeySession } from "./portkey";
|
||||
import { ReplicateSession } from "./replicate";
|
||||
import { streamConverter } from "./utils";
|
||||
|
||||
export type MessageType =
|
||||
| "user"
|
||||
@@ -45,11 +45,16 @@ export interface ChatMessage {
|
||||
export interface ChatResponse {
|
||||
message: ChatMessage;
|
||||
raw?: Record<string, any>;
|
||||
delta?: string;
|
||||
}
|
||||
|
||||
// NOTE in case we need CompletionResponse to diverge from ChatResponse in the future
|
||||
export type CompletionResponse = ChatResponse;
|
||||
export interface ChatResponseChunk {
|
||||
delta: string;
|
||||
}
|
||||
|
||||
export interface CompletionResponse {
|
||||
text: string;
|
||||
raw?: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface LLMMetadata {
|
||||
model: string;
|
||||
@@ -60,40 +65,59 @@ export interface LLMMetadata {
|
||||
tokenizer: Tokenizers | undefined;
|
||||
}
|
||||
|
||||
export interface LLMChatParamsBase {
|
||||
messages: ChatMessage[];
|
||||
parentEvent?: Event;
|
||||
extraParams?: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface LLMChatParamsStreaming extends LLMChatParamsBase {
|
||||
stream: true;
|
||||
}
|
||||
|
||||
export interface LLMChatParamsNonStreaming extends LLMChatParamsBase {
|
||||
stream?: false | null;
|
||||
}
|
||||
|
||||
export interface LLMCompletionParamsBase {
|
||||
prompt: any;
|
||||
parentEvent?: Event;
|
||||
}
|
||||
|
||||
export interface LLMCompletionParamsStreaming extends LLMCompletionParamsBase {
|
||||
stream: true;
|
||||
}
|
||||
|
||||
export interface LLMCompletionParamsNonStreaming
|
||||
extends LLMCompletionParamsBase {
|
||||
stream?: false | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unified language model interface
|
||||
*/
|
||||
export interface LLM {
|
||||
metadata: LLMMetadata;
|
||||
// Whether a LLM has streaming support
|
||||
hasStreaming: boolean;
|
||||
/**
|
||||
* Get a chat response from the LLM
|
||||
* @param messages
|
||||
*
|
||||
* The return type of chat() and complete() are set by the "streaming" parameter being set to True.
|
||||
* @param params
|
||||
*/
|
||||
chat<
|
||||
T extends boolean | undefined = undefined,
|
||||
R = T extends true ? AsyncGenerator<string, void, unknown> : ChatResponse,
|
||||
>(
|
||||
messages: ChatMessage[],
|
||||
parentEvent?: Event,
|
||||
streaming?: T,
|
||||
): Promise<R>;
|
||||
chat(
|
||||
params: LLMChatParamsStreaming,
|
||||
): Promise<AsyncIterable<ChatResponseChunk>>;
|
||||
chat(params: LLMChatParamsNonStreaming): Promise<ChatResponse>;
|
||||
|
||||
/**
|
||||
* Get a prompt completion from the LLM
|
||||
* @param prompt the prompt to complete
|
||||
* @param params
|
||||
*/
|
||||
complete<
|
||||
T extends boolean | undefined = undefined,
|
||||
R = T extends true ? AsyncGenerator<string, void, unknown> : ChatResponse,
|
||||
>(
|
||||
prompt: MessageContent,
|
||||
parentEvent?: Event,
|
||||
streaming?: T,
|
||||
): Promise<R>;
|
||||
complete(
|
||||
params: LLMCompletionParamsStreaming,
|
||||
): Promise<AsyncIterable<CompletionResponse>>;
|
||||
complete(
|
||||
params: LLMCompletionParamsNonStreaming,
|
||||
): Promise<CompletionResponse>;
|
||||
|
||||
/**
|
||||
* Calculates the number of tokens needed for the given chat messages
|
||||
@@ -101,6 +125,46 @@ export interface LLM {
|
||||
tokens(messages: ChatMessage[]): number;
|
||||
}
|
||||
|
||||
export abstract class BaseLLM implements LLM {
|
||||
abstract metadata: LLMMetadata;
|
||||
|
||||
complete(
|
||||
params: LLMCompletionParamsStreaming,
|
||||
): Promise<AsyncIterable<CompletionResponse>>;
|
||||
complete(
|
||||
params: LLMCompletionParamsNonStreaming,
|
||||
): Promise<CompletionResponse>;
|
||||
async complete(
|
||||
params: LLMCompletionParamsStreaming | LLMCompletionParamsNonStreaming,
|
||||
): Promise<CompletionResponse | AsyncIterable<CompletionResponse>> {
|
||||
const { prompt, parentEvent, stream } = params;
|
||||
if (stream) {
|
||||
const stream = await this.chat({
|
||||
messages: [{ content: prompt, role: "user" }],
|
||||
parentEvent,
|
||||
stream: true,
|
||||
});
|
||||
return streamConverter(stream, (chunk) => {
|
||||
return {
|
||||
text: chunk.delta,
|
||||
};
|
||||
});
|
||||
}
|
||||
const chatResponse = await this.chat({
|
||||
messages: [{ content: prompt, role: "user" }],
|
||||
parentEvent,
|
||||
});
|
||||
return { text: chatResponse.message.content as string };
|
||||
}
|
||||
|
||||
abstract chat(
|
||||
params: LLMChatParamsStreaming,
|
||||
): Promise<AsyncIterable<ChatResponseChunk>>;
|
||||
abstract chat(params: LLMChatParamsNonStreaming): Promise<ChatResponse>;
|
||||
|
||||
abstract tokens(messages: ChatMessage[]): number;
|
||||
}
|
||||
|
||||
export const GPT4_MODELS = {
|
||||
"gpt-4": { contextWindow: 8192 },
|
||||
"gpt-4-32k": { contextWindow: 32768 },
|
||||
@@ -125,17 +189,15 @@ export const ALL_AVAILABLE_OPENAI_MODELS = {
|
||||
/**
|
||||
* OpenAI LLM implementation
|
||||
*/
|
||||
export class OpenAI implements LLM {
|
||||
hasStreaming: boolean = true;
|
||||
|
||||
export class OpenAI extends BaseLLM {
|
||||
// Per completion OpenAI params
|
||||
model: keyof typeof ALL_AVAILABLE_OPENAI_MODELS;
|
||||
model: keyof typeof ALL_AVAILABLE_OPENAI_MODELS | string;
|
||||
temperature: number;
|
||||
topP: number;
|
||||
maxTokens?: number;
|
||||
additionalChatOptions?: Omit<
|
||||
Partial<OpenAILLM.Chat.ChatCompletionCreateParams>,
|
||||
"max_tokens" | "messages" | "model" | "temperature" | "top_p" | "streaming"
|
||||
"max_tokens" | "messages" | "model" | "temperature" | "top_p" | "stream"
|
||||
>;
|
||||
|
||||
// OpenAI session params
|
||||
@@ -155,6 +217,7 @@ export class OpenAI implements LLM {
|
||||
azure?: AzureOpenAIConfig;
|
||||
},
|
||||
) {
|
||||
super();
|
||||
this.model = init?.model ?? "gpt-3.5-turbo";
|
||||
this.temperature = init?.temperature ?? 0.1;
|
||||
this.topP = init?.topP ?? 1;
|
||||
@@ -205,12 +268,16 @@ export class OpenAI implements LLM {
|
||||
}
|
||||
|
||||
get metadata() {
|
||||
const contextWindow =
|
||||
ALL_AVAILABLE_OPENAI_MODELS[
|
||||
this.model as keyof typeof ALL_AVAILABLE_OPENAI_MODELS
|
||||
]?.contextWindow ?? 1024;
|
||||
return {
|
||||
model: this.model,
|
||||
temperature: this.temperature,
|
||||
topP: this.topP,
|
||||
maxTokens: this.maxTokens,
|
||||
contextWindow: ALL_AVAILABLE_OPENAI_MODELS[this.model].contextWindow,
|
||||
contextWindow,
|
||||
tokenizer: Tokenizers.CL100K_BASE,
|
||||
};
|
||||
}
|
||||
@@ -247,10 +314,14 @@ export class OpenAI implements LLM {
|
||||
}
|
||||
}
|
||||
|
||||
async chat<
|
||||
T extends boolean | undefined = undefined,
|
||||
R = T extends true ? AsyncGenerator<string, void, unknown> : ChatResponse,
|
||||
>(messages: ChatMessage[], parentEvent?: Event, streaming?: T): Promise<R> {
|
||||
chat(
|
||||
params: LLMChatParamsStreaming,
|
||||
): Promise<AsyncIterable<ChatResponseChunk>>;
|
||||
chat(params: LLMChatParamsNonStreaming): Promise<ChatResponse>;
|
||||
async chat(
|
||||
params: LLMChatParamsNonStreaming | LLMChatParamsStreaming,
|
||||
): Promise<ChatResponse | AsyncIterable<ChatResponseChunk>> {
|
||||
const { messages, parentEvent, stream } = params;
|
||||
const baseRequestParams: OpenAILLM.Chat.ChatCompletionCreateParams = {
|
||||
model: this.model,
|
||||
temperature: this.temperature,
|
||||
@@ -266,11 +337,8 @@ export class OpenAI implements LLM {
|
||||
...this.additionalChatOptions,
|
||||
};
|
||||
// Streaming
|
||||
if (streaming) {
|
||||
if (!this.hasStreaming) {
|
||||
throw Error("No streaming support for this LLM.");
|
||||
}
|
||||
return this.streamChat(messages, parentEvent) as R;
|
||||
if (stream) {
|
||||
return this.streamChat(params);
|
||||
}
|
||||
// Non-streaming
|
||||
const response = await this.session.openai.chat.completions.create({
|
||||
@@ -281,27 +349,13 @@ export class OpenAI implements LLM {
|
||||
const content = response.choices[0].message?.content ?? "";
|
||||
return {
|
||||
message: { content, role: response.choices[0].message.role },
|
||||
} as R;
|
||||
};
|
||||
}
|
||||
|
||||
async complete<
|
||||
T extends boolean | undefined = undefined,
|
||||
R = T extends true ? AsyncGenerator<string, void, unknown> : ChatResponse,
|
||||
>(prompt: string, parentEvent?: Event, streaming?: T): Promise<R> {
|
||||
return this.chat(
|
||||
[{ content: prompt, role: "user" }],
|
||||
parentEvent,
|
||||
streaming,
|
||||
);
|
||||
}
|
||||
|
||||
//We can wrap a stream in a generator to add some additional logging behavior
|
||||
//For future edits: syntax for generator type is <typeof Yield, typeof Return, typeof Accept>
|
||||
//"typeof Accept" refers to what types you'll accept when you manually call generator.next(<AcceptType>)
|
||||
protected async *streamChat(
|
||||
messages: ChatMessage[],
|
||||
parentEvent?: Event,
|
||||
): AsyncGenerator<string, void, unknown> {
|
||||
protected async *streamChat({
|
||||
messages,
|
||||
parentEvent,
|
||||
}: LLMChatParamsStreaming): AsyncIterable<ChatResponseChunk> {
|
||||
const baseRequestParams: OpenAILLM.Chat.ChatCompletionCreateParams = {
|
||||
model: this.model,
|
||||
temperature: this.temperature,
|
||||
@@ -335,6 +389,7 @@ export class OpenAI implements LLM {
|
||||
type: "llmPredict" as EventType,
|
||||
};
|
||||
|
||||
// TODO: add callback to streamConverter and use streamConverter here
|
||||
//Indices
|
||||
var idx_counter: number = 0;
|
||||
for await (const part of chunk_stream) {
|
||||
@@ -356,18 +411,12 @@ export class OpenAI implements LLM {
|
||||
|
||||
idx_counter++;
|
||||
|
||||
yield part.choices[0].delta.content ? part.choices[0].delta.content : "";
|
||||
yield {
|
||||
delta: part.choices[0].delta.content ?? "",
|
||||
};
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//streamComplete doesn't need to be async because it's child function is already async
|
||||
protected streamComplete(
|
||||
query: string,
|
||||
parentEvent?: Event,
|
||||
): AsyncGenerator<string, void, unknown> {
|
||||
return this.streamChat([{ content: query, role: "user" }], parentEvent);
|
||||
}
|
||||
}
|
||||
|
||||
export const ALL_AVAILABLE_LLAMADEUCE_MODELS = {
|
||||
@@ -425,16 +474,16 @@ export enum DeuceChatStrategy {
|
||||
/**
|
||||
* Llama2 LLM implementation
|
||||
*/
|
||||
export class LlamaDeuce implements LLM {
|
||||
export class LlamaDeuce extends BaseLLM {
|
||||
model: keyof typeof ALL_AVAILABLE_LLAMADEUCE_MODELS;
|
||||
chatStrategy: DeuceChatStrategy;
|
||||
temperature: number;
|
||||
topP: number;
|
||||
maxTokens?: number;
|
||||
replicateSession: ReplicateSession;
|
||||
hasStreaming: boolean;
|
||||
|
||||
constructor(init?: Partial<LlamaDeuce>) {
|
||||
super();
|
||||
this.model = init?.model ?? "Llama-2-70b-chat-4bit";
|
||||
this.chatStrategy =
|
||||
init?.chatStrategy ??
|
||||
@@ -447,7 +496,6 @@ export class LlamaDeuce implements LLM {
|
||||
init?.maxTokens ??
|
||||
ALL_AVAILABLE_LLAMADEUCE_MODELS[this.model].contextWindow; // For Replicate, the default is 500 tokens which is too low.
|
||||
this.replicateSession = init?.replicateSession ?? new ReplicateSession();
|
||||
this.hasStreaming = init?.hasStreaming ?? false;
|
||||
}
|
||||
|
||||
tokens(messages: ChatMessage[]): number {
|
||||
@@ -592,10 +640,14 @@ If a question does not make any sense, or is not factually coherent, explain why
|
||||
};
|
||||
}
|
||||
|
||||
async chat<
|
||||
T extends boolean | undefined = undefined,
|
||||
R = T extends true ? AsyncGenerator<string, void, unknown> : ChatResponse,
|
||||
>(messages: ChatMessage[], _parentEvent?: Event, streaming?: T): Promise<R> {
|
||||
chat(
|
||||
params: LLMChatParamsStreaming,
|
||||
): Promise<AsyncIterable<ChatResponseChunk>>;
|
||||
chat(params: LLMChatParamsNonStreaming): Promise<ChatResponse>;
|
||||
async chat(
|
||||
params: LLMChatParamsNonStreaming | LLMChatParamsStreaming,
|
||||
): Promise<ChatResponse | AsyncIterable<ChatResponseChunk>> {
|
||||
const { messages, parentEvent, stream } = params;
|
||||
const api = ALL_AVAILABLE_LLAMADEUCE_MODELS[this.model]
|
||||
.replicateApi as `${string}/${string}:${string}`;
|
||||
|
||||
@@ -617,6 +669,9 @@ If a question does not make any sense, or is not factually coherent, explain why
|
||||
}
|
||||
|
||||
//TODO: Add streaming for this
|
||||
if (stream) {
|
||||
throw new Error("Streaming not supported for LlamaDeuce");
|
||||
}
|
||||
|
||||
//Non-streaming
|
||||
const response = await this.replicateSession.replicate.run(
|
||||
@@ -629,14 +684,7 @@ If a question does not make any sense, or is not factually coherent, explain why
|
||||
//^ We need to do this because Replicate returns a list of strings (for streaming functionality which is not exposed by the run function)
|
||||
role: "assistant",
|
||||
},
|
||||
} as R;
|
||||
}
|
||||
|
||||
async complete<
|
||||
T extends boolean | undefined = undefined,
|
||||
R = T extends true ? AsyncGenerator<string, void, unknown> : ChatResponse,
|
||||
>(prompt: string, parentEvent?: Event, streaming?: T): Promise<R> {
|
||||
return this.chat([{ content: prompt, role: "user" }], parentEvent);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -650,9 +698,7 @@ export const ALL_AVAILABLE_ANTHROPIC_MODELS = {
|
||||
* Anthropic LLM implementation
|
||||
*/
|
||||
|
||||
export class Anthropic implements LLM {
|
||||
hasStreaming: boolean = true;
|
||||
|
||||
export class Anthropic extends BaseLLM {
|
||||
// Per completion Anthropic params
|
||||
model: keyof typeof ALL_AVAILABLE_ANTHROPIC_MODELS;
|
||||
temperature: number;
|
||||
@@ -668,6 +714,7 @@ export class Anthropic implements LLM {
|
||||
callbackManager?: CallbackManager;
|
||||
|
||||
constructor(init?: Partial<Anthropic>) {
|
||||
super();
|
||||
this.model = init?.model ?? "claude-2";
|
||||
this.temperature = init?.temperature ?? 0.1;
|
||||
this.topP = init?.topP ?? 0.999; // Per Ben Mann
|
||||
@@ -719,20 +766,17 @@ export class Anthropic implements LLM {
|
||||
);
|
||||
}
|
||||
|
||||
async chat<
|
||||
T extends boolean | undefined = undefined,
|
||||
R = T extends true ? AsyncGenerator<string, void, unknown> : ChatResponse,
|
||||
>(
|
||||
messages: ChatMessage[],
|
||||
parentEvent?: Event | undefined,
|
||||
streaming?: T,
|
||||
): Promise<R> {
|
||||
chat(
|
||||
params: LLMChatParamsStreaming,
|
||||
): Promise<AsyncIterable<ChatResponseChunk>>;
|
||||
chat(params: LLMChatParamsNonStreaming): Promise<ChatResponse>;
|
||||
async chat(
|
||||
params: LLMChatParamsNonStreaming | LLMChatParamsStreaming,
|
||||
): Promise<ChatResponse | AsyncIterable<ChatResponseChunk>> {
|
||||
const { messages, parentEvent, stream } = params;
|
||||
//Streaming
|
||||
if (streaming) {
|
||||
if (!this.hasStreaming) {
|
||||
throw Error("No streaming support for this LLM.");
|
||||
}
|
||||
return this.streamChat(messages, parentEvent) as R;
|
||||
if (stream) {
|
||||
return this.streamChat(messages, parentEvent);
|
||||
}
|
||||
|
||||
//Non-streaming
|
||||
@@ -748,13 +792,13 @@ export class Anthropic implements LLM {
|
||||
message: { content: response.completion.trimStart(), role: "assistant" },
|
||||
//^ We're trimming the start because Anthropic often starts with a space in the response
|
||||
// That space will be re-added when we generate the next prompt.
|
||||
} as R;
|
||||
};
|
||||
}
|
||||
|
||||
protected async *streamChat(
|
||||
messages: ChatMessage[],
|
||||
parentEvent?: Event | undefined,
|
||||
): AsyncGenerator<string, void, unknown> {
|
||||
): AsyncIterable<ChatResponseChunk> {
|
||||
// AsyncIterable<AnthropicStreamToken>
|
||||
const stream: AsyncIterable<AnthropicStreamToken> =
|
||||
await this.session.anthropic.completions.create({
|
||||
@@ -771,40 +815,13 @@ export class Anthropic implements LLM {
|
||||
//TODO: LLM Stream Callback, pending re-work.
|
||||
|
||||
idx_counter++;
|
||||
yield part.completion;
|
||||
yield { delta: part.completion };
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
async complete<
|
||||
T extends boolean | undefined = undefined,
|
||||
R = T extends true ? AsyncGenerator<string, void, unknown> : ChatResponse,
|
||||
>(
|
||||
prompt: string,
|
||||
parentEvent?: Event | undefined,
|
||||
streaming?: T,
|
||||
): Promise<R> {
|
||||
if (streaming) {
|
||||
return this.streamComplete(prompt, parentEvent) as R;
|
||||
}
|
||||
return this.chat(
|
||||
[{ content: prompt, role: "user" }],
|
||||
parentEvent,
|
||||
streaming,
|
||||
) as R;
|
||||
}
|
||||
|
||||
protected streamComplete(
|
||||
prompt: string,
|
||||
parentEvent?: Event | undefined,
|
||||
): AsyncGenerator<string, void, unknown> {
|
||||
return this.streamChat([{ content: prompt, role: "user" }], parentEvent);
|
||||
}
|
||||
}
|
||||
|
||||
export class Portkey implements LLM {
|
||||
hasStreaming: boolean = true;
|
||||
|
||||
export class Portkey extends BaseLLM {
|
||||
apiKey?: string = undefined;
|
||||
baseURL?: string = undefined;
|
||||
mode?: string = undefined;
|
||||
@@ -813,6 +830,7 @@ export class Portkey implements LLM {
|
||||
callbackManager?: CallbackManager;
|
||||
|
||||
constructor(init?: Partial<Portkey>) {
|
||||
super();
|
||||
this.apiKey = init?.apiKey;
|
||||
this.baseURL = init?.baseURL;
|
||||
this.mode = init?.mode;
|
||||
@@ -834,50 +852,34 @@ export class Portkey implements LLM {
|
||||
throw new Error("metadata not implemented for Portkey");
|
||||
}
|
||||
|
||||
async chat<
|
||||
T extends boolean | undefined = undefined,
|
||||
R = T extends true ? AsyncGenerator<string, void, unknown> : ChatResponse,
|
||||
>(
|
||||
messages: ChatMessage[],
|
||||
parentEvent?: Event | undefined,
|
||||
streaming?: T,
|
||||
params?: Record<string, any>,
|
||||
): Promise<R> {
|
||||
if (streaming) {
|
||||
return this.streamChat(messages, parentEvent, params) as R;
|
||||
chat(
|
||||
params: LLMChatParamsStreaming,
|
||||
): Promise<AsyncIterable<ChatResponseChunk>>;
|
||||
chat(params: LLMChatParamsNonStreaming): Promise<ChatResponse>;
|
||||
async chat(
|
||||
params: LLMChatParamsNonStreaming | LLMChatParamsStreaming,
|
||||
): Promise<ChatResponse | AsyncIterable<ChatResponseChunk>> {
|
||||
const { messages, parentEvent, stream, extraParams } = params;
|
||||
if (stream) {
|
||||
return this.streamChat(messages, parentEvent, extraParams);
|
||||
} else {
|
||||
const resolvedParams = params || {};
|
||||
const bodyParams = extraParams || {};
|
||||
const response = await this.session.portkey.chatCompletions.create({
|
||||
messages,
|
||||
...resolvedParams,
|
||||
...bodyParams,
|
||||
});
|
||||
|
||||
const content = response.choices[0].message?.content ?? "";
|
||||
const role = response.choices[0].message?.role || "assistant";
|
||||
return { message: { content, role: role as MessageType } } as R;
|
||||
return { message: { content, role: role as MessageType } };
|
||||
}
|
||||
}
|
||||
|
||||
async complete<
|
||||
T extends boolean | undefined = undefined,
|
||||
R = T extends true ? AsyncGenerator<string, void, unknown> : ChatResponse,
|
||||
>(
|
||||
prompt: string,
|
||||
parentEvent?: Event | undefined,
|
||||
streaming?: T,
|
||||
): Promise<R> {
|
||||
return this.chat(
|
||||
[{ content: prompt, role: "user" }],
|
||||
parentEvent,
|
||||
streaming,
|
||||
);
|
||||
}
|
||||
|
||||
async *streamChat(
|
||||
messages: ChatMessage[],
|
||||
parentEvent?: Event,
|
||||
params?: Record<string, any>,
|
||||
): AsyncGenerator<string, void, unknown> {
|
||||
): AsyncIterable<ChatResponseChunk> {
|
||||
// Wrapping the stream in a callback.
|
||||
const onLLMStream = this.callbackManager?.onLLMStream
|
||||
? this.callbackManager.onLLMStream
|
||||
@@ -915,15 +917,8 @@ export class Portkey implements LLM {
|
||||
|
||||
idx_counter++;
|
||||
|
||||
yield part.choices[0].delta?.content ?? "";
|
||||
yield { delta: part.choices[0].delta?.content ?? "" };
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
streamComplete(
|
||||
query: string,
|
||||
parentEvent?: Event,
|
||||
): AsyncGenerator<string, void, unknown> {
|
||||
return this.streamChat([{ content: query, role: "user" }], parentEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from "./LLM";
|
||||
export * from "./mistral";
|
||||
export { Ollama } from "./ollama";
|
||||
export { TogetherLLM } from "./together";
|
||||
|
||||
@@ -4,7 +4,14 @@ import {
|
||||
EventType,
|
||||
StreamCallbackResponse,
|
||||
} from "../callbacks/CallbackManager";
|
||||
import { ChatMessage, ChatResponse, LLM } from "./LLM";
|
||||
import {
|
||||
BaseLLM,
|
||||
ChatMessage,
|
||||
ChatResponse,
|
||||
ChatResponseChunk,
|
||||
LLMChatParamsNonStreaming,
|
||||
LLMChatParamsStreaming,
|
||||
} from "./LLM";
|
||||
|
||||
export const ALL_AVAILABLE_MISTRAL_MODELS = {
|
||||
"mistral-tiny": { contextWindow: 32000 },
|
||||
@@ -41,9 +48,7 @@ export class MistralAISession {
|
||||
/**
|
||||
* MistralAI LLM implementation
|
||||
*/
|
||||
export class MistralAI implements LLM {
|
||||
hasStreaming: boolean = true;
|
||||
|
||||
export class MistralAI extends BaseLLM {
|
||||
// Per completion MistralAI params
|
||||
model: keyof typeof ALL_AVAILABLE_MISTRAL_MODELS;
|
||||
temperature: number;
|
||||
@@ -57,6 +62,7 @@ export class MistralAI implements LLM {
|
||||
private session: MistralAISession;
|
||||
|
||||
constructor(init?: Partial<MistralAI>) {
|
||||
super();
|
||||
this.model = init?.model ?? "mistral-small";
|
||||
this.temperature = init?.temperature ?? 0.1;
|
||||
this.topP = init?.topP ?? 1;
|
||||
@@ -94,16 +100,17 @@ export class MistralAI implements LLM {
|
||||
};
|
||||
}
|
||||
|
||||
async chat<
|
||||
T extends boolean | undefined = undefined,
|
||||
R = T extends true ? AsyncGenerator<string, void, unknown> : ChatResponse,
|
||||
>(messages: ChatMessage[], parentEvent?: Event, streaming?: T): Promise<R> {
|
||||
chat(
|
||||
params: LLMChatParamsStreaming,
|
||||
): Promise<AsyncIterable<ChatResponseChunk>>;
|
||||
chat(params: LLMChatParamsNonStreaming): Promise<ChatResponse>;
|
||||
async chat(
|
||||
params: LLMChatParamsNonStreaming | LLMChatParamsStreaming,
|
||||
): Promise<ChatResponse | AsyncIterable<ChatResponseChunk>> {
|
||||
const { messages, stream } = params;
|
||||
// Streaming
|
||||
if (streaming) {
|
||||
if (!this.hasStreaming) {
|
||||
throw Error("No streaming support for this LLM.");
|
||||
}
|
||||
return this.streamChat(messages, parentEvent) as R;
|
||||
if (stream) {
|
||||
return this.streamChat(params);
|
||||
}
|
||||
// Non-streaming
|
||||
const client = await this.session.getClient();
|
||||
@@ -111,24 +118,13 @@ export class MistralAI implements LLM {
|
||||
const message = response.choices[0].message;
|
||||
return {
|
||||
message,
|
||||
} as R;
|
||||
};
|
||||
}
|
||||
|
||||
async complete<
|
||||
T extends boolean | undefined = undefined,
|
||||
R = T extends true ? AsyncGenerator<string, void, unknown> : ChatResponse,
|
||||
>(prompt: string, parentEvent?: Event, streaming?: T): Promise<R> {
|
||||
return this.chat(
|
||||
[{ content: prompt, role: "user" }],
|
||||
parentEvent,
|
||||
streaming,
|
||||
);
|
||||
}
|
||||
|
||||
protected async *streamChat(
|
||||
messages: ChatMessage[],
|
||||
parentEvent?: Event,
|
||||
): AsyncGenerator<string, void, unknown> {
|
||||
protected async *streamChat({
|
||||
messages,
|
||||
parentEvent,
|
||||
}: LLMChatParamsStreaming): AsyncIterable<ChatResponseChunk> {
|
||||
//Now let's wrap our stream in a callback
|
||||
const onLLMStream = this.callbackManager?.onLLMStream
|
||||
? this.callbackManager.onLLMStream
|
||||
@@ -163,16 +159,10 @@ export class MistralAI implements LLM {
|
||||
|
||||
idx_counter++;
|
||||
|
||||
yield part.choices[0].delta.content ?? "";
|
||||
yield {
|
||||
delta: part.choices[0].delta.content ?? "",
|
||||
};
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//streamComplete doesn't need to be async because it's child function is already async
|
||||
protected streamComplete(
|
||||
query: string,
|
||||
parentEvent?: Event,
|
||||
): AsyncGenerator<string, void, unknown> {
|
||||
return this.streamChat([{ content: query, role: "user" }], parentEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,27 @@
|
||||
import { ok } from "node:assert";
|
||||
import { MessageContent } from "../ChatEngine";
|
||||
import { CallbackManager, Event } from "../callbacks/CallbackManager";
|
||||
import { BaseEmbedding } from "../embeddings";
|
||||
import { ChatMessage, ChatResponse, LLM, LLMMetadata } from "./LLM";
|
||||
import {
|
||||
ChatMessage,
|
||||
ChatResponse,
|
||||
ChatResponseChunk,
|
||||
CompletionResponse,
|
||||
LLM,
|
||||
LLMChatParamsNonStreaming,
|
||||
LLMChatParamsStreaming,
|
||||
LLMCompletionParamsNonStreaming,
|
||||
LLMCompletionParamsStreaming,
|
||||
LLMMetadata,
|
||||
} from "./LLM";
|
||||
|
||||
const messageAccessor = (data: any) => data.message.content;
|
||||
const completionAccessor = (data: any) => data.response;
|
||||
const messageAccessor = (data: any): ChatResponseChunk => {
|
||||
return {
|
||||
delta: data.message.content,
|
||||
};
|
||||
};
|
||||
const completionAccessor = (data: any): CompletionResponse => {
|
||||
return { text: data.response };
|
||||
};
|
||||
|
||||
// https://github.com/jmorganca/ollama
|
||||
export class Ollama extends BaseEmbedding implements LLM {
|
||||
@@ -43,21 +59,21 @@ export class Ollama extends BaseEmbedding implements LLM {
|
||||
};
|
||||
}
|
||||
|
||||
async chat<
|
||||
T extends boolean | undefined = undefined,
|
||||
R = T extends true ? AsyncGenerator<string, void, unknown> : ChatResponse,
|
||||
>(
|
||||
messages: ChatMessage[],
|
||||
parentEvent?: Event | undefined,
|
||||
streaming?: T,
|
||||
): Promise<R> {
|
||||
chat(
|
||||
params: LLMChatParamsStreaming,
|
||||
): Promise<AsyncIterable<ChatResponseChunk>>;
|
||||
chat(params: LLMChatParamsNonStreaming): Promise<ChatResponse>;
|
||||
async chat(
|
||||
params: LLMChatParamsNonStreaming | LLMChatParamsStreaming,
|
||||
): Promise<ChatResponse | AsyncIterable<ChatResponseChunk>> {
|
||||
const { messages, parentEvent, stream } = params;
|
||||
const payload = {
|
||||
model: this.model,
|
||||
messages: messages.map((message) => ({
|
||||
role: message.role,
|
||||
content: message.content,
|
||||
})),
|
||||
stream: !!streaming,
|
||||
stream: !!stream,
|
||||
options: {
|
||||
temperature: this.temperature,
|
||||
num_ctx: this.contextWindow,
|
||||
@@ -73,7 +89,7 @@ export class Ollama extends BaseEmbedding implements LLM {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
if (!streaming) {
|
||||
if (!stream) {
|
||||
const raw = await response.json();
|
||||
const { message } = raw;
|
||||
return {
|
||||
@@ -82,20 +98,20 @@ export class Ollama extends BaseEmbedding implements LLM {
|
||||
content: message.content,
|
||||
},
|
||||
raw,
|
||||
} satisfies ChatResponse as R;
|
||||
};
|
||||
} else {
|
||||
const stream = response.body;
|
||||
ok(stream, "stream is null");
|
||||
ok(stream instanceof ReadableStream, "stream is not readable");
|
||||
return this.streamChat(stream, messageAccessor, parentEvent) as R;
|
||||
return this.streamChat(stream, messageAccessor, parentEvent);
|
||||
}
|
||||
}
|
||||
|
||||
private async *streamChat(
|
||||
private async *streamChat<T>(
|
||||
stream: ReadableStream<Uint8Array>,
|
||||
accessor: (data: any) => string,
|
||||
accessor: (data: any) => T,
|
||||
parentEvent?: Event,
|
||||
): AsyncGenerator<string, void, unknown> {
|
||||
): AsyncIterable<T> {
|
||||
const reader = stream.getReader();
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
@@ -119,18 +135,20 @@ export class Ollama extends BaseEmbedding implements LLM {
|
||||
}
|
||||
}
|
||||
|
||||
async complete<
|
||||
T extends boolean | undefined = undefined,
|
||||
R = T extends true ? AsyncGenerator<string, void, unknown> : ChatResponse,
|
||||
>(
|
||||
prompt: MessageContent,
|
||||
parentEvent?: Event | undefined,
|
||||
streaming?: T | undefined,
|
||||
): Promise<R> {
|
||||
complete(
|
||||
params: LLMCompletionParamsStreaming,
|
||||
): Promise<AsyncIterable<CompletionResponse>>;
|
||||
complete(
|
||||
params: LLMCompletionParamsNonStreaming,
|
||||
): Promise<CompletionResponse>;
|
||||
async complete(
|
||||
params: LLMCompletionParamsStreaming | LLMCompletionParamsNonStreaming,
|
||||
): Promise<CompletionResponse | AsyncIterable<CompletionResponse>> {
|
||||
const { prompt, parentEvent, stream } = params;
|
||||
const payload = {
|
||||
model: this.model,
|
||||
prompt: prompt,
|
||||
stream: !!streaming,
|
||||
stream: !!stream,
|
||||
options: {
|
||||
temperature: this.temperature,
|
||||
num_ctx: this.contextWindow,
|
||||
@@ -146,20 +164,17 @@ export class Ollama extends BaseEmbedding implements LLM {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
if (!streaming) {
|
||||
if (!stream) {
|
||||
const raw = await response.json();
|
||||
return {
|
||||
message: {
|
||||
role: "assistant",
|
||||
content: raw.response,
|
||||
},
|
||||
text: raw.response,
|
||||
raw,
|
||||
} satisfies ChatResponse as R;
|
||||
};
|
||||
} else {
|
||||
const stream = response.body;
|
||||
ok(stream, "stream is null");
|
||||
ok(stream instanceof ReadableStream, "stream is not readable");
|
||||
return this.streamChat(stream, completionAccessor, parentEvent) as R;
|
||||
return this.streamChat(stream, completionAccessor, parentEvent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { OpenAI } from "./LLM";
|
||||
|
||||
export class TogetherLLM extends OpenAI {
|
||||
constructor(init?: Partial<OpenAI>) {
|
||||
super({
|
||||
...init,
|
||||
apiKey: process.env.TOGETHER_API_KEY,
|
||||
additionalSessionOptions: {
|
||||
...init?.additionalSessionOptions,
|
||||
baseURL: "https://api.together.xyz/v1",
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
export async function* streamConverter<S, D>(
|
||||
stream: AsyncIterable<S>,
|
||||
converter: (s: S) => D,
|
||||
): AsyncIterable<D> {
|
||||
for await (const data of stream) {
|
||||
yield converter(data);
|
||||
}
|
||||
}
|
||||
|
||||
export async function* streamReducer<S, D>(params: {
|
||||
stream: AsyncIterable<S>;
|
||||
reducer: (previousValue: D, currentValue: S) => D;
|
||||
initialValue: D;
|
||||
finished?: (value: D | undefined) => void;
|
||||
}): AsyncIterable<S> {
|
||||
let value = params.initialValue;
|
||||
for await (const data of params.stream) {
|
||||
value = params.reducer(value, data);
|
||||
yield data;
|
||||
}
|
||||
if (params.finished) {
|
||||
params.finished(value);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as path from "path";
|
||||
import path from "path";
|
||||
import { GenericFileSystem } from "./FileSystem";
|
||||
import {
|
||||
DEFAULT_FS,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import _ from "lodash";
|
||||
import * as path from "path";
|
||||
import path from "path";
|
||||
import { GenericFileSystem } from "../FileSystem";
|
||||
import {
|
||||
DEFAULT_DOC_STORE_PERSIST_FILENAME,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as path from "path";
|
||||
import path from "path";
|
||||
import { GenericFileSystem } from "../FileSystem";
|
||||
import {
|
||||
DEFAULT_FS,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as _ from "lodash";
|
||||
import * as path from "path";
|
||||
import _ from "lodash";
|
||||
import path from "path";
|
||||
import { GenericFileSystem, exists } from "../FileSystem";
|
||||
import { DEFAULT_COLLECTION, DEFAULT_FS } from "../constants";
|
||||
import { BaseKVStore } from "./types";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import _ from "lodash";
|
||||
import * as path from "path";
|
||||
import path from "path";
|
||||
import { BaseNode } from "../../Node";
|
||||
import {
|
||||
getTopKEmbeddings,
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
import { MessageContentDetail } from "../ChatEngine";
|
||||
import {
|
||||
ImageNode,
|
||||
MetadataMode,
|
||||
NodeWithScore,
|
||||
splitNodesByType,
|
||||
} from "../Node";
|
||||
import { ImageNode, MetadataMode, splitNodesByType } from "../Node";
|
||||
import { Response } from "../Response";
|
||||
import { ServiceContext, serviceContextFromDefaults } from "../ServiceContext";
|
||||
import { Event } from "../callbacks/CallbackManager";
|
||||
import { imageToDataUrl } from "../embeddings";
|
||||
import { TextQaPrompt, defaultTextQaPrompt } from "./../Prompt";
|
||||
import { BaseSynthesizer } from "./types";
|
||||
import {
|
||||
BaseSynthesizer,
|
||||
SynthesizeParamsNonStreaming,
|
||||
SynthesizeParamsStreaming,
|
||||
} from "./types";
|
||||
|
||||
export class MultiModalResponseSynthesizer implements BaseSynthesizer {
|
||||
serviceContext: ServiceContext;
|
||||
@@ -27,11 +25,21 @@ export class MultiModalResponseSynthesizer implements BaseSynthesizer {
|
||||
this.textQATemplate = textQATemplate ?? defaultTextQaPrompt;
|
||||
}
|
||||
|
||||
async synthesize(
|
||||
query: string,
|
||||
nodesWithScore: NodeWithScore[],
|
||||
parentEvent?: Event,
|
||||
): Promise<Response> {
|
||||
synthesize(
|
||||
params: SynthesizeParamsStreaming,
|
||||
): Promise<AsyncIterable<Response>>;
|
||||
synthesize(params: SynthesizeParamsNonStreaming): Promise<Response>;
|
||||
async synthesize({
|
||||
query,
|
||||
nodesWithScore,
|
||||
parentEvent,
|
||||
stream,
|
||||
}: SynthesizeParamsStreaming | SynthesizeParamsNonStreaming): Promise<
|
||||
AsyncIterable<Response> | Response
|
||||
> {
|
||||
if (stream) {
|
||||
throw new Error("streaming not implemented");
|
||||
}
|
||||
const nodes = nodesWithScore.map(({ node }) => node);
|
||||
const { imageNodes, textNodes } = splitNodesByType(nodes);
|
||||
const textChunks = textNodes.map((node) =>
|
||||
@@ -54,7 +62,10 @@ export class MultiModalResponseSynthesizer implements BaseSynthesizer {
|
||||
{ type: "text", text: textPrompt },
|
||||
...images,
|
||||
];
|
||||
let response = await this.serviceContext.llm.complete(prompt, parentEvent);
|
||||
return new Response(response.message.content, nodes);
|
||||
let response = await this.serviceContext.llm.complete({
|
||||
prompt,
|
||||
parentEvent,
|
||||
});
|
||||
return new Response(response.text, nodes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
import { Event } from "../callbacks/CallbackManager";
|
||||
import { MetadataMode, NodeWithScore } from "../Node";
|
||||
import { MetadataMode } from "../Node";
|
||||
import { Response } from "../Response";
|
||||
import { ServiceContext, serviceContextFromDefaults } from "../ServiceContext";
|
||||
import { BaseResponseBuilder, getResponseBuilder } from "./builders";
|
||||
import { BaseSynthesizer } from "./types";
|
||||
import { streamConverter } from "../llm/utils";
|
||||
import { getResponseBuilder } from "./builders";
|
||||
import {
|
||||
BaseSynthesizer,
|
||||
ResponseBuilder,
|
||||
SynthesizeParamsNonStreaming,
|
||||
SynthesizeParamsStreaming,
|
||||
} from "./types";
|
||||
|
||||
/**
|
||||
* A ResponseSynthesizer is used to generate a response from a query and a list of nodes.
|
||||
*/
|
||||
export class ResponseSynthesizer implements BaseSynthesizer {
|
||||
responseBuilder: BaseResponseBuilder;
|
||||
responseBuilder: ResponseBuilder;
|
||||
serviceContext: ServiceContext;
|
||||
metadataMode: MetadataMode;
|
||||
|
||||
@@ -18,7 +23,7 @@ export class ResponseSynthesizer implements BaseSynthesizer {
|
||||
serviceContext,
|
||||
metadataMode = MetadataMode.NONE,
|
||||
}: {
|
||||
responseBuilder?: BaseResponseBuilder;
|
||||
responseBuilder?: ResponseBuilder;
|
||||
serviceContext?: ServiceContext;
|
||||
metadataMode?: MetadataMode;
|
||||
} = {}) {
|
||||
@@ -28,22 +33,36 @@ export class ResponseSynthesizer implements BaseSynthesizer {
|
||||
this.metadataMode = metadataMode;
|
||||
}
|
||||
|
||||
async synthesize(
|
||||
query: string,
|
||||
nodesWithScore: NodeWithScore[],
|
||||
parentEvent?: Event,
|
||||
) {
|
||||
let textChunks: string[] = nodesWithScore.map(({ node }) =>
|
||||
synthesize(
|
||||
params: SynthesizeParamsStreaming,
|
||||
): Promise<AsyncIterable<Response>>;
|
||||
synthesize(params: SynthesizeParamsNonStreaming): Promise<Response>;
|
||||
async synthesize({
|
||||
query,
|
||||
nodesWithScore,
|
||||
parentEvent,
|
||||
stream,
|
||||
}: SynthesizeParamsStreaming | SynthesizeParamsNonStreaming): Promise<
|
||||
AsyncIterable<Response> | Response
|
||||
> {
|
||||
const textChunks: string[] = nodesWithScore.map(({ node }) =>
|
||||
node.getContent(this.metadataMode),
|
||||
);
|
||||
const response = await this.responseBuilder.getResponse(
|
||||
const nodes = nodesWithScore.map(({ node }) => node);
|
||||
if (stream) {
|
||||
const response = await this.responseBuilder.getResponse({
|
||||
query,
|
||||
textChunks,
|
||||
parentEvent,
|
||||
stream,
|
||||
});
|
||||
return streamConverter(response, (chunk) => new Response(chunk, nodes));
|
||||
}
|
||||
const response = await this.responseBuilder.getResponse({
|
||||
query,
|
||||
textChunks,
|
||||
parentEvent,
|
||||
);
|
||||
return new Response(
|
||||
response,
|
||||
nodesWithScore.map(({ node }) => node),
|
||||
);
|
||||
});
|
||||
return new Response(response, nodes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Event } from "../callbacks/CallbackManager";
|
||||
import { LLM } from "../llm/LLM";
|
||||
import { LLM } from "../llm";
|
||||
import { streamConverter } from "../llm/utils";
|
||||
import {
|
||||
defaultRefinePrompt,
|
||||
defaultTextQaPrompt,
|
||||
@@ -9,8 +10,13 @@ import {
|
||||
TextQaPrompt,
|
||||
TreeSummarizePrompt,
|
||||
} from "../Prompt";
|
||||
import { getBiggestPrompt } from "../PromptHelper";
|
||||
import { getBiggestPrompt, PromptHelper } from "../PromptHelper";
|
||||
import { ServiceContext } from "../ServiceContext";
|
||||
import {
|
||||
ResponseBuilder,
|
||||
ResponseBuilderParamsNonStreaming,
|
||||
ResponseBuilderParamsStreaming,
|
||||
} from "./types";
|
||||
|
||||
/**
|
||||
* Response modes of the response synthesizer
|
||||
@@ -22,29 +28,10 @@ enum ResponseMode {
|
||||
SIMPLE = "simple",
|
||||
}
|
||||
|
||||
/**
|
||||
* A ResponseBuilder is used in a response synthesizer to generate a response from multiple response chunks.
|
||||
*/
|
||||
export interface BaseResponseBuilder {
|
||||
/**
|
||||
* Get the response from a query and a list of text chunks.
|
||||
* @param query
|
||||
* @param textChunks
|
||||
* @param parentEvent
|
||||
* @param prevResponse
|
||||
*/
|
||||
getResponse(
|
||||
query: string,
|
||||
textChunks: string[],
|
||||
parentEvent?: Event,
|
||||
prevResponse?: string,
|
||||
): Promise<string>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A response builder that just concatenates responses.
|
||||
*/
|
||||
export class SimpleResponseBuilder implements BaseResponseBuilder {
|
||||
export class SimpleResponseBuilder implements ResponseBuilder {
|
||||
llm: LLM;
|
||||
textQATemplate: SimplePrompt;
|
||||
|
||||
@@ -53,27 +40,42 @@ export class SimpleResponseBuilder implements BaseResponseBuilder {
|
||||
this.textQATemplate = defaultTextQaPrompt;
|
||||
}
|
||||
|
||||
async getResponse(
|
||||
query: string,
|
||||
textChunks: string[],
|
||||
parentEvent?: Event,
|
||||
): Promise<string> {
|
||||
getResponse(
|
||||
params: ResponseBuilderParamsStreaming,
|
||||
): Promise<AsyncIterable<string>>;
|
||||
getResponse(params: ResponseBuilderParamsNonStreaming): Promise<string>;
|
||||
async getResponse({
|
||||
query,
|
||||
textChunks,
|
||||
parentEvent,
|
||||
stream,
|
||||
}:
|
||||
| ResponseBuilderParamsStreaming
|
||||
| ResponseBuilderParamsNonStreaming): Promise<
|
||||
AsyncIterable<string> | string
|
||||
> {
|
||||
const input = {
|
||||
query,
|
||||
context: textChunks.join("\n\n"),
|
||||
};
|
||||
|
||||
const prompt = this.textQATemplate(input);
|
||||
const response = await this.llm.complete(prompt, parentEvent);
|
||||
return response.message.content;
|
||||
if (stream) {
|
||||
const response = await this.llm.complete({ prompt, parentEvent, stream });
|
||||
return streamConverter(response, (chunk) => chunk.text);
|
||||
} else {
|
||||
const response = await this.llm.complete({ prompt, parentEvent, stream });
|
||||
return response.text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A response builder that uses the query to ask the LLM generate a better response using multiple text chunks.
|
||||
*/
|
||||
export class Refine implements BaseResponseBuilder {
|
||||
serviceContext: ServiceContext;
|
||||
export class Refine implements ResponseBuilder {
|
||||
llm: LLM;
|
||||
promptHelper: PromptHelper;
|
||||
textQATemplate: TextQaPrompt;
|
||||
refineTemplate: RefinePrompt;
|
||||
|
||||
@@ -82,31 +84,48 @@ export class Refine implements BaseResponseBuilder {
|
||||
textQATemplate?: TextQaPrompt,
|
||||
refineTemplate?: RefinePrompt,
|
||||
) {
|
||||
this.serviceContext = serviceContext;
|
||||
this.llm = serviceContext.llm;
|
||||
this.promptHelper = serviceContext.promptHelper;
|
||||
this.textQATemplate = textQATemplate ?? defaultTextQaPrompt;
|
||||
this.refineTemplate = refineTemplate ?? defaultRefinePrompt;
|
||||
}
|
||||
|
||||
async getResponse(
|
||||
query: string,
|
||||
textChunks: string[],
|
||||
parentEvent?: Event,
|
||||
prevResponse?: string,
|
||||
): Promise<string> {
|
||||
let response: string | undefined = undefined;
|
||||
getResponse(
|
||||
params: ResponseBuilderParamsStreaming,
|
||||
): Promise<AsyncIterable<string>>;
|
||||
getResponse(params: ResponseBuilderParamsNonStreaming): Promise<string>;
|
||||
async getResponse({
|
||||
query,
|
||||
textChunks,
|
||||
parentEvent,
|
||||
prevResponse,
|
||||
stream,
|
||||
}:
|
||||
| ResponseBuilderParamsStreaming
|
||||
| ResponseBuilderParamsNonStreaming): Promise<
|
||||
AsyncIterable<string> | string
|
||||
> {
|
||||
let response: AsyncIterable<string> | string | undefined = prevResponse;
|
||||
|
||||
for (const chunk of textChunks) {
|
||||
if (!prevResponse) {
|
||||
response = await this.giveResponseSingle(query, chunk, parentEvent);
|
||||
} else {
|
||||
response = await this.refineResponseSingle(
|
||||
prevResponse,
|
||||
for (let i = 0; i < textChunks.length; i++) {
|
||||
const chunk = textChunks[i];
|
||||
const lastChunk = i === textChunks.length - 1;
|
||||
if (!response) {
|
||||
response = await this.giveResponseSingle(
|
||||
query,
|
||||
chunk,
|
||||
!!stream && lastChunk,
|
||||
parentEvent,
|
||||
);
|
||||
} else {
|
||||
response = await this.refineResponseSingle(
|
||||
response as string,
|
||||
query,
|
||||
chunk,
|
||||
!!stream && lastChunk,
|
||||
parentEvent,
|
||||
);
|
||||
}
|
||||
prevResponse = response;
|
||||
}
|
||||
|
||||
return response ?? "Empty Response";
|
||||
@@ -115,153 +134,204 @@ export class Refine implements BaseResponseBuilder {
|
||||
private async giveResponseSingle(
|
||||
queryStr: string,
|
||||
textChunk: string,
|
||||
stream: boolean,
|
||||
parentEvent?: Event,
|
||||
): Promise<string> {
|
||||
) {
|
||||
const textQATemplate: SimplePrompt = (input) =>
|
||||
this.textQATemplate({ ...input, query: queryStr });
|
||||
const textChunks = this.serviceContext.promptHelper.repack(textQATemplate, [
|
||||
textChunk,
|
||||
]);
|
||||
const textChunks = this.promptHelper.repack(textQATemplate, [textChunk]);
|
||||
|
||||
let response: string | undefined = undefined;
|
||||
let response: AsyncIterable<string> | string | undefined = undefined;
|
||||
|
||||
for (const chunk of textChunks) {
|
||||
for (let i = 0; i < textChunks.length; i++) {
|
||||
const chunk = textChunks[i];
|
||||
const lastChunk = i === textChunks.length - 1;
|
||||
if (!response) {
|
||||
response = (
|
||||
await this.serviceContext.llm.complete(
|
||||
textQATemplate({
|
||||
context: chunk,
|
||||
}),
|
||||
parentEvent,
|
||||
)
|
||||
).message.content;
|
||||
response = await this.complete({
|
||||
prompt: textQATemplate({
|
||||
context: chunk,
|
||||
}),
|
||||
parentEvent,
|
||||
stream: stream && lastChunk,
|
||||
});
|
||||
} else {
|
||||
response = await this.refineResponseSingle(
|
||||
response,
|
||||
response as string,
|
||||
queryStr,
|
||||
chunk,
|
||||
stream && lastChunk,
|
||||
parentEvent,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return response ?? "Empty Response";
|
||||
return response;
|
||||
}
|
||||
|
||||
private async refineResponseSingle(
|
||||
response: string,
|
||||
initialReponse: string,
|
||||
queryStr: string,
|
||||
textChunk: string,
|
||||
stream: boolean,
|
||||
parentEvent?: Event,
|
||||
) {
|
||||
const refineTemplate: SimplePrompt = (input) =>
|
||||
this.refineTemplate({ ...input, query: queryStr });
|
||||
|
||||
const textChunks = this.serviceContext.promptHelper.repack(refineTemplate, [
|
||||
textChunk,
|
||||
]);
|
||||
const textChunks = this.promptHelper.repack(refineTemplate, [textChunk]);
|
||||
|
||||
for (const chunk of textChunks) {
|
||||
response = (
|
||||
await this.serviceContext.llm.complete(
|
||||
refineTemplate({
|
||||
context: chunk,
|
||||
existingAnswer: response,
|
||||
}),
|
||||
parentEvent,
|
||||
)
|
||||
).message.content;
|
||||
let response: AsyncIterable<string> | string = initialReponse;
|
||||
|
||||
for (let i = 0; i < textChunks.length; i++) {
|
||||
const chunk = textChunks[i];
|
||||
const lastChunk = i === textChunks.length - 1;
|
||||
response = await this.complete({
|
||||
prompt: refineTemplate({
|
||||
context: chunk,
|
||||
existingAnswer: response as string,
|
||||
}),
|
||||
parentEvent,
|
||||
stream: stream && lastChunk,
|
||||
});
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
async complete(params: {
|
||||
prompt: string;
|
||||
stream: boolean;
|
||||
parentEvent?: Event;
|
||||
}): Promise<AsyncIterable<string> | string> {
|
||||
if (params.stream) {
|
||||
const response = await this.llm.complete({ ...params, stream: true });
|
||||
return streamConverter(response, (chunk) => chunk.text);
|
||||
} else {
|
||||
const response = await this.llm.complete({ ...params, stream: false });
|
||||
return response.text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* CompactAndRefine is a slight variation of Refine that first compacts the text chunks into the smallest possible number of chunks.
|
||||
*/
|
||||
export class CompactAndRefine extends Refine {
|
||||
async getResponse(
|
||||
query: string,
|
||||
textChunks: string[],
|
||||
parentEvent?: Event,
|
||||
prevResponse?: string,
|
||||
): Promise<string> {
|
||||
getResponse(
|
||||
params: ResponseBuilderParamsStreaming,
|
||||
): Promise<AsyncIterable<string>>;
|
||||
getResponse(params: ResponseBuilderParamsNonStreaming): Promise<string>;
|
||||
async getResponse({
|
||||
query,
|
||||
textChunks,
|
||||
parentEvent,
|
||||
prevResponse,
|
||||
stream,
|
||||
}:
|
||||
| ResponseBuilderParamsStreaming
|
||||
| ResponseBuilderParamsNonStreaming): Promise<
|
||||
AsyncIterable<string> | string
|
||||
> {
|
||||
const textQATemplate: SimplePrompt = (input) =>
|
||||
this.textQATemplate({ ...input, query: query });
|
||||
const refineTemplate: SimplePrompt = (input) =>
|
||||
this.refineTemplate({ ...input, query: query });
|
||||
|
||||
const maxPrompt = getBiggestPrompt([textQATemplate, refineTemplate]);
|
||||
const newTexts = this.serviceContext.promptHelper.repack(
|
||||
maxPrompt,
|
||||
textChunks,
|
||||
);
|
||||
const response = super.getResponse(
|
||||
const newTexts = this.promptHelper.repack(maxPrompt, textChunks);
|
||||
const params = {
|
||||
query,
|
||||
newTexts,
|
||||
textChunks: newTexts,
|
||||
parentEvent,
|
||||
prevResponse,
|
||||
);
|
||||
return response;
|
||||
};
|
||||
if (stream) {
|
||||
return super.getResponse({
|
||||
...params,
|
||||
stream,
|
||||
});
|
||||
}
|
||||
return super.getResponse(params);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
export class TreeSummarize implements BaseResponseBuilder {
|
||||
serviceContext: ServiceContext;
|
||||
export class TreeSummarize implements ResponseBuilder {
|
||||
llm: LLM;
|
||||
promptHelper: PromptHelper;
|
||||
summaryTemplate: TreeSummarizePrompt;
|
||||
|
||||
constructor(
|
||||
serviceContext: ServiceContext,
|
||||
summaryTemplate?: TreeSummarizePrompt,
|
||||
) {
|
||||
this.serviceContext = serviceContext;
|
||||
this.llm = serviceContext.llm;
|
||||
this.promptHelper = serviceContext.promptHelper;
|
||||
this.summaryTemplate = summaryTemplate ?? defaultTreeSummarizePrompt;
|
||||
}
|
||||
|
||||
async getResponse(
|
||||
query: string,
|
||||
textChunks: string[],
|
||||
parentEvent?: Event,
|
||||
): Promise<string> {
|
||||
getResponse(
|
||||
params: ResponseBuilderParamsStreaming,
|
||||
): Promise<AsyncIterable<string>>;
|
||||
getResponse(params: ResponseBuilderParamsNonStreaming): Promise<string>;
|
||||
async getResponse({
|
||||
query,
|
||||
textChunks,
|
||||
parentEvent,
|
||||
stream,
|
||||
}:
|
||||
| ResponseBuilderParamsStreaming
|
||||
| ResponseBuilderParamsNonStreaming): Promise<
|
||||
AsyncIterable<string> | string
|
||||
> {
|
||||
if (!textChunks || textChunks.length === 0) {
|
||||
throw new Error("Must have at least one text chunk");
|
||||
}
|
||||
|
||||
// Should we send the query here too?
|
||||
const packedTextChunks = this.serviceContext.promptHelper.repack(
|
||||
const packedTextChunks = this.promptHelper.repack(
|
||||
this.summaryTemplate,
|
||||
textChunks,
|
||||
);
|
||||
|
||||
if (packedTextChunks.length === 1) {
|
||||
return (
|
||||
await this.serviceContext.llm.complete(
|
||||
this.summaryTemplate({
|
||||
context: packedTextChunks[0],
|
||||
query,
|
||||
}),
|
||||
parentEvent,
|
||||
)
|
||||
).message.content;
|
||||
const params = {
|
||||
prompt: this.summaryTemplate({
|
||||
context: packedTextChunks[0],
|
||||
query,
|
||||
}),
|
||||
parentEvent,
|
||||
};
|
||||
if (stream) {
|
||||
const response = await this.llm.complete({ ...params, stream });
|
||||
return streamConverter(response, (chunk) => chunk.text);
|
||||
}
|
||||
return (await this.llm.complete(params)).text;
|
||||
} else {
|
||||
const summaries = await Promise.all(
|
||||
packedTextChunks.map((chunk) =>
|
||||
this.serviceContext.llm.complete(
|
||||
this.summaryTemplate({
|
||||
this.llm.complete({
|
||||
prompt: this.summaryTemplate({
|
||||
context: chunk,
|
||||
query,
|
||||
}),
|
||||
parentEvent,
|
||||
),
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
return this.getResponse(
|
||||
const params = {
|
||||
query,
|
||||
summaries.map((s) => s.message.content),
|
||||
);
|
||||
textChunks: summaries.map((s) => s.text),
|
||||
};
|
||||
if (stream) {
|
||||
return this.getResponse({
|
||||
...params,
|
||||
stream,
|
||||
});
|
||||
}
|
||||
return this.getResponse(params);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -269,7 +339,7 @@ export class TreeSummarize implements BaseResponseBuilder {
|
||||
export function getResponseBuilder(
|
||||
serviceContext: ServiceContext,
|
||||
responseMode?: ResponseMode,
|
||||
): BaseResponseBuilder {
|
||||
): ResponseBuilder {
|
||||
switch (responseMode) {
|
||||
case ResponseMode.SIMPLE:
|
||||
return new SimpleResponseBuilder(serviceContext);
|
||||
|
||||
@@ -2,14 +2,57 @@ import { Event } from "../callbacks/CallbackManager";
|
||||
import { NodeWithScore } from "../Node";
|
||||
import { Response } from "../Response";
|
||||
|
||||
export interface SynthesizeParamsBase {
|
||||
query: string;
|
||||
nodesWithScore: NodeWithScore[];
|
||||
parentEvent?: Event;
|
||||
}
|
||||
|
||||
export interface SynthesizeParamsStreaming extends SynthesizeParamsBase {
|
||||
stream: true;
|
||||
}
|
||||
|
||||
export interface SynthesizeParamsNonStreaming extends SynthesizeParamsBase {
|
||||
stream?: false | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A BaseSynthesizer is used to generate a response from a query and a list of nodes.
|
||||
* TODO: convert response builders to implement this interface (similar to Python).
|
||||
*/
|
||||
export interface BaseSynthesizer {
|
||||
synthesize(
|
||||
query: string,
|
||||
nodesWithScore: NodeWithScore[],
|
||||
parentEvent?: Event,
|
||||
): Promise<Response>;
|
||||
params: SynthesizeParamsStreaming,
|
||||
): Promise<AsyncIterable<Response>>;
|
||||
synthesize(params: SynthesizeParamsNonStreaming): Promise<Response>;
|
||||
}
|
||||
|
||||
export interface ResponseBuilderParamsBase {
|
||||
query: string;
|
||||
textChunks: string[];
|
||||
parentEvent?: Event;
|
||||
prevResponse?: string;
|
||||
}
|
||||
|
||||
export interface ResponseBuilderParamsStreaming
|
||||
extends ResponseBuilderParamsBase {
|
||||
stream: true;
|
||||
}
|
||||
|
||||
export interface ResponseBuilderParamsNonStreaming
|
||||
extends ResponseBuilderParamsBase {
|
||||
stream?: false | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A ResponseBuilder is used in a response synthesizer to generate a response from multiple response chunks.
|
||||
*/
|
||||
export interface ResponseBuilder {
|
||||
/**
|
||||
* Get the response from a query and a list of text chunks.
|
||||
* @param params
|
||||
*/
|
||||
getResponse(
|
||||
params: ResponseBuilderParamsStreaming,
|
||||
): Promise<AsyncIterable<string>>;
|
||||
getResponse(params: ResponseBuilderParamsNonStreaming): Promise<string>;
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ describe("CallbackManager: onLLMStream and onRetrieve", () => {
|
||||
});
|
||||
const queryEngine = vectorStoreIndex.asQueryEngine();
|
||||
const query = "What is the author's name?";
|
||||
const response = await queryEngine.query(query);
|
||||
const response = await queryEngine.query({ query });
|
||||
expect(response.toString()).toBe("MOCK_TOKEN_1-MOCK_TOKEN_2");
|
||||
expect(streamCallbackData).toEqual([
|
||||
{
|
||||
@@ -145,7 +145,7 @@ describe("CallbackManager: onLLMStream and onRetrieve", () => {
|
||||
responseSynthesizer,
|
||||
});
|
||||
const query = "What is the author's name?";
|
||||
const response = await queryEngine.query(query);
|
||||
const response = await queryEngine.query({ query });
|
||||
expect(response.toString()).toBe("MOCK_TOKEN_1-MOCK_TOKEN_2");
|
||||
expect(streamCallbackData).toEqual([
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { CallbackManager, Event } from "../../callbacks/CallbackManager";
|
||||
import { CallbackManager } from "../../callbacks/CallbackManager";
|
||||
import { OpenAIEmbedding } from "../../embeddings";
|
||||
import { globalsHelper } from "../../GlobalsHelper";
|
||||
import { ChatMessage, OpenAI } from "../../llm/LLM";
|
||||
import { LLMChatParamsBase, OpenAI } from "../../llm/LLM";
|
||||
|
||||
export function mockLlmGeneration({
|
||||
languageModel,
|
||||
@@ -13,7 +13,7 @@ export function mockLlmGeneration({
|
||||
jest
|
||||
.spyOn(languageModel, "chat")
|
||||
.mockImplementation(
|
||||
async (messages: ChatMessage[], parentEvent?: Event) => {
|
||||
async ({ messages, parentEvent }: LLMChatParamsBase) => {
|
||||
const text = "MOCK_TOKEN_1-MOCK_TOKEN_2";
|
||||
const event = globalsHelper.createEvent({
|
||||
parentEvent,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"rootDir": ".",
|
||||
"outDir": "./lib/",
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"isolatedModules": true,
|
||||
@@ -10,8 +12,8 @@
|
||||
"strict": true,
|
||||
"lib": ["es2015", "dom"],
|
||||
"target": "ES2015",
|
||||
"resolveJsonModule": true,
|
||||
"typeRoots": ["./types", "./node_modules/@types"]
|
||||
"resolveJsonModule": true
|
||||
},
|
||||
"include": ["./src"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
# create-llama
|
||||
|
||||
## 0.0.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 8e124e5: feat: support showing image on chat message
|
||||
|
||||
## 0.0.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 2e6b36e: fix: re-organize file structure
|
||||
- 2b356c8: fix: relative path incorrect
|
||||
|
||||
## 0.0.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -9,8 +9,8 @@ import { makeDir } from "./helpers/make-dir";
|
||||
|
||||
import fs from "fs";
|
||||
import terminalLink from "terminal-link";
|
||||
import type { InstallTemplateArgs } from "./templates";
|
||||
import { installTemplate } from "./templates";
|
||||
import type { InstallTemplateArgs } from "./helpers";
|
||||
import { installTemplate } from "./helpers";
|
||||
|
||||
export type InstallAppArgs = Omit<
|
||||
InstallTemplateArgs,
|
||||
@@ -34,6 +34,7 @@ export async function createApp({
|
||||
communityProjectPath,
|
||||
vectorDb,
|
||||
externalPort,
|
||||
installDependencies,
|
||||
}: InstallAppArgs): Promise<void> {
|
||||
const root = path.resolve(appPath);
|
||||
|
||||
@@ -75,6 +76,7 @@ export async function createApp({
|
||||
communityProjectPath,
|
||||
vectorDb,
|
||||
externalPort,
|
||||
installDependencies,
|
||||
};
|
||||
|
||||
if (frontend) {
|
||||
@@ -94,7 +96,7 @@ export async function createApp({
|
||||
});
|
||||
// copy readme for fullstack
|
||||
await fs.promises.copyFile(
|
||||
path.join(__dirname, "templates", "README-fullstack.md"),
|
||||
path.join(__dirname, "..", "templates", "README-fullstack.md"),
|
||||
path.join(root, "README.md"),
|
||||
);
|
||||
} else {
|
||||
|
||||
@@ -8,7 +8,7 @@ import type {
|
||||
TemplateFramework,
|
||||
TemplateType,
|
||||
TemplateUI,
|
||||
} from "../templates";
|
||||
} from "../helpers";
|
||||
import { createTestDir, runApp, runCreateLlama, type AppType } from "./utils";
|
||||
|
||||
const templateTypes: TemplateType[] = ["streaming", "simple"];
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./lib/.e2e.tsbuildinfo"
|
||||
},
|
||||
"include": ["./**/*.ts"],
|
||||
"references": [
|
||||
{
|
||||
"path": ".."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -104,6 +104,7 @@ export function runCreateLlama(
|
||||
"--use-npm",
|
||||
"--external-port",
|
||||
externalPort,
|
||||
"--install-dependencies",
|
||||
].join(" ");
|
||||
console.log(`running command '${command}' in ${cwd}`);
|
||||
execSync(command, {
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import { copy } from "../helpers/copy";
|
||||
import { callPackageManager } from "../helpers/install";
|
||||
import { copy } from "./copy";
|
||||
import { callPackageManager } from "./install";
|
||||
|
||||
import fs from "fs/promises";
|
||||
import path from "path";
|
||||
import { cyan } from "picocolors";
|
||||
|
||||
import { COMMUNITY_OWNER, COMMUNITY_REPO } from "../helpers/constant";
|
||||
import { PackageManager } from "../helpers/get-pkg-manager";
|
||||
import { downloadAndExtractRepo } from "../helpers/repo";
|
||||
import { COMMUNITY_OWNER, COMMUNITY_REPO } from "./constant";
|
||||
import { PackageManager } from "./get-pkg-manager";
|
||||
import { isHavingPoetryLockFile, tryPoetryRun } from "./poetry";
|
||||
import { installPythonTemplate } from "./python";
|
||||
import { downloadAndExtractRepo } from "./repo";
|
||||
import {
|
||||
InstallTemplateArgs,
|
||||
TemplateEngine,
|
||||
@@ -71,7 +72,13 @@ const copyTestData = async (
|
||||
vectorDb?: TemplateVectorDB,
|
||||
) => {
|
||||
if (engine === "context") {
|
||||
const srcPath = path.join(__dirname, "components", "data");
|
||||
const srcPath = path.join(
|
||||
__dirname,
|
||||
"..",
|
||||
"templates",
|
||||
"components",
|
||||
"data",
|
||||
);
|
||||
const destPath = path.join(root, "data");
|
||||
console.log(`\nCopying test data to ${cyan(destPath)}\n`);
|
||||
await copy("**", destPath, {
|
||||
@@ -83,18 +90,23 @@ const copyTestData = async (
|
||||
if (packageManager && engine === "context") {
|
||||
const runGenerate = `${cyan(
|
||||
framework === "fastapi"
|
||||
? "python app/engine/generate.py"
|
||||
? "poetry run python app/engine/generate.py"
|
||||
: `${packageManager} run generate`,
|
||||
)}`;
|
||||
const hasOpenAiKey = openAiKey || process.env["OPENAI_API_KEY"];
|
||||
const hasVectorDb = vectorDb && vectorDb !== "none";
|
||||
const shouldRunGenerateAfterInstall =
|
||||
hasOpenAiKey && framework !== "fastapi" && vectorDb === "none";
|
||||
if (shouldRunGenerateAfterInstall) {
|
||||
console.log(`\nRunning ${runGenerate} to generate the context data.\n`);
|
||||
await callPackageManager(packageManager, true, ["run", "generate"]);
|
||||
console.log();
|
||||
return;
|
||||
if (framework === "fastapi") {
|
||||
if (hasOpenAiKey && vectorDb === "none" && isHavingPoetryLockFile()) {
|
||||
console.log(`Running ${runGenerate} to generate the context data.`);
|
||||
tryPoetryRun("python app/engine/generate.py");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (hasOpenAiKey && vectorDb === "none") {
|
||||
console.log(`Running ${runGenerate} to generate the context data.`);
|
||||
await callPackageManager(packageManager, true, ["run", "generate"]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const settings = [];
|
||||
@@ -156,6 +168,10 @@ export const installTemplate = async (
|
||||
props.openAiKey,
|
||||
props.vectorDb,
|
||||
);
|
||||
} else {
|
||||
// this is a frontend for a full-stack app, create .env file with model information
|
||||
const content = `MODEL=${props.model}\nNEXT_PUBLIC_MODEL=${props.model}\n`;
|
||||
await fs.writeFile(path.join(props.root, ".env"), content);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import { execSync } from "child_process";
|
||||
import fs from "fs";
|
||||
|
||||
export function isPoetryAvailable(): boolean {
|
||||
try {
|
||||
execSync("poetry --version", { stdio: "ignore" });
|
||||
return true;
|
||||
} catch (_) {}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function tryPoetryInstall(): boolean {
|
||||
try {
|
||||
execSync("poetry install", { stdio: "inherit" });
|
||||
return true;
|
||||
} catch (_) {}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function tryPoetryRun(command: string): boolean {
|
||||
try {
|
||||
execSync(`poetry run ${command}`, { stdio: "inherit" });
|
||||
return true;
|
||||
} catch (_) {}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function isHavingPoetryLockFile(): boolean {
|
||||
try {
|
||||
return fs.existsSync("poetry.lock");
|
||||
} catch (_) {}
|
||||
return false;
|
||||
}
|
||||
+44
-8
@@ -1,8 +1,10 @@
|
||||
import fs from "fs/promises";
|
||||
import path from "path";
|
||||
import { cyan } from "picocolors";
|
||||
import { cyan, yellow } from "picocolors";
|
||||
import { parse, stringify } from "smol-toml";
|
||||
import { copy } from "../helpers/copy";
|
||||
import terminalLink from "terminal-link";
|
||||
import { copy } from "./copy";
|
||||
import { isPoetryAvailable, tryPoetryInstall } from "./poetry";
|
||||
import { InstallTemplateArgs, TemplateVectorDB } from "./types";
|
||||
|
||||
interface Dependency {
|
||||
@@ -96,12 +98,25 @@ export const installPythonTemplate = async ({
|
||||
framework,
|
||||
engine,
|
||||
vectorDb,
|
||||
installDependencies,
|
||||
}: Pick<
|
||||
InstallTemplateArgs,
|
||||
"root" | "framework" | "template" | "engine" | "vectorDb"
|
||||
| "root"
|
||||
| "framework"
|
||||
| "template"
|
||||
| "engine"
|
||||
| "vectorDb"
|
||||
| "installDependencies"
|
||||
>) => {
|
||||
console.log("\nInitializing Python project with template:", template, "\n");
|
||||
const templatePath = path.join(__dirname, "types", template, framework);
|
||||
const templatePath = path.join(
|
||||
__dirname,
|
||||
"..",
|
||||
"templates",
|
||||
"types",
|
||||
template,
|
||||
framework,
|
||||
);
|
||||
await copy("**", root, {
|
||||
parents: true,
|
||||
cwd: templatePath,
|
||||
@@ -123,7 +138,7 @@ export const installPythonTemplate = async ({
|
||||
});
|
||||
|
||||
if (engine === "context") {
|
||||
const compPath = path.join(__dirname, "components");
|
||||
const compPath = path.join(__dirname, "..", "templates", "components");
|
||||
const VectorDBPath = path.join(
|
||||
compPath,
|
||||
"vectordbs",
|
||||
@@ -139,7 +154,28 @@ export const installPythonTemplate = async ({
|
||||
const addOnDependencies = getAdditionalDependencies(vectorDb);
|
||||
await addDependencies(root, addOnDependencies);
|
||||
|
||||
console.log(
|
||||
"\nPython project, dependencies won't be installed automatically.\n",
|
||||
);
|
||||
// install python dependencies
|
||||
if (installDependencies) {
|
||||
if (isPoetryAvailable()) {
|
||||
console.log(
|
||||
`Installing python dependencies using poetry. This may take a while...`,
|
||||
);
|
||||
const installSuccessful = tryPoetryInstall();
|
||||
if (!installSuccessful) {
|
||||
console.warn(
|
||||
yellow("Install failed. Please install dependencies manually."),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
console.warn(
|
||||
yellow(
|
||||
`Poetry is not available in the current environment. The Python dependencies will not be installed automatically.
|
||||
Please check ${terminalLink(
|
||||
"Poetry Installation",
|
||||
`https://python-poetry.org/docs/#installation`,
|
||||
)} to install poetry first, then install the dependencies manually.`,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user