Compare commits

..

6 Commits

Author SHA1 Message Date
github-actions[bot] 3658fec684 Release 0.4.0 (#499)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-02-20 11:11:09 +07:00
Marcus Schiesser c3d275abe1 make minor release 2025-02-20 11:07:56 +07:00
Thuc Pham 61204a1381 chore: bump LITS 0.9 (#505)
---------
Co-authored-by: leehuwuj <leehuwuj@gmail.com>
2025-02-20 10:33:22 +07:00
Huu Le 9e723c3a15 Standardize the code of workflow use cases (#495) 2025-02-05 11:10:47 +07:00
Thuc Pham d5da55b993 feat: add components.json to use CLI (#501) 2025-02-05 11:04:16 +07:00
Thuc Pham c1552ebb00 chore: move wikipedia tool to create-llama (#498) 2025-02-03 17:35:19 +07:00
48 changed files with 683 additions and 400 deletions
+6 -4
View File
@@ -69,11 +69,12 @@ jobs:
DATASOURCE: ${{ matrix.datasources }}
working-directory: .
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report-python
name: playwright-report-python-${{ matrix.os }}-${{ matrix.frameworks }}-${{ matrix.datasources }}
path: ./playwright-report/
overwrite: true
retention-days: 30
e2e-typescript:
@@ -136,9 +137,10 @@ jobs:
DATASOURCE: ${{ matrix.datasources }}
working-directory: .
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report-typescript
name: playwright-report-typescript-${{ matrix.os }}-${{ matrix.frameworks }}-${{ matrix.datasources }}-node${{ matrix.node-version }}
path: ./playwright-report/
overwrite: true
retention-days: 30
+12
View File
@@ -1,5 +1,17 @@
# create-llama
## 0.4.0
### Minor Changes
- 61204a1: chore: bump LITS 0.9
### Patch Changes
- 9e723c3: Standardize the code of the workflow use case (Python)
- d5da55b: feat: add components.json to use CLI
- c1552eb: chore: move wikipedia tool to create-llama
## 0.3.28
### Patch Changes
+8 -1
View File
@@ -325,9 +325,16 @@ export const writeToolsConfig = async (
yaml.stringify(configContent),
);
} else {
// For Typescript, we treat llamahub tools as local tools
const tsConfigContent = {
local: {
...configContent.local,
...configContent.llamahub,
},
};
await fs.writeFile(
path.join(configPath, "tools.json"),
JSON.stringify(configContent, null, 2),
JSON.stringify(tsConfigContent, null, 2),
);
}
};
+90 -22
View File
@@ -6,7 +6,7 @@ import { assetRelocator, copy } from "../helpers/copy";
import { callPackageManager } from "../helpers/install";
import { templatesDir } from "./dir";
import { PackageManager } from "./get-pkg-manager";
import { InstallTemplateArgs } from "./types";
import { InstallTemplateArgs, ModelProvider, TemplateVectorDB } from "./types";
/**
* Install a LlamaIndex internal template to a given `root` directory.
@@ -27,6 +27,7 @@ export const installTSTemplate = async ({
dataSources,
useLlamaParse,
useCase,
modelConfig,
}: InstallTemplateArgs & { backend: boolean }) => {
console.log(bold(`Using ${packageManager}.`));
@@ -181,6 +182,12 @@ export const installTSTemplate = async ({
cwd: path.join(compPath, "loaders", "typescript", loaderFolder),
});
// copy provider settings
await copy("**", enginePath, {
parents: true,
cwd: path.join(compPath, "providers", "typescript", modelConfig.provider),
});
// Select and copy engine code based on data sources and tools
let engine;
tools = tools ?? [];
@@ -239,6 +246,8 @@ export const installTSTemplate = async ({
ui,
observability,
vectorDb,
backend,
modelConfig,
});
if (
@@ -249,6 +258,68 @@ export const installTSTemplate = async ({
}
};
const providerDependencies: {
[key in ModelProvider]?: Record<string, string>;
} = {
openai: {
"@llamaindex/openai": "^0.1.52",
},
gemini: {
"@llamaindex/google": "^0.0.7",
},
ollama: {
"@llamaindex/ollama": "^0.0.40",
},
mistral: {
"@llamaindex/mistral": "^0.0.5",
},
"azure-openai": {
"@llamaindex/openai": "^0.1.52",
},
groq: {
"@llamaindex/groq": "^0.0.51",
"@llamaindex/huggingface": "^0.0.36", // groq uses huggingface as default embedding model
},
anthropic: {
"@llamaindex/anthropic": "^0.1.0",
"@llamaindex/huggingface": "^0.0.36", // anthropic uses huggingface as default embedding model
},
};
const vectorDbDependencies: Record<TemplateVectorDB, Record<string, string>> = {
astra: {
"@llamaindex/astra": "^0.0.5",
},
chroma: {
"@llamaindex/chroma": "^0.0.5",
},
llamacloud: {},
milvus: {
"@zilliz/milvus2-sdk-node": "^2.4.6",
"@llamaindex/milvus": "^0.1.0",
},
mongo: {
mongodb: "6.7.0",
"@llamaindex/mongodb": "^0.0.5",
},
none: {},
pg: {
pg: "^8.12.0",
pgvector: "^0.2.0",
"@llamaindex/postgres": "^0.0.33",
},
pinecone: {
"@llamaindex/pinecone": "^0.0.5",
},
qdrant: {
"@qdrant/js-client-rest": "^1.11.0",
"@llamaindex/qdrant": "^0.1.0",
},
weaviate: {
"@llamaindex/weaviate": "^0.0.5",
},
};
async function updatePackageJson({
root,
appName,
@@ -258,6 +329,8 @@ async function updatePackageJson({
ui,
observability,
vectorDb,
backend,
modelConfig,
}: Pick<
InstallTemplateArgs,
| "root"
@@ -267,8 +340,10 @@ async function updatePackageJson({
| "ui"
| "observability"
| "vectorDb"
| "modelConfig"
> & {
relativeEngineDestPath: string;
backend: boolean;
}): Promise<any> {
const packageJsonFile = path.join(root, "package.json");
const packageJson: any = JSON.parse(
@@ -308,32 +383,25 @@ async function updatePackageJson({
};
}
if (vectorDb === "pg") {
if (backend) {
packageJson.dependencies = {
...packageJson.dependencies,
pg: "^8.12.0",
pgvector: "^0.2.0",
"@llamaindex/readers": "^2.0.0",
};
}
if (vectorDb === "qdrant") {
packageJson.dependencies = {
...packageJson.dependencies,
"@qdrant/js-client-rest": "^1.11.0",
};
}
if (vectorDb === "mongo") {
packageJson.dependencies = {
...packageJson.dependencies,
mongodb: "^6.7.0",
};
}
if (vectorDb && vectorDb in vectorDbDependencies) {
packageJson.dependencies = {
...packageJson.dependencies,
...vectorDbDependencies[vectorDb],
};
}
if (vectorDb === "milvus") {
packageJson.dependencies = {
...packageJson.dependencies,
"@zilliz/milvus2-sdk-node": "^2.4.6",
};
if (modelConfig.provider && modelConfig.provider in providerDependencies) {
packageJson.dependencies = {
...packageJson.dependencies,
...providerDependencies[modelConfig.provider],
};
}
}
if (observability === "traceloop") {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "create-llama",
"version": "0.3.28",
"version": "0.4.0",
"description": "Create LlamaIndex-powered apps with one command",
"keywords": [
"rag",
@@ -32,7 +32,6 @@ logger.setLevel(logging.INFO)
def create_workflow(
chat_history: Optional[List[ChatMessage]] = None,
params: Optional[Dict[str, Any]] = None,
**kwargs,
) -> Workflow:
@@ -45,7 +44,6 @@ def create_workflow(
return DeepResearchWorkflow(
index=index,
chat_history=chat_history,
timeout=120.0,
)
@@ -73,19 +71,13 @@ class DeepResearchWorkflow(Workflow):
def __init__(
self,
index: BaseIndex,
chat_history: Optional[List[ChatMessage]] = None,
stream: bool = True,
**kwargs,
):
super().__init__(**kwargs)
self.index = index
self.context_nodes = []
self.stream = stream
self.chat_history = chat_history
self.memory = SimpleComposableMemory.from_defaults(
primary_memory=ChatMemoryBuffer.from_defaults(
chat_history=chat_history,
),
primary_memory=ChatMemoryBuffer.from_defaults(),
)
@step
@@ -93,8 +85,15 @@ class DeepResearchWorkflow(Workflow):
"""
Initiate the workflow: memory, tools, agent
"""
self.stream = ev.get("stream", True)
self.user_request = ev.get("user_msg")
chat_history = ev.get("chat_history")
if chat_history is not None:
self.memory.put_messages(chat_history)
await ctx.set("total_questions", 0)
self.user_request = ev.get("input")
# Add user message to memory
self.memory.put_messages(
messages=[
ChatMessage(
@@ -319,7 +318,6 @@ class DeepResearchWorkflow(Workflow):
"""
Report the answers
"""
logger.info("Writing the report")
res = await write_report(
memory=self.memory,
user_request=self.user_request,
@@ -1,13 +1,5 @@
from typing import Any, Dict, List, Optional
from app.engine.index import IndexConfig, get_index
from app.engine.tools import ToolFactory
from app.engine.tools.query_engine import get_query_engine_tool
from app.workflows.events import AgentRunEvent
from app.workflows.tools import (
call_tools,
chat_with_tools,
)
from llama_index.core import Settings
from llama_index.core.base.llms.types import ChatMessage, MessageRole
from llama_index.core.llms.function_calling import FunctionCallingLLM
@@ -22,9 +14,17 @@ from llama_index.core.workflow import (
step,
)
from app.engine.index import IndexConfig, get_index
from app.engine.tools import ToolFactory
from app.engine.tools.query_engine import get_query_engine_tool
from app.workflows.events import AgentRunEvent
from app.workflows.tools import (
call_tools,
chat_with_tools,
)
def create_workflow(
chat_history: Optional[List[ChatMessage]] = None,
params: Optional[Dict[str, Any]] = None,
**kwargs,
) -> Workflow:
@@ -45,7 +45,6 @@ def create_workflow(
query_engine_tool=query_engine_tool,
code_interpreter_tool=code_interpreter_tool,
document_generator_tool=document_generator_tool,
chat_history=chat_history,
)
@@ -91,6 +90,7 @@ class FinancialReportWorkflow(Workflow):
It's good to using appropriate tools for the user request and always use the information from the tools, don't make up anything yourself.
For the query engine tool, you should break down the user request into a list of queries and call the tool with the queries.
"""
stream: bool = True
def __init__(
self,
@@ -99,12 +99,10 @@ class FinancialReportWorkflow(Workflow):
document_generator_tool: FunctionTool,
llm: Optional[FunctionCallingLLM] = None,
timeout: int = 360,
chat_history: Optional[List[ChatMessage]] = None,
system_prompt: Optional[str] = None,
):
super().__init__(timeout=timeout)
self.system_prompt = system_prompt or self._default_system_prompt
self.chat_history = chat_history or []
self.query_engine_tool = query_engine_tool
self.code_interpreter_tool = code_interpreter_tool
self.document_generator_tool = document_generator_tool
@@ -122,13 +120,19 @@ class FinancialReportWorkflow(Workflow):
]
self.llm: FunctionCallingLLM = llm or Settings.llm
assert isinstance(self.llm, FunctionCallingLLM)
self.memory = ChatMemoryBuffer.from_defaults(
llm=self.llm, chat_history=self.chat_history
)
self.memory = ChatMemoryBuffer.from_defaults(llm=self.llm)
@step()
async def prepare_chat_history(self, ctx: Context, ev: StartEvent) -> InputEvent:
ctx.data["input"] = ev.input
self.stream = ev.get("stream", True)
user_msg = ev.get("user_msg")
chat_history = ev.get("chat_history")
if chat_history is not None:
self.memory.put_messages(chat_history)
# Add user message to memory
self.memory.put(ChatMessage(role=MessageRole.USER, content=user_msg))
if self.system_prompt:
system_msg = ChatMessage(
@@ -136,9 +140,6 @@ class FinancialReportWorkflow(Workflow):
)
self.memory.put(system_msg)
# Add user input to memory
self.memory.put(ChatMessage(role=MessageRole.USER, content=ev.input))
return InputEvent(input=self.memory.get())
@step()
@@ -160,8 +161,10 @@ class FinancialReportWorkflow(Workflow):
chat_history,
)
if not response.has_tool_calls():
# If no tool call, return the response generator
return StopEvent(result=response.generator)
if self.stream:
return StopEvent(result=response.generator)
else:
return StopEvent(result=await response.full_response())
# calling different tools at the same time is not supported at the moment
# add an error message to tell the AI to process step by step
if response.is_calling_different_tools():
@@ -25,7 +25,6 @@ from app.workflows.tools import (
def create_workflow(
chat_history: Optional[List[ChatMessage]] = None,
params: Optional[Dict[str, Any]] = None,
**kwargs,
) -> Workflow:
@@ -45,7 +44,6 @@ def create_workflow(
query_engine_tool=query_engine_tool,
extractor_tool=extractor_tool, # type: ignore
filling_tool=filling_tool, # type: ignore
chat_history=chat_history,
)
return workflow
@@ -88,6 +86,7 @@ class FormFillingWorkflow(Workflow):
Only use provided data - never make up any information yourself. Fill N/A if an answer is not found.
If there is no query engine tool or the gathered information has many N/A values indicating the questions don't match the data, respond with a warning and ask the user to upload a different file or connect to a knowledge base.
"""
stream: bool = True
def __init__(
self,
@@ -96,12 +95,10 @@ class FormFillingWorkflow(Workflow):
filling_tool: FunctionTool,
llm: Optional[FunctionCallingLLM] = None,
timeout: int = 360,
chat_history: Optional[List[ChatMessage]] = None,
system_prompt: Optional[str] = None,
):
super().__init__(timeout=timeout)
self.system_prompt = system_prompt or self._default_system_prompt
self.chat_history = chat_history or []
self.query_engine_tool = query_engine_tool
self.extractor_tool = extractor_tool
self.filling_tool = filling_tool
@@ -113,13 +110,18 @@ class FormFillingWorkflow(Workflow):
self.llm: FunctionCallingLLM = llm or Settings.llm
if not isinstance(self.llm, FunctionCallingLLM):
raise ValueError("FormFillingWorkflow only supports FunctionCallingLLM.")
self.memory = ChatMemoryBuffer.from_defaults(
llm=self.llm, chat_history=self.chat_history
)
self.memory = ChatMemoryBuffer.from_defaults(llm=self.llm)
@step()
async def start(self, ctx: Context, ev: StartEvent) -> InputEvent:
ctx.data["input"] = ev.input
self.stream = ev.get("stream", True)
user_msg = ev.get("user_msg", "")
chat_history = ev.get("chat_history", [])
if chat_history:
self.memory.put_messages(chat_history)
self.memory.put(ChatMessage(role=MessageRole.USER, content=user_msg))
if self.system_prompt:
system_msg = ChatMessage(
@@ -127,12 +129,7 @@ class FormFillingWorkflow(Workflow):
)
self.memory.put(system_msg)
user_input = ev.input
user_msg = ChatMessage(role=MessageRole.USER, content=user_input)
self.memory.put(user_msg)
chat_history = self.memory.get()
return InputEvent(input=chat_history)
return InputEvent(input=self.memory.get())
@step()
async def handle_llm_input( # type: ignore
@@ -150,7 +147,10 @@ class FormFillingWorkflow(Workflow):
chat_history,
)
if not response.has_tool_calls():
return StopEvent(result=response.generator)
if self.stream:
return StopEvent(result=response.generator)
else:
return StopEvent(result=await response.full_response())
# calling different tools at the same time is not supported at the moment
# add an error message to tell the AI to process step by step
if response.is_calling_different_tools():
@@ -1,5 +1,4 @@
import { BaseToolWithCall } from "llamaindex";
import { ToolsFactory } from "llamaindex/tools/ToolsFactory";
import fs from "node:fs/promises";
import path from "node:path";
import { CodeGeneratorTool, CodeGeneratorToolParams } from "./code-generator";
@@ -18,6 +17,7 @@ import { ImgGeneratorTool, ImgGeneratorToolParams } from "./img-gen";
import { InterpreterTool, InterpreterToolParams } from "./interpreter";
import { OpenAPIActionTool } from "./openapi-action";
import { WeatherTool, WeatherToolParams } from "./weather";
import { WikipediaTool, WikipediaToolParams } from "./wikipedia";
type ToolCreator = (config: unknown) => Promise<BaseToolWithCall[]>;
@@ -27,12 +27,13 @@ export async function createTools(toolConfig: {
}): Promise<BaseToolWithCall[]> {
// add local tools from the 'tools' folder (if configured)
const tools = await createLocalTools(toolConfig.local);
// add tools from LlamaIndexTS (if configured)
tools.push(...(await ToolsFactory.createTools(toolConfig.llamahub)));
return tools;
}
const toolFactory: Record<string, ToolCreator> = {
"wikipedia.WikipediaToolSpec": async (config: unknown) => {
return [new WikipediaTool(config as WikipediaToolParams)];
},
weather: async (config: unknown) => {
return [new WeatherTool(config as WeatherToolParams)];
},
@@ -0,0 +1,60 @@
import type { JSONSchemaType } from "ajv";
import type { BaseTool, ToolMetadata } from "llamaindex";
import { default as wiki } from "wikipedia";
type WikipediaParameter = {
query: string;
lang?: string;
};
export type WikipediaToolParams = {
metadata?: ToolMetadata<JSONSchemaType<WikipediaParameter>>;
};
const DEFAULT_META_DATA: ToolMetadata<JSONSchemaType<WikipediaParameter>> = {
name: "wikipedia_tool",
description: "A tool that uses a query engine to search Wikipedia.",
parameters: {
type: "object",
properties: {
query: {
type: "string",
description: "The query to search for",
},
lang: {
type: "string",
description: "The language to search in",
nullable: true,
},
},
required: ["query"],
},
};
export class WikipediaTool implements BaseTool<WikipediaParameter> {
private readonly DEFAULT_LANG = "en";
metadata: ToolMetadata<JSONSchemaType<WikipediaParameter>>;
constructor(params?: WikipediaToolParams) {
this.metadata = params?.metadata || DEFAULT_META_DATA;
}
async loadData(
page: string,
lang: string = this.DEFAULT_LANG,
): Promise<string> {
wiki.setLang(lang);
const pageResult = await wiki.page(page, { autoSuggest: false });
const content = await pageResult.content();
return content;
}
async call({
query,
lang = this.DEFAULT_LANG,
}: WikipediaParameter): Promise<string> {
const searchResult = await wiki.search(query);
if (searchResult.results.length === 0) return "No search results.";
return await this.loadData(searchResult.results[0].title, lang);
}
}
@@ -1,7 +1,7 @@
import {
FILE_EXT_TO_READER,
SimpleDirectoryReader,
} from "llamaindex/readers/index";
} from "@llamaindex/readers/directory";
export const DATA_DIR = "./data";
@@ -1,8 +1,8 @@
import { LlamaParseReader } from "llamaindex";
import {
FILE_EXT_TO_READER,
SimpleDirectoryReader,
} from "llamaindex/readers/index";
} from "@llamaindex/readers/directory";
import { LlamaParseReader } from "llamaindex";
export const DATA_DIR = "./data";
@@ -0,0 +1,32 @@
import logging
from abc import ABC, abstractmethod
from typing import Any
logger = logging.getLogger("uvicorn")
class EventCallback(ABC):
"""
Base class for event callbacks during event streaming.
"""
async def run(self, event: Any) -> Any:
"""
Called for each event in the stream.
Default behavior: pass through the event unchanged.
"""
return event
async def on_complete(self, final_response: str) -> Any:
"""
Called when the stream is complete.
Default behavior: return None.
"""
return None
@abstractmethod
def from_default(self, *args, **kwargs) -> "EventCallback":
"""
Create a new instance of the processor from default values.
"""
pass
@@ -0,0 +1,42 @@
import logging
from typing import Any, List
from fastapi import BackgroundTasks
from llama_index.core.schema import NodeWithScore
from app.api.callbacks.base import EventCallback
logger = logging.getLogger("uvicorn")
class LlamaCloudFileDownload(EventCallback):
"""
Processor for handling LlamaCloud file downloads from source nodes.
Only work if LlamaCloud service code is available.
"""
def __init__(self, background_tasks: BackgroundTasks):
self.background_tasks = background_tasks
async def run(self, event: Any) -> Any:
if hasattr(event, "to_response"):
event_response = event.to_response()
if event_response.get("type") == "sources" and hasattr(event, "nodes"):
await self._process_response_nodes(event.nodes)
return event
async def _process_response_nodes(self, source_nodes: List[NodeWithScore]):
try:
from app.engine.service import LLamaCloudFileService # type: ignore
LLamaCloudFileService.download_files_from_nodes(
source_nodes, self.background_tasks
)
except ImportError:
pass
@classmethod
def from_default(
cls, background_tasks: BackgroundTasks
) -> "LlamaCloudFileDownload":
return cls(background_tasks=background_tasks)
@@ -0,0 +1,34 @@
import logging
from typing import Any
from app.api.callbacks.base import EventCallback
from app.api.routers.models import ChatData
from app.api.services.suggestion import NextQuestionSuggestion
logger = logging.getLogger("uvicorn")
class SuggestNextQuestions(EventCallback):
"""Processor for generating next question suggestions."""
def __init__(self, chat_data: ChatData):
self.chat_data = chat_data
self.accumulated_text = ""
async def on_complete(self, final_response: str) -> Any:
if final_response == "":
return None
questions = await NextQuestionSuggestion.suggest_next_questions(
self.chat_data.messages, final_response
)
if questions:
return {
"type": "suggested_questions",
"data": questions,
}
return None
@classmethod
def from_default(cls, chat_data: ChatData) -> "SuggestNextQuestions":
return cls(chat_data=chat_data)
@@ -0,0 +1,66 @@
import logging
from typing import List, Optional
from llama_index.core.workflow.handler import WorkflowHandler
from app.api.callbacks.base import EventCallback
logger = logging.getLogger("uvicorn")
class StreamHandler:
"""
Streams events from a workflow handler through a chain of callbacks.
"""
def __init__(
self,
workflow_handler: WorkflowHandler,
callbacks: Optional[List[EventCallback]] = None,
):
self.workflow_handler = workflow_handler
self.callbacks = callbacks or []
self.accumulated_text = ""
def vercel_stream(self):
"""Create a streaming response with Vercel format."""
from app.api.routers.vercel_response import VercelStreamResponse
return VercelStreamResponse(stream_handler=self)
async def cancel_run(self):
"""Cancel the workflow handler."""
await self.workflow_handler.cancel_run()
async def stream_events(self):
"""Stream events through the processor chain."""
try:
async for event in self.workflow_handler.stream_events():
# Process the event through each processor
for callback in self.callbacks:
event = await callback.run(event)
yield event
# After all events are processed, call on_complete for each callback
for callback in self.callbacks:
result = await callback.on_complete(self.accumulated_text)
if result:
yield result
except Exception as e:
# Make sure to cancel the workflow on error
await self.workflow_handler.cancel_run()
raise e
async def accumulate_text(self, text: str):
"""Accumulate text from the workflow handler."""
self.accumulated_text += text
@classmethod
def from_default(
cls,
handler: WorkflowHandler,
callbacks: Optional[List[EventCallback]] = None,
) -> "StreamHandler":
"""Create a new instance with the given workflow handler and callbacks."""
return cls(workflow_handler=handler, callbacks=callbacks)
@@ -2,10 +2,12 @@ import logging
from fastapi import APIRouter, BackgroundTasks, HTTPException, Request, status
from app.api.callbacks.llamacloud import LlamaCloudFileDownload
from app.api.callbacks.next_question import SuggestNextQuestions
from app.api.callbacks.stream_handler import StreamHandler
from app.api.routers.models import (
ChatData,
)
from app.api.routers.vercel_response import VercelStreamResponse
from app.engine.query_filter import generate_filters
from app.workflows import create_workflow
@@ -29,19 +31,22 @@ async def chat(
params = data.data or {}
workflow = create_workflow(
chat_history=messages,
params=params,
filters=filters,
)
event_handler = workflow.run(input=last_message_content, streaming=True)
return VercelStreamResponse(
request=request,
chat_data=data,
background_tasks=background_tasks,
event_handler=event_handler,
events=workflow.stream_events(),
handler = workflow.run(
user_msg=last_message_content,
chat_history=messages,
stream=True,
)
return StreamHandler.from_default(
handler=handler,
callbacks=[
LlamaCloudFileDownload.from_default(background_tasks),
SuggestNextQuestions.from_default(data),
],
).vercel_stream()
except Exception as e:
logger.exception("Error in chat engine", exc_info=True)
raise HTTPException(
@@ -1,22 +1,20 @@
import asyncio
import json
import logging
from typing import AsyncGenerator, Awaitable, List
from typing import AsyncGenerator
from aiostream import stream
from fastapi import BackgroundTasks, Request
from fastapi.responses import StreamingResponse
from llama_index.core.schema import NodeWithScore
from llama_index.core.agent.workflow.workflow_events import AgentStream
from llama_index.core.workflow import StopEvent
from app.api.routers.models import ChatData, Message
from app.api.services.suggestion import NextQuestionSuggestion
from app.api.callbacks.stream_handler import StreamHandler
logger = logging.getLogger("uvicorn")
class VercelStreamResponse(StreamingResponse):
"""
Base class to convert the response from the chat engine to the streaming format expected by Vercel
Converts preprocessed events into Vercel-compatible streaming response format.
"""
TEXT_PREFIX = "0:"
@@ -25,136 +23,77 @@ class VercelStreamResponse(StreamingResponse):
def __init__(
self,
request: Request,
chat_data: ChatData,
background_tasks: BackgroundTasks,
stream_handler: StreamHandler,
*args,
**kwargs,
):
self.request = request
self.chat_data = chat_data
self.background_tasks = background_tasks
content = self.content_generator(*args, **kwargs)
super().__init__(content=content)
self.handler = stream_handler
super().__init__(content=self.content_generator())
async def content_generator(self, event_handler, events):
stream = self._create_stream(
self.request, self.chat_data, event_handler, events
)
is_stream_started = False
async def content_generator(self):
"""Generate Vercel-formatted content from preprocessed events."""
stream_started = False
try:
async with stream.stream() as streamer:
async for output in streamer:
if not is_stream_started:
is_stream_started = True
# Stream a blank message to start the stream
yield self.convert_text("")
async for event in self.handler.stream_events():
if not stream_started:
# Start the stream with an empty message
stream_started = True
yield self.convert_text("")
yield output
except asyncio.CancelledError:
logger.warning("Workflow has been cancelled!")
except Exception as e:
logger.error(
f"Unexpected error in content_generator: {str(e)}", exc_info=True
)
yield self.convert_error(
"An unexpected error occurred while processing your request, preventing the creation of a final answer. Please try again."
)
finally:
await event_handler.cancel_run()
logger.info("The stream has been stopped!")
def _create_stream(
self,
request: Request,
chat_data: ChatData,
event_handler: Awaitable,
events: AsyncGenerator,
verbose: bool = True,
):
# Yield the text response
async def _chat_response_generator():
result = await event_handler
final_response = ""
if isinstance(result, AsyncGenerator):
async for token in result:
final_response += str(token.delta)
yield self.convert_text(token.delta)
else:
if hasattr(result, "response"):
content = result.response.message.content
if content:
for token in content:
final_response += str(token)
yield self.convert_text(token)
else:
final_response += str(result)
yield self.convert_text(result)
# Generate next questions if next question prompt is configured
question_data = await self._generate_next_questions(
chat_data.messages, final_response
)
if question_data:
yield self.convert_data(question_data)
# Yield the events from the event handler
async def _event_generator():
async for event in events:
event_response = event.to_response()
if verbose:
logger.debug(event_response)
if event_response is not None:
# Handle different types of events
if isinstance(event, (AgentStream, StopEvent)):
async for chunk in self._stream_text(event):
await self.handler.accumulate_text(chunk)
yield self.convert_text(chunk)
elif isinstance(event, dict):
yield self.convert_data(event)
elif hasattr(event, "to_response"):
event_response = event.to_response()
yield self.convert_data(event_response)
if event_response.get("type") == "sources":
self._process_response_nodes(event.nodes, self.background_tasks)
else:
yield self.convert_data(event.model_dump())
combine = stream.merge(_chat_response_generator(), _event_generator())
return combine
except asyncio.CancelledError:
logger.warning("Client cancelled the request!")
await self.handler.cancel_run()
except Exception as e:
logger.error(f"Error in stream response: {e}")
yield self.convert_error(str(e))
await self.handler.cancel_run()
@staticmethod
def _process_response_nodes(
source_nodes: List[NodeWithScore],
background_tasks: BackgroundTasks,
):
try:
# Start background tasks to download documents from LlamaCloud if needed
from app.engine.service import LLamaCloudFileService # type: ignore
LLamaCloudFileService.download_files_from_nodes(
source_nodes, background_tasks
)
except ImportError:
logger.debug(
"LlamaCloud is not configured. Skipping post processing of nodes"
)
pass
async def _stream_text(
self, event: AgentStream | StopEvent
) -> AsyncGenerator[str, None]:
"""
Accept stream text from either AgentStream or StopEvent with string or AsyncGenerator result
"""
if isinstance(event, AgentStream):
yield self.convert_text(event.delta)
elif isinstance(event, StopEvent):
if isinstance(event.result, str):
yield event.result
elif isinstance(event.result, AsyncGenerator):
async for chunk in event.result:
if isinstance(chunk, str):
yield chunk
elif hasattr(chunk, "delta"):
yield chunk.delta
@classmethod
def convert_text(cls, token: str):
def convert_text(cls, token: str) -> str:
"""Convert text event to Vercel format."""
# Escape newlines and double quotes to avoid breaking the stream
token = json.dumps(token)
return f"{cls.TEXT_PREFIX}{token}\n"
@classmethod
def convert_data(cls, data: dict):
def convert_data(cls, data: dict) -> str:
"""Convert data event to Vercel format."""
data_str = json.dumps(data)
return f"{cls.DATA_PREFIX}[{data_str}]\n"
@classmethod
def convert_error(cls, error: str):
def convert_error(cls, error: str) -> str:
"""Convert error event to Vercel format."""
error_str = json.dumps(error)
return f"{cls.ERROR_PREFIX}{error_str}\n"
@staticmethod
async def _generate_next_questions(chat_history: List[Message], response: str):
questions = await NextQuestionSuggestion.suggest_next_questions(
chat_history, response
)
if questions:
return {
"type": "suggested_questions",
"data": questions,
}
return None
@@ -3,7 +3,6 @@ import uuid
from abc import ABC, abstractmethod
from typing import Any, AsyncGenerator, Callable, Optional
from app.workflows.events import AgentRunEvent, AgentRunEventType
from llama_index.core.base.llms.types import ChatMessage, ChatResponse, MessageRole
from llama_index.core.llms.function_calling import FunctionCallingLLM
from llama_index.core.tools import (
@@ -15,6 +14,8 @@ from llama_index.core.tools import (
from llama_index.core.workflow import Context
from pydantic import BaseModel, ConfigDict
from app.workflows.events import AgentRunEvent, AgentRunEventType
logger = logging.getLogger("uvicorn")
@@ -51,7 +52,9 @@ class ChatWithToolsResponse(BaseModel):
assert self.generator is not None
full_response = ""
async for chunk in self.generator:
full_response += chunk.message.content
content = chunk.message.content
if content:
full_response += content
return full_response
@@ -0,0 +1,19 @@
import {
ALL_AVAILABLE_ANTHROPIC_MODELS,
Anthropic,
} from "@llamaindex/anthropic";
import { HuggingFaceEmbedding } from "@llamaindex/huggingface";
import { Settings } from "llamaindex";
export function setupProvider() {
const embedModelMap: Record<string, string> = {
"all-MiniLM-L6-v2": "Xenova/all-MiniLM-L6-v2",
"all-mpnet-base-v2": "Xenova/all-mpnet-base-v2",
};
Settings.llm = new Anthropic({
model: process.env.MODEL as keyof typeof ALL_AVAILABLE_ANTHROPIC_MODELS,
});
Settings.embedModel = new HuggingFaceEmbedding({
modelType: embedModelMap[process.env.EMBEDDING_MODEL!],
});
}
@@ -0,0 +1,49 @@
import { OpenAI, OpenAIEmbedding } from "@llamaindex/openai";
import { Settings } from "llamaindex";
export function setupProvider() {
// Map Azure OpenAI model names to OpenAI model names (only for TS)
const AZURE_OPENAI_MODEL_MAP: Record<string, string> = {
"gpt-35-turbo": "gpt-3.5-turbo",
"gpt-35-turbo-16k": "gpt-3.5-turbo-16k",
"gpt-4o": "gpt-4o",
"gpt-4": "gpt-4",
"gpt-4-32k": "gpt-4-32k",
"gpt-4-turbo": "gpt-4-turbo",
"gpt-4-turbo-2024-04-09": "gpt-4-turbo",
"gpt-4-vision-preview": "gpt-4-vision-preview",
"gpt-4-1106-preview": "gpt-4-1106-preview",
"gpt-4o-2024-05-13": "gpt-4o-2024-05-13",
};
const azureConfig = {
apiKey: process.env.AZURE_OPENAI_KEY,
endpoint: process.env.AZURE_OPENAI_ENDPOINT,
apiVersion:
process.env.AZURE_OPENAI_API_VERSION || process.env.OPENAI_API_VERSION,
};
Settings.llm = new OpenAI({
model:
AZURE_OPENAI_MODEL_MAP[process.env.MODEL ?? "gpt-35-turbo"] ??
"gpt-3.5-turbo",
maxTokens: process.env.LLM_MAX_TOKENS
? Number(process.env.LLM_MAX_TOKENS)
: undefined,
azure: {
...azureConfig,
deployment: process.env.AZURE_OPENAI_LLM_DEPLOYMENT,
},
});
Settings.embedModel = new OpenAIEmbedding({
model: process.env.EMBEDDING_MODEL,
dimensions: process.env.EMBEDDING_DIM
? parseInt(process.env.EMBEDDING_DIM)
: undefined,
azure: {
...azureConfig,
deployment: process.env.AZURE_OPENAI_EMBEDDING_DEPLOYMENT,
},
});
}
@@ -0,0 +1,16 @@
import {
Gemini,
GEMINI_EMBEDDING_MODEL,
GEMINI_MODEL,
GeminiEmbedding,
} from "@llamaindex/google";
import { Settings } from "llamaindex";
export function setupProvider() {
Settings.llm = new Gemini({
model: process.env.MODEL as GEMINI_MODEL,
});
Settings.embedModel = new GeminiEmbedding({
model: process.env.EMBEDDING_MODEL as GEMINI_EMBEDDING_MODEL,
});
}
@@ -0,0 +1,18 @@
import { Groq } from "@llamaindex/groq";
import { HuggingFaceEmbedding } from "@llamaindex/huggingface";
import { Settings } from "llamaindex";
export function setupProvider() {
const embedModelMap: Record<string, string> = {
"all-MiniLM-L6-v2": "Xenova/all-MiniLM-L6-v2",
"all-mpnet-base-v2": "Xenova/all-mpnet-base-v2",
};
Settings.llm = new Groq({
model: process.env.MODEL!,
});
Settings.embedModel = new HuggingFaceEmbedding({
modelType: embedModelMap[process.env.EMBEDDING_MODEL!],
});
}
@@ -0,0 +1,16 @@
import {
ALL_AVAILABLE_MISTRAL_MODELS,
MistralAI,
MistralAIEmbedding,
MistralAIEmbeddingModelType,
} from "@llamaindex/mistral";
import { Settings } from "llamaindex";
export function setupProvider() {
Settings.llm = new MistralAI({
model: process.env.MODEL as keyof typeof ALL_AVAILABLE_MISTRAL_MODELS,
});
Settings.embedModel = new MistralAIEmbedding({
model: process.env.EMBEDDING_MODEL as MistralAIEmbeddingModelType,
});
}
@@ -0,0 +1,16 @@
import { Ollama, OllamaEmbedding } from "@llamaindex/ollama";
import { Settings } from "llamaindex";
export function setupProvider() {
const config = {
host: process.env.OLLAMA_BASE_URL ?? "http://127.0.0.1:11434",
};
Settings.llm = new Ollama({
model: process.env.MODEL ?? "",
config,
});
Settings.embedModel = new OllamaEmbedding({
model: process.env.EMBEDDING_MODEL ?? "",
config,
});
}
@@ -0,0 +1,17 @@
import { OpenAI, OpenAIEmbedding } from "@llamaindex/openai";
import { Settings } from "llamaindex";
export function setupProvider() {
Settings.llm = new OpenAI({
model: process.env.MODEL ?? "gpt-4o-mini",
maxTokens: process.env.LLM_MAX_TOKENS
? Number(process.env.LLM_MAX_TOKENS)
: undefined,
});
Settings.embedModel = new OpenAIEmbedding({
model: process.env.EMBEDDING_MODEL,
dimensions: process.env.EMBEDDING_DIM
? parseInt(process.env.EMBEDDING_DIM)
: undefined,
});
}
@@ -1,179 +1,18 @@
import {
ALL_AVAILABLE_MISTRAL_MODELS,
Anthropic,
GEMINI_EMBEDDING_MODEL,
GEMINI_MODEL,
Gemini,
GeminiEmbedding,
Groq,
MistralAI,
MistralAIEmbedding,
MistralAIEmbeddingModelType,
OpenAI,
OpenAIEmbedding,
Settings,
} from "llamaindex";
import { HuggingFaceEmbedding } from "llamaindex/embeddings/HuggingFaceEmbedding";
import { OllamaEmbedding } from "llamaindex/embeddings/OllamaEmbedding";
import { ALL_AVAILABLE_ANTHROPIC_MODELS } from "llamaindex/llm/anthropic";
import { Ollama } from "llamaindex/llm/ollama";
import { Settings } from "llamaindex";
import { setupProvider } from "./provider";
const CHUNK_SIZE = 512;
const CHUNK_OVERLAP = 20;
export const initSettings = async () => {
// HINT: you can delete the initialization code for unused model providers
console.log(`Using '${process.env.MODEL_PROVIDER}' model provider`);
if (!process.env.MODEL || !process.env.EMBEDDING_MODEL) {
throw new Error("'MODEL' and 'EMBEDDING_MODEL' env variables must be set.");
}
switch (process.env.MODEL_PROVIDER) {
case "ollama":
initOllama();
break;
case "groq":
initGroq();
break;
case "anthropic":
initAnthropic();
break;
case "gemini":
initGemini();
break;
case "mistral":
initMistralAI();
break;
case "azure-openai":
initAzureOpenAI();
break;
default:
initOpenAI();
break;
}
Settings.chunkSize = CHUNK_SIZE;
Settings.chunkOverlap = CHUNK_OVERLAP;
setupProvider();
};
function initOpenAI() {
Settings.llm = new OpenAI({
model: process.env.MODEL ?? "gpt-4o-mini",
maxTokens: process.env.LLM_MAX_TOKENS
? Number(process.env.LLM_MAX_TOKENS)
: undefined,
});
Settings.embedModel = new OpenAIEmbedding({
model: process.env.EMBEDDING_MODEL,
dimensions: process.env.EMBEDDING_DIM
? parseInt(process.env.EMBEDDING_DIM)
: undefined,
});
}
function initAzureOpenAI() {
// Map Azure OpenAI model names to OpenAI model names (only for TS)
const AZURE_OPENAI_MODEL_MAP: Record<string, string> = {
"gpt-35-turbo": "gpt-3.5-turbo",
"gpt-35-turbo-16k": "gpt-3.5-turbo-16k",
"gpt-4o": "gpt-4o",
"gpt-4": "gpt-4",
"gpt-4-32k": "gpt-4-32k",
"gpt-4-turbo": "gpt-4-turbo",
"gpt-4-turbo-2024-04-09": "gpt-4-turbo",
"gpt-4-vision-preview": "gpt-4-vision-preview",
"gpt-4-1106-preview": "gpt-4-1106-preview",
"gpt-4o-2024-05-13": "gpt-4o-2024-05-13",
};
const azureConfig = {
apiKey: process.env.AZURE_OPENAI_KEY,
endpoint: process.env.AZURE_OPENAI_ENDPOINT,
apiVersion:
process.env.AZURE_OPENAI_API_VERSION || process.env.OPENAI_API_VERSION,
};
Settings.llm = new OpenAI({
model:
AZURE_OPENAI_MODEL_MAP[process.env.MODEL ?? "gpt-35-turbo"] ??
"gpt-3.5-turbo",
maxTokens: process.env.LLM_MAX_TOKENS
? Number(process.env.LLM_MAX_TOKENS)
: undefined,
azure: {
...azureConfig,
deployment: process.env.AZURE_OPENAI_LLM_DEPLOYMENT,
},
});
Settings.embedModel = new OpenAIEmbedding({
model: process.env.EMBEDDING_MODEL,
dimensions: process.env.EMBEDDING_DIM
? parseInt(process.env.EMBEDDING_DIM)
: undefined,
azure: {
...azureConfig,
deployment: process.env.AZURE_OPENAI_EMBEDDING_DEPLOYMENT,
},
});
}
function initOllama() {
const config = {
host: process.env.OLLAMA_BASE_URL ?? "http://127.0.0.1:11434",
};
Settings.llm = new Ollama({
model: process.env.MODEL ?? "",
config,
});
Settings.embedModel = new OllamaEmbedding({
model: process.env.EMBEDDING_MODEL ?? "",
config,
});
}
function initGroq() {
const embedModelMap: Record<string, string> = {
"all-MiniLM-L6-v2": "Xenova/all-MiniLM-L6-v2",
"all-mpnet-base-v2": "Xenova/all-mpnet-base-v2",
};
Settings.llm = new Groq({
model: process.env.MODEL!,
});
Settings.embedModel = new HuggingFaceEmbedding({
modelType: embedModelMap[process.env.EMBEDDING_MODEL!],
});
}
function initAnthropic() {
const embedModelMap: Record<string, string> = {
"all-MiniLM-L6-v2": "Xenova/all-MiniLM-L6-v2",
"all-mpnet-base-v2": "Xenova/all-mpnet-base-v2",
};
Settings.llm = new Anthropic({
model: process.env.MODEL as keyof typeof ALL_AVAILABLE_ANTHROPIC_MODELS,
});
Settings.embedModel = new HuggingFaceEmbedding({
modelType: embedModelMap[process.env.EMBEDDING_MODEL!],
});
}
function initGemini() {
Settings.llm = new Gemini({
model: process.env.MODEL as GEMINI_MODEL,
});
Settings.embedModel = new GeminiEmbedding({
model: process.env.EMBEDDING_MODEL as GEMINI_EMBEDDING_MODEL,
});
}
function initMistralAI() {
Settings.llm = new MistralAI({
model: process.env.MODEL as keyof typeof ALL_AVAILABLE_MISTRAL_MODELS,
});
Settings.embedModel = new MistralAIEmbedding({
model: process.env.EMBEDDING_MODEL as MistralAIEmbeddingModelType,
});
}
@@ -1,7 +1,7 @@
/* eslint-disable turbo/no-undeclared-env-vars */
import { AstraDBVectorStore } from "@llamaindex/astra";
import * as dotenv from "dotenv";
import { VectorStoreIndex, storageContextFromDefaults } from "llamaindex";
import { AstraDBVectorStore } from "llamaindex/vector-store/AstraDBVectorStore";
import { getDocuments } from "./loader";
import { initSettings } from "./settings";
import { checkRequiredEnvVars } from "./shared";
@@ -1,6 +1,6 @@
/* eslint-disable turbo/no-undeclared-env-vars */
import { AstraDBVectorStore } from "@llamaindex/astra";
import { VectorStoreIndex } from "llamaindex";
import { AstraDBVectorStore } from "llamaindex/vector-store/AstraDBVectorStore";
import { checkRequiredEnvVars } from "./shared";
export async function getDataSource(params?: any) {
@@ -1,7 +1,7 @@
/* eslint-disable turbo/no-undeclared-env-vars */
import { ChromaVectorStore } from "@llamaindex/chroma";
import * as dotenv from "dotenv";
import { VectorStoreIndex, storageContextFromDefaults } from "llamaindex";
import { ChromaVectorStore } from "llamaindex/vector-store/ChromaVectorStore";
import { getDocuments } from "./loader";
import { initSettings } from "./settings";
import { checkRequiredEnvVars } from "./shared";
@@ -1,6 +1,6 @@
/* eslint-disable turbo/no-undeclared-env-vars */
import { ChromaVectorStore } from "@llamaindex/chroma";
import { VectorStoreIndex } from "llamaindex";
import { ChromaVectorStore } from "llamaindex/vector-store/ChromaVectorStore";
import { checkRequiredEnvVars } from "./shared";
export async function getDataSource(params?: any) {
@@ -1,7 +1,7 @@
/* eslint-disable turbo/no-undeclared-env-vars */
import { MilvusVectorStore } from "@llamaindex/milvus";
import * as dotenv from "dotenv";
import { VectorStoreIndex, storageContextFromDefaults } from "llamaindex";
import { MilvusVectorStore } from "llamaindex/vector-store/MilvusVectorStore";
import { getDocuments } from "./loader";
import { initSettings } from "./settings";
import { checkRequiredEnvVars, getMilvusClient } from "./shared";
@@ -1,5 +1,5 @@
import { MilvusVectorStore } from "@llamaindex/milvus";
import { VectorStoreIndex } from "llamaindex";
import { MilvusVectorStore } from "llamaindex/vector-store/MilvusVectorStore";
import { checkRequiredEnvVars, getMilvusClient } from "./shared";
export async function getDataSource(params?: any) {
@@ -1,7 +1,7 @@
/* eslint-disable turbo/no-undeclared-env-vars */
import { MongoDBAtlasVectorSearch } from "@llamaindex/mongodb";
import * as dotenv from "dotenv";
import { storageContextFromDefaults, VectorStoreIndex } from "llamaindex";
import { MongoDBAtlasVectorSearch } from "llamaindex/vector-store/MongoDBAtlasVectorStore";
import { MongoClient } from "mongodb";
import { getDocuments } from "./loader";
import { initSettings } from "./settings";
@@ -1,6 +1,6 @@
/* eslint-disable turbo/no-undeclared-env-vars */
import { MongoDBAtlasVectorSearch } from "@llamaindex/mongodb";
import { VectorStoreIndex } from "llamaindex";
import { MongoDBAtlasVectorSearch } from "llamaindex/vector-store/MongoDBAtlasVectorStore";
import { MongoClient } from "mongodb";
import { checkRequiredEnvVars, POPULATED_METADATA_FIELDS } from "./shared";
@@ -1,6 +1,6 @@
import { PGVectorStore } from "@llamaindex/postgres";
import * as dotenv from "dotenv";
import { VectorStoreIndex, storageContextFromDefaults } from "llamaindex";
import { PGVectorStore } from "llamaindex/vector-store/PGVectorStore";
import { getDocuments } from "./loader";
import { initSettings } from "./settings";
import {
@@ -1,5 +1,5 @@
import { PGVectorStore } from "@llamaindex/postgres";
import { VectorStoreIndex } from "llamaindex";
import { PGVectorStore } from "llamaindex/vector-store/PGVectorStore";
import {
PGVECTOR_SCHEMA,
PGVECTOR_TABLE,
@@ -1,7 +1,7 @@
/* eslint-disable turbo/no-undeclared-env-vars */
import { PineconeVectorStore } from "@llamaindex/pinecone";
import * as dotenv from "dotenv";
import { VectorStoreIndex, storageContextFromDefaults } from "llamaindex";
import { PineconeVectorStore } from "llamaindex/vector-store/PineconeVectorStore";
import { getDocuments } from "./loader";
import { initSettings } from "./settings";
import { checkRequiredEnvVars } from "./shared";
@@ -1,6 +1,6 @@
/* eslint-disable turbo/no-undeclared-env-vars */
import { PineconeVectorStore } from "@llamaindex/pinecone";
import { VectorStoreIndex } from "llamaindex";
import { PineconeVectorStore } from "llamaindex/vector-store/PineconeVectorStore";
import { checkRequiredEnvVars } from "./shared";
export async function getDataSource(params?: any) {
@@ -1,7 +1,7 @@
/* eslint-disable turbo/no-undeclared-env-vars */
import { QdrantVectorStore } from "@llamaindex/qdrant";
import * as dotenv from "dotenv";
import { VectorStoreIndex, storageContextFromDefaults } from "llamaindex";
import { QdrantVectorStore } from "llamaindex/vector-store/QdrantVectorStore";
import { getDocuments } from "./loader";
import { initSettings } from "./settings";
import { checkRequiredEnvVars, getQdrantClient } from "./shared";
@@ -1,6 +1,6 @@
import { QdrantVectorStore } from "@llamaindex/qdrant";
import * as dotenv from "dotenv";
import { VectorStoreIndex } from "llamaindex";
import { QdrantVectorStore } from "llamaindex/vector-store/QdrantVectorStore";
import { checkRequiredEnvVars, getQdrantClient } from "./shared";
dotenv.config();
@@ -1,10 +1,7 @@
/* eslint-disable turbo/no-undeclared-env-vars */
import { WeaviateVectorStore } from "@llamaindex/weaviate";
import * as dotenv from "dotenv";
import {
VectorStoreIndex,
WeaviateVectorStore,
storageContextFromDefaults,
} from "llamaindex";
import { VectorStoreIndex, storageContextFromDefaults } from "llamaindex";
import { getDocuments } from "./loader";
import { initSettings } from "./settings";
import { DEFAULT_INDEX_NAME, checkRequiredEnvVars } from "./shared";
@@ -1,6 +1,6 @@
import { WeaviateVectorStore } from "@llamaindex/weaviate";
import * as dotenv from "dotenv";
import { VectorStoreIndex } from "llamaindex";
import { WeaviateVectorStore } from "llamaindex/vector-store/WeaviateVectorStore";
import { checkRequiredEnvVars, DEFAULT_INDEX_NAME } from "./shared";
dotenv.config();
@@ -21,7 +21,7 @@
"dotenv": "^16.3.1",
"duck-duck-scrape": "^2.2.5",
"express": "^4.18.2",
"llamaindex": "0.8.2",
"llamaindex": "^0.9.1",
"pdf2json": "^3.0.5",
"ajv": "^8.12.0",
"@e2b/code-interpreter": "^1.0.4",
@@ -29,7 +29,8 @@
"@apidevtools/swagger-parser": "^10.1.0",
"formdata-node": "^6.0.3",
"marked": "^14.1.2",
"papaparse": "^5.4.1"
"papaparse": "^5.4.1",
"wikipedia": "^2.1.2"
},
"devDependencies": {
"@types/cors": "^2.8.16",
@@ -3,6 +3,7 @@ import logging
from fastapi import APIRouter
from app.engine.index import IndexConfig, get_index
from llama_index.core.base.base_query_engine import BaseQueryEngine
from llama_index.core.base.response.schema import Response
query_router = r = APIRouter()
@@ -25,5 +26,5 @@ async def query_request(
query: str,
) -> str:
query_engine = get_query_engine()
response = await query_engine.aquery(query)
response: Response = await query_engine.aquery(query)
return response.response
@@ -0,0 +1,21 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "app/globals.css",
"baseColor": "slate",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/app/components",
"utils": "@/app/components/ui/lib/utils",
"ui": "@/app/components/ui",
"lib": "@/app/components/ui/lib",
"hooks": "@/app/components/ui/hooks"
},
"iconLibrary": "lucide"
}
@@ -26,7 +26,7 @@
"duck-duck-scrape": "^2.2.5",
"formdata-node": "^6.0.3",
"got": "^14.4.1",
"llamaindex": "0.8.2",
"llamaindex": "^0.9.1",
"lucide-react": "^0.460.0",
"next": "^15.1.3",
"react": "^19.0.0",
@@ -36,7 +36,8 @@
"tailwind-merge": "^2.6.0",
"tiktoken": "^1.0.15",
"uuid": "^9.0.1",
"marked": "^14.1.2"
"marked": "^14.1.2",
"wikipedia": "^2.1.2"
},
"devDependencies": {
"@types/node": "^20.10.3",