├── README.md
├── backend
├── .gitignore
├── README.md
├── app
│ ├── __init__.py
│ ├── api
│ │ ├── __init__.py
│ │ └── routers
│ │ │ ├── __init__.py
│ │ │ └── chat.py
│ └── utils
│ │ ├── Test.ipynb
│ │ ├── __init__.py
│ │ ├── index.py
│ │ ├── json.py
│ │ ├── misc.py
│ │ ├── node_parsers
│ │ └── markdown.py
│ │ ├── test.py
│ │ ├── transformations
│ │ ├── __init__.py
│ │ ├── deduplicator.py
│ │ ├── hyperlinks_remover.py
│ │ ├── summarizer.py
│ │ ├── upserter.py
│ │ └── url_extractor.py
│ │ └── vectorstores
│ │ └── dummy.py
├── data
│ └── docs
│ │ ├── .gitignore
│ │ ├── _static
│ │ └── concepts
│ │ │ ├── indexing.jpg
│ │ │ ├── querying.jpg
│ │ │ └── rag.jpg
│ │ ├── getting_started
│ │ ├── _category_.yml
│ │ ├── concepts.md
│ │ ├── environments.md
│ │ ├── installation.mdx
│ │ └── starter.mdx
│ │ ├── introduction.md
│ │ ├── modules
│ │ ├── _category_.yml
│ │ ├── agent
│ │ │ ├── _category_.yml
│ │ │ ├── index.md
│ │ │ ├── multi_document_agent.mdx
│ │ │ ├── openai.mdx
│ │ │ ├── query_engine_tool.mdx
│ │ │ └── react_agent.mdx
│ │ ├── chat_engine.md
│ │ ├── data_index.md
│ │ ├── data_loader.mdx
│ │ ├── documents_and_nodes
│ │ │ ├── _category_.yml
│ │ │ ├── index.md
│ │ │ └── metadata_extraction.md
│ │ ├── embeddings
│ │ │ ├── _category_.yml
│ │ │ ├── available_embeddings
│ │ │ │ ├── _category_.yml
│ │ │ │ ├── huggingface.md
│ │ │ │ ├── mistral.md
│ │ │ │ ├── ollama.md
│ │ │ │ ├── openai.md
│ │ │ │ └── together.md
│ │ │ └── index.md
│ │ ├── evaluation
│ │ │ ├── _category_.yml
│ │ │ ├── index.md
│ │ │ └── modules
│ │ │ │ ├── _category_.yml
│ │ │ │ ├── correctness.md
│ │ │ │ ├── faithfulness.md
│ │ │ │ └── relevancy.md
│ │ ├── ingestion_pipeline
│ │ │ ├── _category_.yml
│ │ │ ├── index.md
│ │ │ └── transformations.md
│ │ ├── llamacloud.mdx
│ │ ├── llms
│ │ │ ├── _category_.yml
│ │ │ ├── available_llms
│ │ │ │ ├── _category_.yml
│ │ │ │ ├── anthropic.md
│ │ │ │ ├── azure.md
│ │ │ │ ├── fireworks.md
│ │ │ │ ├── groq.mdx
│ │ │ │ ├── llama2.md
│ │ │ │ ├── mistral.md
│ │ │ │ ├── ollama.md
│ │ │ │ ├── openai.md
│ │ │ │ ├── portkey.md
│ │ │ │ └── together.md
│ │ │ └── index.md
│ │ ├── node_parser.md
│ │ ├── node_postprocessors
│ │ │ ├── _category_.yml
│ │ │ ├── cohere_reranker.md
│ │ │ └── index.md
│ │ ├── prompt
│ │ │ ├── _category_.yml
│ │ │ └── index.md
│ │ ├── query_engines
│ │ │ ├── _category_.yml
│ │ │ ├── index.md
│ │ │ ├── metadata_filtering.md
│ │ │ └── router_query_engine.md
│ │ ├── response_synthesizer.md
│ │ ├── retriever.md
│ │ ├── storage.md
│ │ └── vector_stores
│ │ │ ├── _category_.yml
│ │ │ └── qdrant.md
│ │ └── observability
│ │ ├── _category_.yml
│ │ └── index.md
├── main.py
├── pipeline_storage
│ ├── docstore.json
│ └── llama_cache
├── poetry.lock
├── pyproject.toml
└── tests
│ └── __init__.py
└── frontend
├── .gitignore
├── README.md
├── app
├── components
│ ├── chat-section.tsx
│ ├── header.tsx
│ ├── transform.ts
│ └── ui
│ │ ├── README.md
│ │ ├── button.tsx
│ │ ├── chat
│ │ ├── chat-actions.tsx
│ │ ├── chat-avatar.tsx
│ │ ├── chat-input.tsx
│ │ ├── chat-message.tsx
│ │ ├── chat-messages.tsx
│ │ ├── chat.interface.ts
│ │ ├── codeblock.tsx
│ │ ├── index.ts
│ │ ├── markdown.tsx
│ │ └── use-copy-to-clipboard.tsx
│ │ ├── input.tsx
│ │ ├── lib
│ │ └── utils.ts
│ │ └── nodes.tsx
├── favicon.ico
├── globals.css
├── hooks
│ └── useNodes.ts
├── layout.tsx
└── page.tsx
├── next.config.js
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
└── llama.png
├── tailwind.config.ts
└── tsconfig.json
/README.md:
--------------------------------------------------------------------------------
1 | ### Advanced chatbot over LlamaIndex TS documentation 🔥
2 |
3 | https://github.com/rsrohan99/llamaindex-docs-agent/assets/62835870/42fbd1ba-c42f-4b86-b33e-093272d76639
4 |
5 | # Multi-document Agents
6 |
7 | This is a [LlamaIndex](https://www.llamaindex.ai/) project bootstrapped with [`create-llama`](https://github.com/run-llama/LlamaIndexTS/tree/main/packages/create-llama).
8 |
9 | This multi-document agent is built over the LlamaIndex.TS documentation.
10 |
11 | We use our multi-document agent architecture:
12 |
13 | - Individual query engine per document
14 | - Top level Orchestrator agent across documents that can pick relevant subsets
15 |
16 | This also streams _all_ intermediate results from the agent via a custom Callback handler.
17 |
18 | We use this Custom Callback handler to also send intermediate nodes that are retrieved during retrieval of document level query engines, to the frontend.
19 |
20 | It allows us to show the relevant section of the documentation in the preview window.
21 |
22 | ## Main Files to Look At
23 |
24 | This extends beyond the simple `create-llama` example. To see changes, look at the following files:
25 |
26 | - `backend/app/utils/index.py` - contains core logic for constructing + getting multi-doc agent
27 | - `backend/app/api/routers/chat.py` - contains implementation of chat endpoint + threading to stream intermediate responses.
28 |
29 | We also created some custom `Transformations` that we use with out robust `IngestionPipeline`
30 |
31 | As we update the documentations in the `data` folder, this `IngestionPipeline` takes care of handling duplicates, applying our custom nodes transformation logic etc.
32 |
33 | The custom transformations we've used:
34 |
35 | - `Deduplicator` - handles duplicates.
36 | - `HyperlinksRemover` - cleans the markdown files.
37 | - `Summarizer` - creates summary of the node and adds that as a metadata.
38 | - `URLExtractor` - generates the url of a particular node section.
39 | - `Upserter` - updates the docstore with new and updated nodes, deletes old ones.
40 |
41 | ## Getting Started
42 |
43 | First, startup the backend as described in the [backend README](./backend/README.md).
44 |
45 | Second, run the development server of the frontend as described in the [frontend README](./frontend/README.md).
46 |
47 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
48 |
49 | ## Learn More
50 |
51 | To learn more about LlamaIndex, take a look at the following resources:
52 |
53 | - [LlamaIndex Documentation](https://docs.llamaindex.ai) - learn about LlamaIndex (Python features).
54 | - [LlamaIndexTS Documentation](https://ts.llamaindex.ai) - learn about LlamaIndex (Typescript features).
55 |
56 | You can check out [the LlamaIndexTS GitHub repository](https://github.com/run-llama/LlamaIndexTS) - your feedback and contributions are welcome!
57 |
--------------------------------------------------------------------------------
/backend/.gitignore:
--------------------------------------------------------------------------------
1 | __pycache__
2 | storage
3 | pipeline_storage
--------------------------------------------------------------------------------
/backend/README.md:
--------------------------------------------------------------------------------
1 | This is a [LlamaIndex](https://www.llamaindex.ai/) project using [FastAPI](https://fastapi.tiangolo.com/) bootstrapped with [`create-llama`](https://github.com/run-llama/LlamaIndexTS/tree/main/packages/create-llama).
2 |
3 | ## Getting Started
4 |
5 | First, setup the environment:
6 |
7 |
8 | ```
9 | poetry install
10 | poetry shell
11 | ```
12 |
13 | By default, we use the OpenAI LLM (though you can customize, see app/api/routers/chat.py). As a result you need to specify an `OPENAI_API_KEY` in an .env file in this directory.
14 |
15 | Example `backend/.env` file:
16 | ```
17 | OPENAI_API_KEY=
18 | ```
19 |
20 | Second, run the development server:
21 |
22 | ```
23 | python main.py
24 | ```
25 |
26 | Then call the API endpoint `/api/chat` to see the result:
27 |
28 | ```
29 | curl --location 'localhost:8000/api/chat' \
30 | --header 'Content-Type: application/json' \
31 | --data '{ "messages": [{ "role": "user", "content": "Hello" }] }'
32 | ```
33 |
34 | You can start editing the API by modifying `app/api/routers/chat.py`. The endpoint auto-updates as you save the file.
35 |
36 | Open [http://localhost:8000/docs](http://localhost:8000/docs) with your browser to see the Swagger UI of the API.
37 |
38 | The API allows CORS for all origins to simplify development. You can change this behavior by setting the `ENVIRONMENT` environment variable to `prod`:
39 |
40 | ```
41 | ENVIRONMENT=prod uvicorn main:app
42 | ```
43 |
44 | ## Learn More
45 |
46 | To learn more about LlamaIndex, take a look at the following resources:
47 |
48 | - [LlamaIndex Documentation](https://docs.llamaindex.ai) - learn about LlamaIndex.
49 |
50 | You can check out [the LlamaIndex GitHub repository](https://github.com/run-llama/llama_index) - your feedback and contributions are welcome!
51 |
--------------------------------------------------------------------------------
/backend/app/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rsrohan99/llamaindex-docs-agent/1b2a6f5efee1bd5a4b9cd7771eac5f76a57155b1/backend/app/__init__.py
--------------------------------------------------------------------------------
/backend/app/api/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rsrohan99/llamaindex-docs-agent/1b2a6f5efee1bd5a4b9cd7771eac5f76a57155b1/backend/app/api/__init__.py
--------------------------------------------------------------------------------
/backend/app/api/routers/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rsrohan99/llamaindex-docs-agent/1b2a6f5efee1bd5a4b9cd7771eac5f76a57155b1/backend/app/api/routers/__init__.py
--------------------------------------------------------------------------------
/backend/app/api/routers/chat.py:
--------------------------------------------------------------------------------
1 | from typing import List, cast
2 |
3 | from fastapi.responses import StreamingResponse
4 |
5 | from threading import Thread
6 | from app.utils.json import json_to_model
7 | from app.utils.index import EventObject, get_agent
8 | from fastapi import APIRouter, Depends, HTTPException, Request, status
9 | from llama_index.core.base.llms.types import MessageRole, ChatMessage
10 | from llama_index.agent.openai import OpenAIAgent
11 | from llama_index.core.chat_engine.types import StreamingAgentChatResponse
12 | from pydantic import BaseModel
13 | import logging
14 | import json
15 |
16 | chat_router = r = APIRouter()
17 |
18 |
19 | class _Message(BaseModel):
20 | role: MessageRole
21 | content: str
22 |
23 |
24 | class _ChatData(BaseModel):
25 | messages: List[_Message]
26 |
27 |
28 | def convert_sse(obj: str | dict):
29 | """Convert the given object (or string) to a Server-Sent Event (SSE) event"""
30 | # print(obj)
31 | return "data: {}\n\n".format(json.dumps(obj))
32 |
33 |
34 | @r.post("")
35 | async def chat(
36 | request: Request,
37 | # Note: To support clients sending a JSON object using content-type "text/plain",
38 | # we need to use Depends(json_to_model(_ChatData)) here
39 | data: _ChatData = Depends(json_to_model(_ChatData)),
40 | agent: OpenAIAgent = Depends(get_agent),
41 | ):
42 | logger = logging.getLogger("uvicorn")
43 | # check preconditions and get last message
44 | if len(data.messages) == 0:
45 | raise HTTPException(
46 | status_code=status.HTTP_400_BAD_REQUEST,
47 | detail="No messages provided",
48 | )
49 | lastMessage = data.messages.pop()
50 | if lastMessage.role != MessageRole.USER:
51 | raise HTTPException(
52 | status_code=status.HTTP_400_BAD_REQUEST,
53 | detail="Last message must be from user",
54 | )
55 | # convert messages coming from the request to type ChatMessage
56 | messages = [
57 | ChatMessage(
58 | role=m.role,
59 | content=m.content,
60 | )
61 | for m in data.messages
62 | ]
63 |
64 | # start a new thread here to query chat engine
65 | thread = Thread(target=agent.stream_chat, args=(lastMessage.content, messages))
66 | thread.start()
67 | logger.info("Querying chat engine")
68 | # response = agent.stream_chat(lastMessage.content, messages)
69 | # response = agent.chat(lastMessage.content, messages)
70 | # logger.info(response)
71 |
72 | # stream response
73 | # NOTE: changed to sync due to issues with blocking the event loop
74 | # see https://stackoverflow.com/a/75760884
75 | def event_generator():
76 | queue = agent.callback_manager.handlers[0].queue
77 |
78 | # stream response
79 | while True:
80 | next_item = queue.get(True, 60.0) # set a generous timeout of 60 seconds
81 | # check type of next_item, if string or not
82 | if isinstance(next_item, EventObject):
83 | yield convert_sse(dict(next_item))
84 | elif isinstance(next_item, StreamingAgentChatResponse):
85 | response = cast(StreamingAgentChatResponse, next_item)
86 | for token in response.response_gen:
87 | yield convert_sse(token)
88 | break
89 |
90 | return StreamingResponse(event_generator(), media_type="text/event-stream")
91 |
--------------------------------------------------------------------------------
/backend/app/utils/Test.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [],
3 | "metadata": {},
4 | "nbformat": 4,
5 | "nbformat_minor": 5
6 | }
7 |
--------------------------------------------------------------------------------
/backend/app/utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rsrohan99/llamaindex-docs-agent/1b2a6f5efee1bd5a4b9cd7771eac5f76a57155b1/backend/app/utils/__init__.py
--------------------------------------------------------------------------------
/backend/app/utils/index.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import os
3 | from pydantic import BaseModel
4 | import shutil
5 | from queue import Queue
6 |
7 | from llama_index.agent.openai import OpenAIAgent
8 | from llama_index.core import VectorStoreIndex
9 | from llama_index.core.objects import ObjectIndex, SimpleToolNodeMapping
10 | from llama_index.core.tools import QueryEngineTool, ToolMetadata
11 | from llama_index.llms.openai import OpenAI
12 | from llama_index.embeddings.openai import OpenAIEmbedding
13 | from llama_index.readers.file import FlatReader
14 | from llama_index.core.callbacks import CallbackManager
15 | from llama_index.core.callbacks.base_handler import BaseCallbackHandler
16 | from llama_index.core.callbacks.schema import CBEventType
17 | from llama_index.core.readers import SimpleDirectoryReader
18 | from llama_index.core.storage.docstore import SimpleDocumentStore
19 | from llama_index.core.ingestion import IngestionPipeline
20 | from llama_index.core.schema import TextNode
21 | from llama_index.core.settings import Settings
22 | from llama_index.core.indices import load_index_from_storage
23 | from llama_index.core.storage import StorageContext
24 | from llama_index.core.storage.docstore.types import BaseDocumentStore
25 | from llama_index.core.callbacks.schema import EventPayload
26 | from llama_index.core.schema import NodeWithScore
27 |
28 | from app.utils.node_parsers.markdown import CustomMarkdownNodeParser
29 | from app.utils.transformations import URLExtractor, Deduplicator, Upserter
30 | from app.utils.transformations import HyperlinksRemover, DocsSummarizer
31 | from app.utils.misc import get_max_h_value
32 |
33 | from typing import Optional, Dict, Any, List, Tuple
34 |
35 | import os
36 |
37 |
38 | PIPELINE_STORAGE_DIR = "./pipeline_storage" # directory to cache the ingestion pipeline
39 | STORAGE_DIR = "./storage"
40 | DATA_DIR = "./data" # directory containing the documents to index
41 |
42 |
43 | class EventObject(BaseModel):
44 | """
45 | Represents an event from the LlamaIndex callback handler.
46 |
47 | Attributes:
48 | type (str): The type of the event, e.g. "function_call".
49 | payload (dict): The payload associated with the event.
50 | """
51 |
52 | type: str
53 | payload: dict
54 |
55 |
56 | class StreamingCallbackHandler(BaseCallbackHandler):
57 | """Callback handler specifically designed to stream function calls to a queue."""
58 |
59 | def __init__(self, queue: Queue) -> None:
60 | """Initialize the base callback handler."""
61 | super().__init__([], [])
62 | self._queue = queue
63 | self._counter = 0
64 |
65 | def on_event_start(
66 | self,
67 | event_type: CBEventType,
68 | payload: Optional[Dict[str, Any]] = None,
69 | event_id: str = "",
70 | parent_id: str = "",
71 | **kwargs: Any,
72 | ) -> str:
73 | """Run when an event starts and return id of event."""
74 | if event_type == CBEventType.FUNCTION_CALL:
75 | self._queue.put(
76 | EventObject(
77 | type="function_call",
78 | payload={
79 | "arguments_str": payload["function_call"],
80 | "tool_str": payload["tool"].name,
81 | },
82 | )
83 | )
84 |
85 | def on_event_end(
86 | self,
87 | event_type: CBEventType,
88 | payload: Optional[Dict[str, Any]] = None,
89 | event_id: str = "",
90 | **kwargs: Any,
91 | ) -> None:
92 | # print(event_type)
93 | # print(payload)
94 | """Run when an event ends."""
95 | # if event_type == CBEventType.FUNCTION_CALL:
96 | # # print(payload)
97 | # self._queue.put(
98 | # EventObject(
99 | # type="function_call_response",
100 | # payload={"response": payload["function_call_response"]},
101 | # )
102 | # )
103 | if event_type == CBEventType.AGENT_STEP:
104 | # put LLM response into queue
105 | self._queue.put(payload["response"])
106 | elif event_type == CBEventType.RETRIEVE:
107 | nodes_with_scores: list[NodeWithScore] = payload[EventPayload.NODES]
108 | nodes_to_return = []
109 | for node_with_score in nodes_with_scores:
110 | node = node_with_score.node
111 | node_meta = node.metadata
112 | # print(node_meta)
113 | if 'section_link' in node_meta:
114 | nodes_to_return.append({
115 | "id": node.id_,
116 | "title": get_max_h_value(node_meta)
117 | or node_meta['file_path'],
118 | "url": node.metadata['file_path'],
119 | "section": node.metadata['section_link'],
120 | "summary": node.metadata['summary'],
121 | })
122 | # print(nodes_to_return)
123 | self._queue.put(
124 | EventObject(
125 | type="nodes_retrieved",
126 | payload={
127 | "nodes": nodes_to_return
128 | }
129 | )
130 | )
131 |
132 | @property
133 | def queue(self) -> Queue:
134 | """Get the queue of events."""
135 | return self._queue
136 |
137 | @property
138 | def counter(self) -> int:
139 | """Get the counter."""
140 | return self._counter
141 |
142 | def start_trace(self, trace_id: Optional[str] = None) -> None:
143 | """Run when an overall trace is launched."""
144 | pass
145 |
146 | def end_trace(
147 | self,
148 | trace_id: Optional[str] = None,
149 | trace_map: Optional[Dict[str, List[str]]] = None,
150 | ) -> None:
151 | """Run when an overall trace is exited."""
152 | pass
153 |
154 |
155 | def clean_old_persisted_indices(doc_id:str):
156 | dir_to_delete = f"{STORAGE_DIR}"
157 | if os.path.exists(dir_to_delete):
158 | print(f"Deleting dir: {dir_to_delete}")
159 | shutil.rmtree(dir_to_delete)
160 |
161 | async def ingest(directory:str, docstore: BaseDocumentStore)->TextNode:
162 | reader = SimpleDirectoryReader(
163 | input_dir=directory,
164 | recursive=True,
165 | required_exts=[".md", ".mdx"],
166 | file_extractor={
167 | ".md": FlatReader(),
168 | ".mdx": FlatReader()
169 | }
170 | )
171 |
172 | docs = reader.load_data()
173 |
174 | deduplicator = Deduplicator(
175 | cleanup_fn=clean_old_persisted_indices,
176 | docstore=docstore,
177 | )
178 | url_extractor = URLExtractor(data_path=DATA_DIR)
179 | hyperlinks_remover = HyperlinksRemover()
180 | summarizer = DocsSummarizer(
181 | llm="gpt-3.5-turbo-0125"
182 | )
183 | upserter = Upserter(
184 | docstore=docstore,
185 | persist_dir=PIPELINE_STORAGE_DIR
186 | )
187 |
188 | pipeline = IngestionPipeline(
189 | transformations=[
190 | deduplicator,
191 | hyperlinks_remover,
192 | summarizer,
193 | url_extractor,
194 | upserter
195 | ],
196 | )
197 |
198 | if os.path.exists(PIPELINE_STORAGE_DIR):
199 | pipeline.load(PIPELINE_STORAGE_DIR)
200 | nodes = await pipeline.arun(documents=docs)
201 | else:
202 | nodes = await pipeline.arun(documents=docs)
203 | pipeline.persist(PIPELINE_STORAGE_DIR)
204 |
205 | print(f"new nodes: {len(nodes)}")
206 | if len(nodes) > 0:
207 | # print(nodes)
208 | docstore.add_documents(nodes)
209 | docstore.persist(
210 | persist_path=os.path.join(PIPELINE_STORAGE_DIR, "docstore.json")
211 | )
212 | # if os.path.exists(STORAGE_DIR):
213 | # print(f"Deleting {STORAGE_DIR}")
214 | # shutil.rmtree(STORAGE_DIR)
215 | all_doc_ids = docstore.get_all_document_hashes().values()
216 | # print(all_doc_ids)
217 | return [docstore.get_document(doc_id=id) for id in all_doc_ids]
218 |
219 |
220 | def _build_document_agents(
221 | storage_dir: str, docs: list[TextNode], callback_manager: CallbackManager
222 | ) -> Dict:
223 | """Build document agents."""
224 | node_parser = CustomMarkdownNodeParser()
225 | llm = OpenAI(temperature=0, model="gpt-3.5-turbo-0125")
226 | embed_model = OpenAIEmbedding(
227 | model="text-embedding-3-small"
228 | )
229 | Settings.llm = llm
230 | Settings.embed_model = embed_model
231 | # Settings.callback_manager = callback_manager
232 | # service_context = ServiceContext.from_defaults(llm=llm)
233 |
234 | # Build agents dictionary
235 | agents = {}
236 |
237 | # this is for the baseline
238 | all_nodes = []
239 |
240 | for idx, doc in enumerate(docs):
241 | nodes = node_parser.get_nodes_from_documents([doc])
242 | all_nodes.extend(nodes)
243 |
244 | if not os.path.exists(f"./{storage_dir}/{doc.id_}"):
245 | # build vector index
246 | vector_index = VectorStoreIndex(
247 | nodes,
248 | )
249 | vector_index.storage_context.persist(
250 | persist_dir=f"./{storage_dir}/{doc.id_}"
251 | )
252 | else:
253 | vector_index = load_index_from_storage(
254 | StorageContext.from_defaults(
255 | persist_dir=f"./{storage_dir}/{doc.id_}"
256 | ),
257 | )
258 |
259 | # define query engines
260 | vector_index._callback_manager = callback_manager
261 | vector_query_engine = vector_index.as_query_engine()
262 | agents[doc.id_] = vector_query_engine
263 |
264 | return agents
265 |
266 |
267 | def _build_top_agent(
268 | storage_dir: str, doc_agents: Dict, docstore: BaseDocumentStore,
269 | callback_manager: CallbackManager
270 | ) -> OpenAIAgent:
271 | """Build top-level agent."""
272 | # define tool for each document agent
273 | all_tools = []
274 | for doc_id in doc_agents.keys():
275 | doc = docstore.get_document(doc_id=doc_id)
276 | assert doc is not None
277 | wiki_summary = (
278 | f"This is the brief summary of one LlamaIndex documentation page: \"{doc.metadata['summary']}\". Use"
279 | f" this tool if you want to answer any questions about topics from the above summary. Please ask this tool a clearly specified elaborate query while using it and fully utilize it's response to answer the final query. Example input to this tool -> input: \"install llamaindex using node js?\". Don't use 1-2 word queries\n"
280 | )
281 | doc_tool = QueryEngineTool(
282 | query_engine=doc_agents[doc_id],
283 | metadata=ToolMetadata(
284 | name=f"page_{doc.metadata['filename'].split('.')[0]}",
285 | description=wiki_summary,
286 | ),
287 | )
288 | all_tools.append(doc_tool)
289 | tool_mapping = SimpleToolNodeMapping.from_objects(all_tools)
290 | # if obj_index doesn't already exist
291 | if not os.path.exists(f"./{storage_dir}/top"):
292 | storage_context = StorageContext.from_defaults()
293 | obj_index = ObjectIndex.from_objects(
294 | all_tools, tool_mapping, VectorStoreIndex, storage_context=storage_context
295 | )
296 | storage_context.persist(persist_dir=f"./{storage_dir}/top")
297 | # TODO: don't access private property
298 |
299 | else:
300 | # initialize storage context from existing storage
301 | storage_context = StorageContext.from_defaults(
302 | persist_dir=f"./{storage_dir}/top"
303 | )
304 | index = load_index_from_storage(storage_context)
305 | obj_index = ObjectIndex(index, tool_mapping)
306 |
307 | top_agent = OpenAIAgent.from_tools(
308 | tool_retriever=obj_index.as_retriever(similarity_top_k=7),
309 | system_prompt=""" \
310 | You are an agent designed to answer queries about a Generative AI framework, LlamaIndex.
311 | Please always use the tools provided to answer a question. Do not rely on prior knowledge. Pass the provided tools with clear and elaborate queries (e.g. "install llamaindex using node js?") and then fully utilize their response to answer the original query. When using multiple tools, break the original query into multiple elaborate queries and pass them to the respective tool as input. Be sure to make the inputs long and elaborate to capture entire context of the query. Don't use 1-2 word queries.\
312 |
313 | """,
314 | verbose=True,
315 | callback_manager=callback_manager,
316 | )
317 |
318 | return top_agent
319 |
320 |
321 | async def get_agent():
322 | logger = logging.getLogger("uvicorn")
323 |
324 | if os.path.exists(PIPELINE_STORAGE_DIR):
325 | docstore = SimpleDocumentStore.from_persist_dir(PIPELINE_STORAGE_DIR)
326 | else:
327 | docstore = SimpleDocumentStore()
328 |
329 | docs = await ingest(
330 | # directory=f'{DATA_DIR}/docs/modules/ingestion_pipeline',
331 | # directory=f'{DATA_DIR}/docs/getting_started',
332 | directory=f'{DATA_DIR}/docs/modules',
333 | docstore=docstore,
334 | )
335 |
336 | # define callback manager with streaming
337 | queue = Queue()
338 | handler = StreamingCallbackHandler(queue)
339 | callback_manager = CallbackManager([handler])
340 |
341 | # build agent for each document
342 | doc_agents = _build_document_agents(
343 | STORAGE_DIR, docs, callback_manager=callback_manager
344 | )
345 |
346 | # build top-level agent
347 | top_agent = _build_top_agent(
348 | STORAGE_DIR, doc_agents, docstore, callback_manager
349 | )
350 |
351 | logger.info(f"Built agent.")
352 |
353 | return top_agent
354 |
--------------------------------------------------------------------------------
/backend/app/utils/json.py:
--------------------------------------------------------------------------------
1 | import json
2 | from typing import TypeVar
3 | from fastapi import HTTPException, Request
4 |
5 | from pydantic import BaseModel, ValidationError
6 |
7 |
8 | T = TypeVar("T", bound=BaseModel)
9 |
10 |
11 | def json_to_model(cls: T):
12 | async def get_json(request: Request) -> T:
13 | body = await request.body()
14 | try:
15 | data_dict = json.loads(body.decode("utf-8"))
16 | return cls(**data_dict)
17 | except (json.JSONDecodeError, ValidationError) as e:
18 | raise HTTPException(
19 | status_code=400, detail=f"Could not decode JSON: {str(e)}"
20 | )
21 |
22 | return get_json
23 |
--------------------------------------------------------------------------------
/backend/app/utils/misc.py:
--------------------------------------------------------------------------------
1 | def get_max_h_value(dic):
2 | h_keys = [
3 | key for key in dic.keys()
4 | if key.startswith('Header ') and key.split(' ')[-1].isdigit()
5 | ]
6 | if not h_keys:
7 | return None # No keys with 'Header ' pattern found
8 | max_key = max(h_keys, key=lambda k: int(k.split(' ')[-1]))
9 | return dic[max_key]
--------------------------------------------------------------------------------
/backend/app/utils/node_parsers/markdown.py:
--------------------------------------------------------------------------------
1 | """Custom Markdown node parser."""
2 | import re
3 | from llama_index.core.node_parser import MarkdownNodeParser
4 | from llama_index.core.node_parser.node_utils import build_nodes_from_splits
5 | from llama_index.core.schema import BaseNode, TextNode
6 | from llama_index.readers.file import MarkdownReader
7 |
8 | def generate_markdown_header_id(header_text, previous_ids=None):
9 | if previous_ids is None:
10 | previous_ids = set()
11 |
12 | # Step 1: Convert to lowercase
13 | id = header_text.lower()
14 |
15 | # Step 2: Remove non-word text
16 | id = re.sub(r'\W+', ' ', id)
17 |
18 | # Step 3: Convert spaces to hyphens
19 | id = id.strip().replace(' ', '-')
20 |
21 | # Step 4: Remove multiple consecutive hyphens
22 | id = re.sub(r'-+', '-', id)
23 |
24 | # Step 5 & 6: Ensure uniqueness
25 | original_id = id
26 | counter = 1
27 | while id in previous_ids:
28 | id = f"{original_id}-{counter}"
29 | counter += 1
30 |
31 | # Add the new unique id to the set of previous ids
32 | # previous_ids.add(id)
33 |
34 | return id
35 |
36 | class CustomMarkdownNodeParser(MarkdownNodeParser):
37 | """Markdown node parser.
38 |
39 | Splits a document into Nodes using custom Markdown splitting logic.
40 |
41 | Args:
42 | include_metadata (bool): whether to include metadata in nodes
43 | include_prev_next_rel (bool): whether to include prev/next relationships
44 |
45 | """
46 |
47 | def _update_metadata(
48 | self, headers_metadata: dict, new_header: str, new_header_level: int
49 | ) -> dict:
50 | """Update the markdown headers for metadata.
51 |
52 | Removes all headers that are equal or less than the level
53 | of the newly found header
54 | """
55 | updated_headers = {}
56 |
57 | for i in range(1, new_header_level):
58 | key = f"Header {i}"
59 | if key in headers_metadata:
60 | updated_headers[key] = headers_metadata[key]
61 |
62 | updated_headers[f"Header {new_header_level}"] = new_header
63 | # print(updated_headers)
64 | updated_headers['section_link'] \
65 | = generate_markdown_header_id(new_header, updated_headers.values())
66 | return updated_headers
67 |
68 | def _build_node_from_split(
69 | self,
70 | text_split: str,
71 | node: BaseNode,
72 | metadata: dict,
73 | ) -> TextNode:
74 | """Build node from single text split."""
75 | md_reader = MarkdownReader()
76 |
77 | node = build_nodes_from_splits([text_split], node, id_func=self.id_func)[0]
78 |
79 | node.text = md_reader.remove_hyperlinks(node.text)
80 | node.text = md_reader.remove_images(node.text)
81 |
82 | if self.include_metadata:
83 | node.metadata = {**node.metadata, **metadata}
84 |
85 | return node
--------------------------------------------------------------------------------
/backend/app/utils/test.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | from dotenv import load_dotenv
4 |
5 | from llama_index.core.readers import SimpleDirectoryReader
6 | from llama_index.readers.file import FlatReader
7 | from llama_index.core.schema import MetadataMode
8 | from llama_index.core.ingestion import IngestionPipeline
9 | from llama_index.core.storage.docstore import SimpleDocumentStore
10 |
11 | from app.utils.node_parsers.markdown import CustomMarkdownNodeParser
12 | from app.utils.transformations import URLExtractor, Deduplicator, Upserter
13 | from app.utils.transformations import HyperlinksRemover, DocsSummarizer
14 |
15 | load_dotenv()
16 |
17 | DATA_PATH="./data"
18 | PIPELINE_STORAGE_PATH="./pipeline_storage"
19 | def main():
20 | reader = SimpleDirectoryReader(
21 | input_dir=f'{DATA_PATH}/docs/getting_started',
22 | required_exts=[".md", ".mdx"],
23 | file_extractor={
24 | ".md": FlatReader(),
25 | ".mdx": FlatReader()
26 | }
27 | )
28 |
29 | docs = reader.load_data()
30 | # for doc in docs:
31 | # doc.metadata["url_path"], _ = splitext(relpath(
32 | # doc.metadata['file_path'], DATA_PATH+"/docs"
33 | # ))
34 |
35 |
36 | print(len(docs))
37 | # print(docs[1])
38 | if os.path.exists(PIPELINE_STORAGE_PATH):
39 | docstore = SimpleDocumentStore.from_persist_dir(PIPELINE_STORAGE_PATH)
40 | else:
41 | docstore = SimpleDocumentStore()
42 |
43 | deduplicator = Deduplicator(
44 | docstore=docstore,
45 | )
46 | hyperlinks_remover = HyperlinksRemover()
47 | url_extractor = URLExtractor(data_path=DATA_PATH)
48 | summarizer = DocsSummarizer(
49 | llm="gpt-3.5-turbo-0125"
50 | )
51 | upserter = Upserter(
52 | docstore=docstore,
53 | persist_dir=PIPELINE_STORAGE_PATH
54 | )
55 | node_parser = CustomMarkdownNodeParser()
56 |
57 | pipeline = IngestionPipeline(
58 | transformations=[
59 | deduplicator,
60 | hyperlinks_remover,
61 | summarizer,
62 | url_extractor,
63 | upserter
64 | ],
65 | )
66 |
67 | if os.path.exists(PIPELINE_STORAGE_PATH):
68 | pipeline.load(PIPELINE_STORAGE_PATH)
69 | pipeline.run(documents=docs)
70 | else:
71 | pipeline.run(documents=docs)
72 | pipeline.persist(PIPELINE_STORAGE_PATH)
73 |
74 |
75 | # nodes = node_parser.get_nodes_from_documents(docs)
76 | all_doc_ids = docstore.get_all_document_hashes().values()
77 | print(all_doc_ids)
78 | all_docs = [docstore.get_document(doc_id=id) for id in docstore.get_all_document_hashes().values()]
79 | # print(all_docs.get_content(metadata_mode=MetadataMode.ALL))
80 |
81 | print(len(all_docs))
82 | # print(nodes[2:6])
83 | # print(all_docs[2].get_content(metadata_mode=MetadataMode.ALL))
84 | print(all_docs[2].metadata)
--------------------------------------------------------------------------------
/backend/app/utils/transformations/__init__.py:
--------------------------------------------------------------------------------
1 | """Custom Transformations."""
2 |
3 | from app.utils.transformations.deduplicator import Deduplicator
4 | from app.utils.transformations.url_extractor import URLExtractor
5 | from app.utils.transformations.upserter import Upserter
6 | from app.utils.transformations.hyperlinks_remover import HyperlinksRemover
7 | from app.utils.transformations.summarizer import DocsSummarizer
8 |
9 | __all__ = [
10 | "Deduplicator",
11 | "URLExtractor",
12 | "Upserter",
13 | "DocsSummarizer",
14 | "HyperlinksRemover",
15 | ]
--------------------------------------------------------------------------------
/backend/app/utils/transformations/deduplicator.py:
--------------------------------------------------------------------------------
1 | from typing import Callable
2 |
3 | from llama_index.core.schema import TransformComponent
4 | from llama_index.core.storage.docstore.types import BaseDocumentStore
5 | from llama_index.core.bridge.pydantic import Field
6 |
7 | class Deduplicator(TransformComponent):
8 | """Deduplicate documents and also delete old and updated ones."""
9 |
10 | docstore: BaseDocumentStore = Field(
11 | description='Document store to check for duplicates'
12 | )
13 |
14 | cleanup_fn: Callable = Field(
15 | default=lambda _:...,
16 | description="after deleting missing nodes, call this function."
17 | )
18 |
19 | def __call__(self, nodes, **kwargs):
20 | assert self.docstore is not None
21 |
22 | existing_doc_ids_before = set(
23 | self.docstore.get_all_document_hashes().values()
24 | )
25 | doc_ids_from_nodes = set()
26 | deduped_nodes_to_run = {}
27 |
28 | for node in nodes:
29 | ref_doc_id = node.ref_doc_id if node.ref_doc_id else node.id_
30 | doc_ids_from_nodes.add(ref_doc_id)
31 | existing_hash = self.docstore.get_document_hash(ref_doc_id)
32 | if not existing_hash:
33 | # document doesn't exist, so add it
34 | print(f"new document {ref_doc_id}")
35 | self.docstore.set_document_hash(ref_doc_id, node.hash)
36 | deduped_nodes_to_run[ref_doc_id] = node
37 | elif existing_hash and existing_hash != node.hash:
38 | print(f"updated document {ref_doc_id}")
39 | self.docstore.delete_ref_doc(ref_doc_id, raise_error=False)
40 | self.docstore.set_document_hash(ref_doc_id, node.hash)
41 | deduped_nodes_to_run[ref_doc_id] = node
42 | self.cleanup_fn(ref_doc_id)
43 | else:
44 | print(f"skipping document {ref_doc_id}")
45 | continue # document exists and is unchanged, so skip it
46 |
47 | doc_ids_to_delete = existing_doc_ids_before - doc_ids_from_nodes
48 | for ref_doc_id in doc_ids_to_delete:
49 | print(f"deleting missing document {ref_doc_id}")
50 | self.docstore.delete_document(ref_doc_id)
51 | self.cleanup_fn(ref_doc_id)
52 |
53 | nodes_to_return = list(deduped_nodes_to_run.values())
54 | if len(nodes_to_return) > 0:
55 | self.cleanup_fn(ref_doc_id)
56 |
57 |
58 | return nodes_to_return
--------------------------------------------------------------------------------
/backend/app/utils/transformations/hyperlinks_remover.py:
--------------------------------------------------------------------------------
1 | from llama_index.core.schema import TransformComponent
2 | from llama_index.core.bridge.pydantic import Field
3 | from llama_index.readers.file import MarkdownReader
4 |
5 | class HyperlinksRemover(TransformComponent):
6 | """Remove hyperlinks and images from md or mdx."""
7 |
8 | def __call__(self, nodes, **kwargs):
9 | md_reader = MarkdownReader()
10 | for node in nodes:
11 | node.text = md_reader.remove_hyperlinks(node.text)
12 | node.text = md_reader.remove_images(node.text)
13 | # print(node.metadata)
14 | return nodes
--------------------------------------------------------------------------------
/backend/app/utils/transformations/summarizer.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 |
3 | from llama_index.core.schema import TransformComponent
4 | from llama_index.core.bridge.pydantic import Field
5 | from llama_index.core.response_synthesizers import TreeSummarize
6 | from llama_index.llms.openai import OpenAI
7 |
8 | class DocsSummarizer(TransformComponent):
9 | """Summarize current documentation page."""
10 |
11 | llm: str = Field(
12 | default='gpt-3.5-turbo',
13 | description='LLM to summarize'
14 | )
15 |
16 | async def generate_summary(self, node, summarizer, prompt):
17 | print(f"getting summary for {node.id_}")
18 | summary = await summarizer.aget_response(prompt, [node.text])
19 | node.metadata['summary'] = summary
20 |
21 | async def process_nodes(self, nodes, summarizer, prompt):
22 | tasks = []
23 | for node in nodes:
24 | task = asyncio.create_task(
25 | self.generate_summary(node, summarizer, prompt)
26 | )
27 | tasks.append(task)
28 | await asyncio.gather(*tasks)
29 |
30 |
31 | def __call__(self, nodes, **kwargs):
32 | print('calling')
33 |
34 | async def acall(self, nodes, **kwargs):
35 | summarizer = TreeSummarize(
36 | verbose=True,
37 | llm=OpenAI(
38 | model=self.llm,
39 | temperature=0,
40 | )
41 | )
42 |
43 | SUMMARY_PROMPT = "Give me a brief summary under 50 words of the given LlamaIndex documentation page. There are many pages, this is just one of them. This 50-word summary must cover everything discussed in this particular documentation page but briefly so that someone reading this brief summary will get a complete picture of what they'll learn if they read the entire page."
44 |
45 | # loop = asyncio.get_event_loop()
46 | # loop.run_until_complete(
47 | # self.process_nodes(nodes, summarizer, SUMMARY_PROMPT)
48 | # )
49 | await self.process_nodes(nodes, summarizer, SUMMARY_PROMPT)
50 | return nodes
--------------------------------------------------------------------------------
/backend/app/utils/transformations/upserter.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | from llama_index.core.schema import TransformComponent
4 | from llama_index.core.bridge.pydantic import Field
5 | from llama_index.core.storage.docstore.types import BaseDocumentStore
6 |
7 | class Upserter(TransformComponent):
8 | """Extract the relative path or a documentation page."""
9 |
10 | docstore: BaseDocumentStore = Field(
11 | description='Document store to check for duplicates'
12 | )
13 |
14 | persist_dir: str = Field(
15 | description='persist path for docstore'
16 | )
17 |
18 | def __call__(self, nodes, **kwargs):
19 | assert self.docstore is not None
20 |
21 | self.docstore.add_documents(nodes)
22 | self.docstore.persist(
23 | persist_path=os.path.join(self.persist_dir, "docstore.json")
24 | )
25 | return nodes
--------------------------------------------------------------------------------
/backend/app/utils/transformations/url_extractor.py:
--------------------------------------------------------------------------------
1 | from os.path import relpath, splitext
2 |
3 | from llama_index.core.schema import TransformComponent
4 | from llama_index.core.bridge.pydantic import Field
5 |
6 | class URLExtractor(TransformComponent):
7 | """Upsert the new docs to the docstore."""
8 |
9 | data_path: str = Field(
10 | default='./data',
11 | description='Relative Data directory'
12 | )
13 |
14 | def __call__(self, nodes, **kwargs):
15 | for node in nodes:
16 | node.metadata["file_path"], _ = splitext(relpath(
17 | node.metadata['file_path'], self.data_path+"/docs"
18 | ))
19 | # print(node.metadata)
20 | return nodes
--------------------------------------------------------------------------------
/backend/app/utils/vectorstores/dummy.py:
--------------------------------------------------------------------------------
1 | from llama_index.core.vector_stores.types import BasePydanticVectorStore
2 |
3 | class DummyVectorStore(BasePydanticVectorStore):
4 | stores_text = False
5 | def client(self):
6 | ...
7 |
8 | def add(self, _):
9 | ...
10 |
11 | def delete(self, _, **__):
12 | ...
13 |
14 | def query(self, _, **__):
15 | ...
--------------------------------------------------------------------------------
/backend/data/docs/.gitignore:
--------------------------------------------------------------------------------
1 | api/
2 |
--------------------------------------------------------------------------------
/backend/data/docs/_static/concepts/indexing.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rsrohan99/llamaindex-docs-agent/1b2a6f5efee1bd5a4b9cd7771eac5f76a57155b1/backend/data/docs/_static/concepts/indexing.jpg
--------------------------------------------------------------------------------
/backend/data/docs/_static/concepts/querying.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rsrohan99/llamaindex-docs-agent/1b2a6f5efee1bd5a4b9cd7771eac5f76a57155b1/backend/data/docs/_static/concepts/querying.jpg
--------------------------------------------------------------------------------
/backend/data/docs/_static/concepts/rag.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rsrohan99/llamaindex-docs-agent/1b2a6f5efee1bd5a4b9cd7771eac5f76a57155b1/backend/data/docs/_static/concepts/rag.jpg
--------------------------------------------------------------------------------
/backend/data/docs/getting_started/_category_.yml:
--------------------------------------------------------------------------------
1 | label: Getting Started
2 | position: 1
3 |
--------------------------------------------------------------------------------
/backend/data/docs/getting_started/concepts.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 3
3 | ---
4 |
5 | # Concepts
6 |
7 | LlamaIndex.TS helps you build LLM-powered applications (e.g. Q&A, chatbot) over custom data.
8 |
9 | In this high-level concepts guide, you will learn:
10 |
11 | - how an LLM can answer questions using your own data.
12 | - key concepts and modules in LlamaIndex.TS for composing your own query pipeline.
13 |
14 | ## Answering Questions Across Your Data
15 |
16 | LlamaIndex uses a two stage method when using an LLM with your data:
17 |
18 | 1. **indexing stage**: preparing a knowledge base, and
19 | 2. **querying stage**: retrieving relevant context from the knowledge to assist the LLM in responding to a question
20 |
21 | 
22 |
23 | This process is also known as Retrieval Augmented Generation (RAG).
24 |
25 | LlamaIndex.TS provides the essential toolkit for making both steps super easy.
26 |
27 | Let's explore each stage in detail.
28 |
29 | ### Indexing Stage
30 |
31 | LlamaIndex.TS help you prepare the knowledge base with a suite of data connectors and indexes.
32 |
33 | 
34 |
35 | [**Data Loaders**](../modules/data_loader.md):
36 | A data connector (i.e. `Reader`) ingest data from different data sources and data formats into a simple `Document` representation (text and simple metadata).
37 |
38 | [**Documents / Nodes**](../modules/documents_and_nodes/index.md): A `Document` is a generic container around any data source - for instance, a PDF, an API output, or retrieved data from a database. A `Node` is the atomic unit of data in LlamaIndex and represents a "chunk" of a source `Document`. It's a rich representation that includes metadata and relationships (to other nodes) to enable accurate and expressive retrieval operations.
39 |
40 | [**Data Indexes**](../modules/data_index.md):
41 | Once you've ingested your data, LlamaIndex helps you index data into a format that's easy to retrieve.
42 |
43 | Under the hood, LlamaIndex parses the raw documents into intermediate representations, calculates vector embeddings, and stores your data in-memory or to disk.
44 |
45 | ### Querying Stage
46 |
47 | In the querying stage, the query pipeline retrieves the most relevant context given a user query,
48 | and pass that to the LLM (along with the query) to synthesize a response.
49 |
50 | This gives the LLM up-to-date knowledge that is not in its original training data,
51 | (also reducing hallucination).
52 |
53 | The key challenge in the querying stage is retrieval, orchestration, and reasoning over (potentially many) knowledge bases.
54 |
55 | LlamaIndex provides composable modules that help you build and integrate RAG pipelines for Q&A (query engine), chatbot (chat engine), or as part of an agent.
56 |
57 | These building blocks can be customized to reflect ranking preferences, as well as composed to reason over multiple knowledge bases in a structured way.
58 |
59 | 
60 |
61 | #### Building Blocks
62 |
63 | [**Retrievers**](../modules/retriever.md):
64 | A retriever defines how to efficiently retrieve relevant context from a knowledge base (i.e. index) when given a query.
65 | The specific retrieval logic differs for difference indices, the most popular being dense retrieval against a vector index.
66 |
67 | [**Response Synthesizers**](../modules/response_synthesizer.md):
68 | A response synthesizer generates a response from an LLM, using a user query and a given set of retrieved text chunks.
69 |
70 | #### Pipelines
71 |
72 | [**Query Engines**](../modules/query_engines):
73 | A query engine is an end-to-end pipeline that allow you to ask question over your data.
74 | It takes in a natural language query, and returns a response, along with reference context retrieved and passed to the LLM.
75 |
76 | [**Chat Engines**](../modules/chat_engine.md):
77 | A chat engine is an end-to-end pipeline for having a conversation with your data
78 | (multiple back-and-forth instead of a single question & answer).
79 |
--------------------------------------------------------------------------------
/backend/data/docs/getting_started/environments.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 2
3 | ---
4 |
5 | # Environments
6 |
7 | LlamaIndex currently officially supports NodeJS 18 and NodeJS 20.
8 |
9 | ## NextJS App Router
10 |
11 | If you're using NextJS App Router route handlers/serverless functions, you'll need to use the NodeJS mode:
12 |
13 | ```js
14 | export const runtime = "nodejs"; // default
15 | ```
16 |
--------------------------------------------------------------------------------
/backend/data/docs/getting_started/installation.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 0
3 | ---
4 |
5 | # Installation and Setup
6 |
7 | Make sure you have NodeJS v18 or higher.
8 |
9 | ## Using create-llama
10 |
11 | The easiest way to get started with LlamaIndex is by using `create-llama`. This CLI tool enables you to quickly start building a new LlamaIndex application, with everything set up for you.
12 |
13 | Just run
14 |
15 |
16 |
17 |
18 | ```bash
19 | npx create-llama@latest
20 | ```
21 |
22 |
23 |
24 |
25 | ```bash
26 | yarn create llama
27 | ```
28 |
29 |
30 |
31 |
32 | ```bash
33 | pnpm create llama@latest
34 | ```
35 |
36 |
37 |
38 |
39 | to get started. Once your app is generated, run
40 |
41 | ```bash npm2yarn
42 | npm run dev
43 | ```
44 |
45 | to start the development server. You can then visit [http://localhost:3000](http://localhost:3000) to see your app
46 |
47 | ## Installation from NPM
48 |
49 | ```bash npm2yarn
50 | npm install llamaindex
51 | ```
52 |
53 | ### Environment variables
54 |
55 | Our examples use OpenAI by default. You'll need to set up your Open AI key like so:
56 |
57 | ```bash
58 | export OPENAI_API_KEY="sk-......" # Replace with your key from https://platform.openai.com/account/api-keys
59 | ```
60 |
61 | If you want to have it automatically loaded every time, add it to your `.zshrc/.bashrc`.
62 |
63 | WARNING: do not check in your OpenAI key into version control.
64 |
--------------------------------------------------------------------------------
/backend/data/docs/getting_started/starter.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 1
3 | ---
4 |
5 | import CodeBlock from "@theme/CodeBlock";
6 | import CodeSource from "!raw-loader!../../../../examples/vectorIndex";
7 | import TSConfigSource from "!!raw-loader!../../../../examples/tsconfig.json";
8 |
9 | # Starter Tutorial
10 |
11 | Make sure you have installed LlamaIndex.TS and have an OpenAI key. If you haven't, check out the [installation](installation) guide.
12 |
13 | ## From scratch(node.js + TypeScript):
14 |
15 | In a new folder:
16 |
17 | ```bash npm2yarn
18 | npm init
19 | npm install -D typescript @types/node
20 | ```
21 |
22 | Create the file `example.ts`. This code will load some example data, create a document, index it (which creates embeddings using OpenAI), and then creates query engine to answer questions about the data.
23 |
24 | {CodeSource}
25 |
26 | Create a `tsconfig.json` file in the same folder:
27 |
28 | {TSConfigSource}
29 |
30 | Now you can run the code with
31 |
32 | ```bash
33 | npx tsx example.ts
34 | ```
35 |
36 | Also, you can clone our examples and try them out:
37 |
38 | ```bash npm2yarn
39 | npx degit run-llama/LlamaIndexTS/examples my-new-project
40 | cd my-new-project
41 | npm install
42 | npx tsx ./vectorIndex.ts
43 | ```
44 |
45 | ## From scratch (Next.js + TypeScript):
46 |
47 | You just need one command to create a new Next.js project:
48 |
49 | ```bash npm2yarn
50 | npx create-llama@latest
51 | ```
52 |
--------------------------------------------------------------------------------
/backend/data/docs/introduction.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 0
3 | slug: /
4 | ---
5 |
6 | # What is LlamaIndex.TS?
7 |
8 | LlamaIndex.TS is a data framework for LLM applications to ingest, structure, and access private or domain-specific data. While a python package is also available (see [here](https://docs.llamaindex.ai/en/stable/)), LlamaIndex.TS offers core features in a simple package, optimized for usage with TypeScript.
9 |
10 | ## 🚀 Why LlamaIndex.TS?
11 |
12 | At their core, LLMs offer a natural language interface between humans and inferred data. Widely available models come pre-trained on huge amounts of publicly available data, from Wikipedia and mailing lists to textbooks and source code.
13 |
14 | Applications built on top of LLMs often require augmenting these models with private or domain-specific data. Unfortunately, that data can be distributed across siloed applications and data stores. It's behind APIs, in SQL databases, or trapped in PDFs and slide decks.
15 |
16 | That's where **LlamaIndex.TS** comes in.
17 |
18 | ## 🦙 How can LlamaIndex.TS help?
19 |
20 | LlamaIndex.TS provides the following tools:
21 |
22 | - **Data loading** ingest your existing `.txt`, `.pdf`, `.csv`, `.md` and `.docx` data directly
23 | - **Data indexes** structure your data in intermediate representations that are easy and performant for LLMs to consume.
24 | - **Engines** provide natural language access to your data. For example:
25 | - Query engines are powerful retrieval interfaces for knowledge-augmented output.
26 | - Chat engines are conversational interfaces for multi-message, "back and forth" interactions with your data.
27 |
28 | ## 👨👩👧👦 Who is LlamaIndex for?
29 |
30 | LlamaIndex.TS provides a core set of tools, essential for anyone building LLM apps with JavaScript and TypeScript.
31 |
32 | Our high-level API allows beginner users to use LlamaIndex.TS to ingest and query their data.
33 |
34 | For more complex applications, our lower-level APIs allow advanced users to customize and extend any module—data connectors, indices, retrievers, and query engines, to fit their needs.
35 |
36 | ## Getting Started
37 |
38 | `npm install llamaindex`
39 |
40 | Our documentation includes [Installation Instructions](./getting_started/installation.mdx) and a [Starter Tutorial](./getting_started/starter.mdx) to build your first application.
41 |
42 | Once you're up and running, [High-Level Concepts](./getting_started/concepts.md) has an overview of LlamaIndex's modular architecture. For more hands-on practical examples, look through our Examples section on the sidebar.
43 |
44 | ## 🗺️ Ecosystem
45 |
46 | To download or contribute, find LlamaIndex on:
47 |
48 | - Github: https://github.com/run-llama/LlamaIndexTS
49 | - NPM: https://www.npmjs.com/package/llamaindex
50 |
51 | ## Community
52 |
53 | Need help? Have a feature suggestion? Join the LlamaIndex community:
54 |
55 | - Twitter: https://twitter.com/llama_index
56 | - Discord https://discord.gg/dGcwcsnxhU
57 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/_category_.yml:
--------------------------------------------------------------------------------
1 | label: "Modules"
2 | collapsed: false
3 | position: 5
4 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/agent/_category_.yml:
--------------------------------------------------------------------------------
1 | label: "Agents"
2 | position: 3
3 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/agent/index.md:
--------------------------------------------------------------------------------
1 | # Agents
2 |
3 | An “agent” is an automated reasoning and decision engine. It takes in a user input/query and can make internal decisions for executing that query in order to return the correct result. The key agent components can include, but are not limited to:
4 |
5 | - Breaking down a complex question into smaller ones
6 | - Choosing an external Tool to use + coming up with parameters for calling the Tool
7 | - Planning out a set of tasks
8 | - Storing previously completed tasks in a memory module
9 |
10 | ## Getting Started
11 |
12 | LlamaIndex.TS comes with a few built-in agents, but you can also create your own. The built-in agents include:
13 |
14 | - [OpenAI Agent](./openai.mdx)
15 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/agent/multi_document_agent.mdx:
--------------------------------------------------------------------------------
1 | # Multi-Document Agent
2 |
3 | In this guide, you learn towards setting up an agent that can effectively answer different types of questions over a larger set of documents.
4 |
5 | These questions include the following
6 |
7 | - QA over a specific doc
8 | - QA comparing different docs
9 | - Summaries over a specific doc
10 | - Comparing summaries between different docs
11 |
12 | We do this with the following architecture:
13 |
14 | - setup a “document agent” over each Document: each doc agent can do QA/summarization within its doc
15 | - setup a top-level agent over this set of document agents. Do tool retrieval and then do CoT over the set of tools to answer a question.
16 |
17 | ## Setup and Download Data
18 |
19 | We first start by installing the necessary libraries and downloading the data.
20 |
21 | ```bash
22 | pnpm i llamaindex
23 | ```
24 |
25 | ```ts
26 | import {
27 | Document,
28 | ObjectIndex,
29 | OpenAI,
30 | OpenAIAgent,
31 | QueryEngineTool,
32 | SimpleNodeParser,
33 | SimpleToolNodeMapping,
34 | SummaryIndex,
35 | VectorStoreIndex,
36 | serviceContextFromDefaults,
37 | storageContextFromDefaults,
38 | } from "llamaindex";
39 | ```
40 |
41 | And then for the data we will run through a list of countries and download the wikipedia page for each country.
42 |
43 | ```ts
44 | import fs from "fs";
45 | import path from "path";
46 |
47 | const dataPath = path.join(__dirname, "tmp_data");
48 |
49 | const extractWikipediaTitle = async (title: string) => {
50 | const fileExists = fs.existsSync(path.join(dataPath, `${title}.txt`));
51 |
52 | if (fileExists) {
53 | console.log(`File already exists for the title: ${title}`);
54 | return;
55 | }
56 |
57 | const queryParams = new URLSearchParams({
58 | action: "query",
59 | format: "json",
60 | titles: title,
61 | prop: "extracts",
62 | explaintext: "true",
63 | });
64 |
65 | const url = `https://en.wikipedia.org/w/api.php?${queryParams}`;
66 |
67 | const response = await fetch(url);
68 | const data: any = await response.json();
69 |
70 | const pages = data.query.pages;
71 | const page = pages[Object.keys(pages)[0]];
72 | const wikiText = page.extract;
73 |
74 | await new Promise((resolve) => {
75 | fs.writeFile(path.join(dataPath, `${title}.txt`), wikiText, (err: any) => {
76 | if (err) {
77 | console.error(err);
78 | resolve(title);
79 | return;
80 | }
81 | console.log(`${title} stored in file!`);
82 |
83 | resolve(title);
84 | });
85 | });
86 | };
87 | ```
88 |
89 | ```ts
90 | export const extractWikipedia = async (titles: string[]) => {
91 | if (!fs.existsSync(dataPath)) {
92 | fs.mkdirSync(dataPath);
93 | }
94 |
95 | for await (const title of titles) {
96 | await extractWikipediaTitle(title);
97 | }
98 |
99 | console.log("Extration finished!");
100 | ```
101 |
102 | These files will be saved in the `tmp_data` folder.
103 |
104 | Now we can call the function to download the data for each country.
105 |
106 | ```ts
107 | await extractWikipedia([
108 | "Brazil",
109 | "United States",
110 | "Canada",
111 | "Mexico",
112 | "Argentina",
113 | "Chile",
114 | "Colombia",
115 | "Peru",
116 | "Venezuela",
117 | "Ecuador",
118 | "Bolivia",
119 | "Paraguay",
120 | "Uruguay",
121 | "Guyana",
122 | "Suriname",
123 | "French Guiana",
124 | "Falkland Islands",
125 | ]);
126 | ```
127 |
128 | ## Load the data
129 |
130 | Now that we have the data, we can load it into the LlamaIndex and store as a document.
131 |
132 | ```ts
133 | import { Document } from "llamaindex";
134 |
135 | const countryDocs: Record = {};
136 |
137 | for (const title of wikiTitles) {
138 | const path = `./agent/helpers/tmp_data/${title}.txt`;
139 | const text = await fs.readFile(path, "utf-8");
140 | const document = new Document({ text: text, id_: path });
141 | countryDocs[title] = document;
142 | }
143 | ```
144 |
145 | ## Setup LLM and StorageContext
146 |
147 | We will be using gpt-4 for this example and we will use the `StorageContext` to store the documents in-memory.
148 |
149 | ```ts
150 | const llm = new OpenAI({
151 | model: "gpt-4",
152 | });
153 |
154 | const ctx = serviceContextFromDefaults({ llm });
155 |
156 | const storageContext = await storageContextFromDefaults({
157 | persistDir: "./storage",
158 | });
159 | ```
160 |
161 | ## Building Multi-Document Agents
162 |
163 | In this section we show you how to construct the multi-document agent. We first build a document agent for each document, and then define the top-level parent agent with an object index.
164 |
165 | ```ts
166 | const documentAgents: Record = {};
167 | const queryEngines: Record = {};
168 | ```
169 |
170 | Now we iterate over each country and create a document agent for each one.
171 |
172 | ### Build Agent for each Document
173 |
174 | In this section we define “document agents” for each document.
175 |
176 | We define both a vector index (for semantic search) and summary index (for summarization) for each document. The two query engines are then converted into tools that are passed to an OpenAI function calling agent.
177 |
178 | This document agent can dynamically choose to perform semantic search or summarization within a given document.
179 |
180 | We create a separate document agent for each coutnry.
181 |
182 | ```ts
183 | for (const title of wikiTitles) {
184 | // parse the document into nodes
185 | const nodes = new SimpleNodeParser({
186 | chunkSize: 200,
187 | chunkOverlap: 20,
188 | }).getNodesFromDocuments([countryDocs[title]]);
189 |
190 | // create the vector index for specific search
191 | const vectorIndex = await VectorStoreIndex.init({
192 | serviceContext: serviceContext,
193 | storageContext: storageContext,
194 | nodes,
195 | });
196 |
197 | // create the summary index for broader search
198 | const summaryIndex = await SummaryIndex.init({
199 | serviceContext: serviceContext,
200 | nodes,
201 | });
202 |
203 | const vectorQueryEngine = summaryIndex.asQueryEngine();
204 | const summaryQueryEngine = summaryIndex.asQueryEngine();
205 |
206 | // create the query engines for each task
207 | const queryEngineTools = [
208 | new QueryEngineTool({
209 | queryEngine: vectorQueryEngine,
210 | metadata: {
211 | name: "vector_tool",
212 | description: `Useful for questions related to specific aspects of ${title} (e.g. the history, arts and culture, sports, demographics, or more).`,
213 | },
214 | }),
215 | new QueryEngineTool({
216 | queryEngine: summaryQueryEngine,
217 | metadata: {
218 | name: "summary_tool",
219 | description: `Useful for any requests that require a holistic summary of EVERYTHING about ${title}. For questions about more specific sections, please use the vector_tool.`,
220 | },
221 | }),
222 | ];
223 |
224 | // create the document agent
225 | const agent = new OpenAIAgent({
226 | tools: queryEngineTools,
227 | llm,
228 | verbose: true,
229 | });
230 |
231 | documentAgents[title] = agent;
232 | queryEngines[title] = vectorIndex.asQueryEngine();
233 | }
234 | ```
235 |
236 | ## Build Top-Level Agent
237 |
238 | Now we define the top-level agent that can answer questions over the set of document agents.
239 |
240 | This agent takes in all document agents as tools. This specific agent RetrieverOpenAIAgent performs tool retrieval before tool use (unlike a default agent that tries to put all tools in the prompt).
241 |
242 | Here we use a top-k retriever, but we encourage you to customize the tool retriever method!
243 |
244 | Firstly, we create a tool for each document agent
245 |
246 | ```ts
247 | const allTools: QueryEngineTool[] = [];
248 | ```
249 |
250 | ```ts
251 | for (const title of wikiTitles) {
252 | const wikiSummary = `
253 | This content contains Wikipedia articles about ${title}.
254 | Use this tool if you want to answer any questions about ${title}
255 | `;
256 |
257 | const docTool = new QueryEngineTool({
258 | queryEngine: documentAgents[title],
259 | metadata: {
260 | name: `tool_${title}`,
261 | description: wikiSummary,
262 | },
263 | });
264 |
265 | allTools.push(docTool);
266 | }
267 | ```
268 |
269 | Our top level agent will use this document agents as tools and use toolRetriever to retrieve the best tool to answer a question.
270 |
271 | ```ts
272 | // map the tools to nodes
273 | const toolMapping = SimpleToolNodeMapping.fromObjects(allTools);
274 |
275 | // create the object index
276 | const objectIndex = await ObjectIndex.fromObjects(
277 | allTools,
278 | toolMapping,
279 | VectorStoreIndex,
280 | {
281 | serviceContext,
282 | storageContext,
283 | },
284 | );
285 |
286 | // create the top agent
287 | const topAgent = new OpenAIAgent({
288 | toolRetriever: await objectIndex.asRetriever({}),
289 | llm,
290 | verbose: true,
291 | prefixMessages: [
292 | {
293 | content:
294 | "You are an agent designed to answer queries about a set of given countries. Please always use the tools provided to answer a question. Do not rely on prior knowledge.",
295 | role: "system",
296 | },
297 | ],
298 | });
299 | ```
300 |
301 | ## Use the Agent
302 |
303 | Now we can use the agent to answer questions.
304 |
305 | ```ts
306 | const response = await topAgent.chat({
307 | message: "Tell me the differences between Brazil and Canada economics?",
308 | });
309 |
310 | // print output
311 | console.log(response);
312 | ```
313 |
314 | You can find the full code for this example [here](https://github.com/run-llama/LlamaIndexTS/tree/main/examples/agent/multi-document-agent.ts)
315 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/agent/openai.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 0
3 | ---
4 |
5 | # OpenAI Agent
6 |
7 | OpenAI API that supports function calling, it’s never been easier to build your own agent!
8 |
9 | In this notebook tutorial, we showcase how to write your own OpenAI agent
10 |
11 | ## Setup
12 |
13 | First, you need to install the `llamaindex` package. You can do this by running the following command in your terminal:
14 |
15 | ```bash
16 | pnpm i llamaindex
17 | ```
18 |
19 | Then we can define a function to sum two numbers and another function to divide two numbers.
20 |
21 | ```ts
22 | function sumNumbers({ a, b }: { a: number; b: number }): number {
23 | return a + b;
24 | }
25 |
26 | // Define a function to divide two numbers
27 | function divideNumbers({ a, b }: { a: number; b: number }): number {
28 | return a / b;
29 | }
30 | ```
31 |
32 | ## Create a function tool
33 |
34 | Now we can create a function tool from the sum function and another function tool from the divide function.
35 |
36 | For the parameters of the sum function, we can define a JSON schema.
37 |
38 | ### JSON Schema
39 |
40 | ```ts
41 | const sumJSON = {
42 | type: "object",
43 | properties: {
44 | a: {
45 | type: "number",
46 | description: "The first number",
47 | },
48 | b: {
49 | type: "number",
50 | description: "The second number",
51 | },
52 | },
53 | required: ["a", "b"],
54 | };
55 |
56 | const divideJSON = {
57 | type: "object",
58 | properties: {
59 | a: {
60 | type: "number",
61 | description: "The dividend a to divide",
62 | },
63 | b: {
64 | type: "number",
65 | description: "The divisor b to divide by",
66 | },
67 | },
68 | required: ["a", "b"],
69 | };
70 |
71 | const sumFunctionTool = new FunctionTool(sumNumbers, {
72 | name: "sumNumbers",
73 | description: "Use this function to sum two numbers",
74 | parameters: sumJSON,
75 | });
76 |
77 | const divideFunctionTool = new FunctionTool(divideNumbers, {
78 | name: "divideNumbers",
79 | description: "Use this function to divide two numbers",
80 | parameters: divideJSON,
81 | });
82 | ```
83 |
84 | ## Create an OpenAIAgent
85 |
86 | Now we can create an OpenAIAgent with the function tools.
87 |
88 | ```ts
89 | const agent = new OpenAIAgent({
90 | tools: [sumFunctionTool, divideFunctionTool],
91 | verbose: true,
92 | });
93 | ```
94 |
95 | ## Chat with the agent
96 |
97 | Now we can chat with the agent.
98 |
99 | ```ts
100 | const response = await agent.chat({
101 | message: "How much is 5 + 5? then divide by 2",
102 | });
103 |
104 | console.log(String(response));
105 | ```
106 |
107 | ## Full code
108 |
109 | ```ts
110 | import { FunctionTool, OpenAIAgent } from "llamaindex";
111 |
112 | // Define a function to sum two numbers
113 | function sumNumbers({ a, b }: { a: number; b: number }): number {
114 | return a + b;
115 | }
116 |
117 | // Define a function to divide two numbers
118 | function divideNumbers({ a, b }: { a: number; b: number }): number {
119 | return a / b;
120 | }
121 |
122 | // Define the parameters of the sum function as a JSON schema
123 | const sumJSON = {
124 | type: "object",
125 | properties: {
126 | a: {
127 | type: "number",
128 | description: "The first number",
129 | },
130 | b: {
131 | type: "number",
132 | description: "The second number",
133 | },
134 | },
135 | required: ["a", "b"],
136 | };
137 |
138 | // Define the parameters of the divide function as a JSON schema
139 | const divideJSON = {
140 | type: "object",
141 | properties: {
142 | a: {
143 | type: "number",
144 | description: "The argument a to divide",
145 | },
146 | b: {
147 | type: "number",
148 | description: "The argument b to divide",
149 | },
150 | },
151 | required: ["a", "b"],
152 | };
153 |
154 | async function main() {
155 | // Create a function tool from the sum function
156 | const sumFunctionTool = new FunctionTool(sumNumbers, {
157 | name: "sumNumbers",
158 | description: "Use this function to sum two numbers",
159 | parameters: sumJSON,
160 | });
161 |
162 | // Create a function tool from the divide function
163 | const divideFunctionTool = new FunctionTool(divideNumbers, {
164 | name: "divideNumbers",
165 | description: "Use this function to divide two numbers",
166 | parameters: divideJSON,
167 | });
168 |
169 | // Create an OpenAIAgent with the function tools
170 | const agent = new OpenAIAgent({
171 | tools: [sumFunctionTool, divideFunctionTool],
172 | verbose: true,
173 | });
174 |
175 | // Chat with the agent
176 | const response = await agent.chat({
177 | message: "How much is 5 + 5? then divide by 2",
178 | });
179 |
180 | // Print the response
181 | console.log(String(response));
182 | }
183 |
184 | main().then(() => {
185 | console.log("Done");
186 | });
187 | ```
188 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/agent/query_engine_tool.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 1
3 | ---
4 |
5 | # OpenAI Agent + QueryEngineTool
6 |
7 | QueryEngineTool is a tool that allows you to query a vector index. In this example, we will create a vector index from a set of documents and then create a QueryEngineTool from the vector index. We will then create an OpenAIAgent with the QueryEngineTool and chat with the agent.
8 |
9 | ## Setup
10 |
11 | First, you need to install the `llamaindex` package. You can do this by running the following command in your terminal:
12 |
13 | ```bash
14 | pnpm i llamaindex
15 | ```
16 |
17 | Then you can import the necessary classes and functions.
18 |
19 | ```ts
20 | import {
21 | OpenAIAgent,
22 | SimpleDirectoryReader,
23 | VectorStoreIndex,
24 | QueryEngineTool,
25 | } from "llamaindex";
26 | ```
27 |
28 | ## Create a vector index
29 |
30 | Now we can create a vector index from a set of documents.
31 |
32 | ```ts
33 | // Load the documents
34 | const documents = await new SimpleDirectoryReader().loadData({
35 | directoryPath: "node_modules/llamaindex/examples/",
36 | });
37 |
38 | // Create a vector index from the documents
39 | const vectorIndex = await VectorStoreIndex.fromDocuments(documents);
40 | ```
41 |
42 | ## Create a QueryEngineTool
43 |
44 | Now we can create a QueryEngineTool from the vector index.
45 |
46 | ```ts
47 | // Create a query engine from the vector index
48 | const abramovQueryEngine = vectorIndex.asQueryEngine();
49 |
50 | // Create a QueryEngineTool with the query engine
51 | const queryEngineTool = new QueryEngineTool({
52 | queryEngine: abramovQueryEngine,
53 | metadata: {
54 | name: "abramov_query_engine",
55 | description: "A query engine for the Abramov documents",
56 | },
57 | });
58 | ```
59 |
60 | ## Create an OpenAIAgent
61 |
62 | ```ts
63 | // Create an OpenAIAgent with the query engine tool tools
64 |
65 | const agent = new OpenAIAgent({
66 | tools: [queryEngineTool],
67 | verbose: true,
68 | });
69 | ```
70 |
71 | ## Chat with the agent
72 |
73 | Now we can chat with the agent.
74 |
75 | ```ts
76 | const response = await agent.chat({
77 | message: "What was his salary?",
78 | });
79 |
80 | console.log(String(response));
81 | ```
82 |
83 | ## Full code
84 |
85 | ```ts
86 | import {
87 | OpenAIAgent,
88 | SimpleDirectoryReader,
89 | VectorStoreIndex,
90 | QueryEngineTool,
91 | } from "llamaindex";
92 |
93 | async function main() {
94 | // Load the documents
95 | const documents = await new SimpleDirectoryReader().loadData({
96 | directoryPath: "node_modules/llamaindex/examples/",
97 | });
98 |
99 | // Create a vector index from the documents
100 | const vectorIndex = await VectorStoreIndex.fromDocuments(documents);
101 |
102 | // Create a query engine from the vector index
103 | const abramovQueryEngine = vectorIndex.asQueryEngine();
104 |
105 | // Create a QueryEngineTool with the query engine
106 | const queryEngineTool = new QueryEngineTool({
107 | queryEngine: abramovQueryEngine,
108 | metadata: {
109 | name: "abramov_query_engine",
110 | description: "A query engine for the Abramov documents",
111 | },
112 | });
113 |
114 | // Create an OpenAIAgent with the function tools
115 | const agent = new OpenAIAgent({
116 | tools: [queryEngineTool],
117 | verbose: true,
118 | });
119 |
120 | // Chat with the agent
121 | const response = await agent.chat({
122 | message: "What was his salary?",
123 | });
124 |
125 | // Print the response
126 | console.log(String(response));
127 | }
128 |
129 | main().then(() => {
130 | console.log("Done");
131 | });
132 | ```
133 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/agent/react_agent.mdx:
--------------------------------------------------------------------------------
1 | # ReAct Agent
2 |
3 | The ReAct agent is an AI agent that can reason over the next action, construct an action command, execute the action, and repeat these steps in an iterative loop until the task is complete.
4 |
5 | In this notebook tutorial, we showcase how to write your ReAct agent using the `llamaindex` package.
6 |
7 | ## Setup
8 |
9 | First, you need to install the `llamaindex` package. You can do this by running the following command in your terminal:
10 |
11 | ```bash
12 | pnpm i llamaindex
13 | ```
14 |
15 | And then you can import the `OpenAIAgent` and `FunctionTool` from the `llamaindex` package.
16 |
17 | ```ts
18 | import { FunctionTool, OpenAIAgent } from "llamaindex";
19 | ```
20 |
21 | Then we can define a function to sum two numbers and another function to divide two numbers.
22 |
23 | ```ts
24 | function sumNumbers({ a, b }: { a: number; b: number }): number {
25 | return a + b;
26 | }
27 |
28 | // Define a function to divide two numbers
29 | function divideNumbers({ a, b }: { a: number; b: number }): number {
30 | return a / b;
31 | }
32 | ```
33 |
34 | ## Create a function tool
35 |
36 | Now we can create a function tool from the sum function and another function tool from the divide function.
37 |
38 | For the parameters of the sum function, we can define a JSON schema.
39 |
40 | ### JSON Schema
41 |
42 | ```ts
43 | const sumJSON = {
44 | type: "object",
45 | properties: {
46 | a: {
47 | type: "number",
48 | description: "The first number",
49 | },
50 | b: {
51 | type: "number",
52 | description: "The second number",
53 | },
54 | },
55 | required: ["a", "b"],
56 | };
57 |
58 | const divideJSON = {
59 | type: "object",
60 | properties: {
61 | a: {
62 | type: "number",
63 | description: "The dividend a to divide",
64 | },
65 | b: {
66 | type: "number",
67 | description: "The divisor b to divide by",
68 | },
69 | },
70 | required: ["a", "b"],
71 | };
72 |
73 | const sumFunctionTool = new FunctionTool(sumNumbers, {
74 | name: "sumNumbers",
75 | description: "Use this function to sum two numbers",
76 | parameters: sumJSON,
77 | });
78 |
79 | const divideFunctionTool = new FunctionTool(divideNumbers, {
80 | name: "divideNumbers",
81 | description: "Use this function to divide two numbers",
82 | parameters: divideJSON,
83 | });
84 | ```
85 |
86 | ## Create an ReAct
87 |
88 | Now we can create an OpenAIAgent with the function tools.
89 |
90 | ```ts
91 | const agent = new ReActAgent({
92 | tools: [sumFunctionTool, divideFunctionTool],
93 | verbose: true,
94 | });
95 | ```
96 |
97 | ## Chat with the agent
98 |
99 | Now we can chat with the agent.
100 |
101 | ```ts
102 | const response = await agent.chat({
103 | message: "How much is 5 + 5? then divide by 2",
104 | });
105 |
106 | console.log(String(response));
107 | ```
108 |
109 | The output will be:
110 |
111 | ```bash
112 | Thought: I need to use a tool to help me answer the question.
113 | Action: sumNumbers
114 | Action Input: {"a":5,"b":5}
115 |
116 | Observation: 10
117 | Thought: I can answer without using any more tools.
118 | Answer: The sum of 5 and 5 is 10, and when divided by 2, the result is 5.
119 |
120 | The sum of 5 and 5 is 10, and when divided by 2, the result is 5.
121 | ```
122 |
123 | ## Full code
124 |
125 | ```ts
126 | import { FunctionTool, ReActAgent } from "llamaindex";
127 |
128 | // Define a function to sum two numbers
129 | function sumNumbers({ a, b }: { a: number; b: number }): number {
130 | return a + b;
131 | }
132 |
133 | // Define a function to divide two numbers
134 | function divideNumbers({ a, b }: { a: number; b: number }): number {
135 | return a / b;
136 | }
137 |
138 | // Define the parameters of the sum function as a JSON schema
139 | const sumJSON = {
140 | type: "object",
141 | properties: {
142 | a: {
143 | type: "number",
144 | description: "The first number",
145 | },
146 | b: {
147 | type: "number",
148 | description: "The second number",
149 | },
150 | },
151 | required: ["a", "b"],
152 | };
153 |
154 | // Define the parameters of the divide function as a JSON schema
155 | const divideJSON = {
156 | type: "object",
157 | properties: {
158 | a: {
159 | type: "number",
160 | description: "The argument a to divide",
161 | },
162 | b: {
163 | type: "number",
164 | description: "The argument b to divide",
165 | },
166 | },
167 | required: ["a", "b"],
168 | };
169 |
170 | async function main() {
171 | // Create a function tool from the sum function
172 | const sumFunctionTool = new FunctionTool(sumNumbers, {
173 | name: "sumNumbers",
174 | description: "Use this function to sum two numbers",
175 | parameters: sumJSON,
176 | });
177 |
178 | // Create a function tool from the divide function
179 | const divideFunctionTool = new FunctionTool(divideNumbers, {
180 | name: "divideNumbers",
181 | description: "Use this function to divide two numbers",
182 | parameters: divideJSON,
183 | });
184 |
185 | // Create an OpenAIAgent with the function tools
186 | const agent = new OpenAIAgent({
187 | tools: [sumFunctionTool, divideFunctionTool],
188 | verbose: true,
189 | });
190 |
191 | // Chat with the agent
192 | const response = await agent.chat({
193 | message: "I want to sum 5 and 5 and then divide by 2",
194 | });
195 |
196 | // Print the response
197 | console.log(String(response));
198 | }
199 |
200 | main().then(() => {
201 | console.log("Done");
202 | });
203 | ```
204 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/chat_engine.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 4
3 | ---
4 |
5 | # ChatEngine
6 |
7 | The chat engine is a quick and simple way to chat with the data in your index.
8 |
9 | ```typescript
10 | const retriever = index.asRetriever();
11 | const chatEngine = new ContextChatEngine({ retriever });
12 |
13 | // start chatting
14 | const response = await chatEngine.chat({ message: query });
15 | ```
16 |
17 | The `chat` function also supports streaming, just add `stream: true` as an option:
18 |
19 | ```typescript
20 | const stream = await chatEngine.chat({ message: query, stream: true });
21 | for await (const chunk of stream) {
22 | process.stdout.write(chunk.response);
23 | }
24 | ```
25 |
26 | ## Api References
27 |
28 | - [ContextChatEngine](../api/classes/ContextChatEngine.md)
29 | - [CondenseQuestionChatEngine](../api/classes/ContextChatEngine.md)
30 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/data_index.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 4
3 | ---
4 |
5 | # Index
6 |
7 | An index is the basic container and organization for your data. LlamaIndex.TS supports two indexes:
8 |
9 | - `VectorStoreIndex` - will send the top-k `Node`s to the LLM when generating a response. The default top-k is 2.
10 | - `SummaryIndex` - will send every `Node` in the index to the LLM in order to generate a response
11 |
12 | ```typescript
13 | import { Document, VectorStoreIndex } from "llamaindex";
14 |
15 | const document = new Document({ text: "test" });
16 |
17 | const index = await VectorStoreIndex.fromDocuments([document]);
18 | ```
19 |
20 | ## API Reference
21 |
22 | - [SummaryIndex](../api/classes/SummaryIndex.md)
23 | - [VectorStoreIndex](../api/classes/VectorStoreIndex.md)
24 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/data_loader.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 4
3 | ---
4 |
5 | import CodeBlock from "@theme/CodeBlock";
6 | import CodeSource from "!raw-loader!../../../../examples/readers/src/simple-directory-reader";
7 | import CodeSource2 from "!raw-loader!../../../../examples/readers/src/custom-simple-directory-reader";
8 | import CodeSource3 from "!raw-loader!../../../../examples/readers/src/llamaparse";
9 |
10 | # Loader
11 |
12 | Before you can start indexing your documents, you need to load them into memory.
13 |
14 | ### SimpleDirectoryReader
15 |
16 | [](https://stackblitz.com/github/run-llama/LlamaIndexTS/tree/main/examples/readers?file=src/simple-directory-reader.ts&title=Simple%20Directory%20Reader)
17 |
18 | LlamaIndex.TS supports easy loading of files from folders using the `SimpleDirectoryReader` class.
19 |
20 | It is a simple reader that reads all files from a directory and its subdirectories.
21 |
22 | {CodeSource}
23 |
24 | Currently, it supports reading `.csv`, `.docx`, `.html`, `.md` and `.pdf` files,
25 | but support for other file types is planned.
26 |
27 | Also, you can provide a `defaultReader` as a fallback for files with unsupported extensions.
28 | Or pass new readers for `fileExtToReader` to support more file types.
29 |
30 |
31 | {CodeSource2}
32 |
33 |
34 | ### LlamaParse
35 |
36 | LlamaParse is an API created by LlamaIndex to efficiently parse files, e.g. it's great at converting PDF tables into markdown.
37 |
38 | To use it, first login and get an API key from https://cloud.llamaindex.ai. Make sure to store the key in the environment variable `LLAMA_CLOUD_API_KEY`.
39 |
40 | Then, you can use the `LlamaParseReader` class to read a local PDF file and convert it into a markdown document that can be used by LlamaIndex:
41 |
42 | {CodeSource3}
43 |
44 | Alternatively, you can set the [`resultType`](../api/classes/LlamaParseReader.md#resulttype) option to `text` to get the parsed document as a text string.
45 |
46 | ## API Reference
47 |
48 | - [SimpleDirectoryReader](../api/classes/SimpleDirectoryReader.md)
49 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/documents_and_nodes/_category_.yml:
--------------------------------------------------------------------------------
1 | label: "Document / Nodes"
2 | position: 0
3 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/documents_and_nodes/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 1
3 | ---
4 |
5 | # Documents and Nodes
6 |
7 | `Document`s and `Node`s are the basic building blocks of any index. While the API for these objects is similar, `Document` objects represent entire files, while `Node`s are smaller pieces of that original document, that are suitable for an LLM and Q&A.
8 |
9 | ```typescript
10 | import { Document } from "llamaindex";
11 |
12 | document = new Document({ text: "text", metadata: { key: "val" } });
13 | ```
14 |
15 | ## API Reference
16 |
17 | - [Document](../api/classes/Document.md)
18 | - [TextNode](../api/classes/TextNode.md)
19 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/documents_and_nodes/metadata_extraction.md:
--------------------------------------------------------------------------------
1 | # Metadata Extraction Usage Pattern
2 |
3 | You can use LLMs to automate metadata extraction with our `Metadata Extractor` modules.
4 |
5 | Our metadata extractor modules include the following "feature extractors":
6 |
7 | - `SummaryExtractor` - automatically extracts a summary over a set of Nodes
8 | - `QuestionsAnsweredExtractor` - extracts a set of questions that each Node can answer
9 | - `TitleExtractor` - extracts a title over the context of each Node by document and combine them
10 | - `KeywordExtractor` - extracts keywords over the context of each Node
11 |
12 | Then you can chain the `Metadata Extractors` with the `IngestionPipeline` to extract metadata from a set of documents.
13 |
14 | ```ts
15 | import {
16 | IngestionPipeline,
17 | TitleExtractor,
18 | QuestionsAnsweredExtractor,
19 | Document,
20 | OpenAI,
21 | } from "llamaindex";
22 |
23 | async function main() {
24 | const pipeline = new IngestionPipeline({
25 | transformations: [
26 | new TitleExtractor(),
27 | new QuestionsAnsweredExtractor({
28 | questions: 5,
29 | }),
30 | ],
31 | });
32 |
33 | const nodes = await pipeline.run({
34 | documents: [
35 | new Document({ text: "I am 10 years old. John is 20 years old." }),
36 | ],
37 | });
38 |
39 | for (const node of nodes) {
40 | console.log(node.metadata);
41 | }
42 | }
43 |
44 | main().then(() => console.log("done"));
45 | ```
46 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/embeddings/_category_.yml:
--------------------------------------------------------------------------------
1 | label: "Embeddings"
2 | position: 3
3 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/embeddings/available_embeddings/_category_.yml:
--------------------------------------------------------------------------------
1 | label: "Available Embeddings"
2 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/embeddings/available_embeddings/huggingface.md:
--------------------------------------------------------------------------------
1 | # HuggingFace
2 |
3 | To use HuggingFace embeddings, you need to import `HuggingFaceEmbedding` from `llamaindex`.
4 |
5 | ```ts
6 | import { HuggingFaceEmbedding, serviceContextFromDefaults } from "llamaindex";
7 |
8 | const huggingFaceEmbeds = new HuggingFaceEmbedding();
9 |
10 | const serviceContext = serviceContextFromDefaults({ embedModel: openaiEmbeds });
11 |
12 | const document = new Document({ text: essay, id_: "essay" });
13 |
14 | const index = await VectorStoreIndex.fromDocuments([document], {
15 | serviceContext,
16 | });
17 |
18 | const queryEngine = index.asQueryEngine();
19 |
20 | const query = "What is the meaning of life?";
21 |
22 | const results = await queryEngine.query({
23 | query,
24 | });
25 | ```
26 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/embeddings/available_embeddings/mistral.md:
--------------------------------------------------------------------------------
1 | # MistralAI
2 |
3 | To use MistralAI embeddings, you need to import `MistralAIEmbedding` from `llamaindex`.
4 |
5 | ```ts
6 | import { MistralAIEmbedding, serviceContextFromDefaults } from "llamaindex";
7 |
8 | const mistralEmbedModel = new MistralAIEmbedding({
9 | apiKey: "",
10 | });
11 |
12 | const serviceContext = serviceContextFromDefaults({
13 | embedModel: mistralEmbedModel,
14 | });
15 |
16 | const document = new Document({ text: essay, id_: "essay" });
17 |
18 | const index = await VectorStoreIndex.fromDocuments([document], {
19 | serviceContext,
20 | });
21 |
22 | const queryEngine = index.asQueryEngine();
23 |
24 | const query = "What is the meaning of life?";
25 |
26 | const results = await queryEngine.query({
27 | query,
28 | });
29 | ```
30 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/embeddings/available_embeddings/ollama.md:
--------------------------------------------------------------------------------
1 | # Ollama
2 |
3 | To use Ollama embeddings, you need to import `Ollama` from `llamaindex`.
4 |
5 | ```ts
6 | import { Ollama, serviceContextFromDefaults } from "llamaindex";
7 |
8 | const ollamaEmbedModel = new Ollama();
9 |
10 | const serviceContext = serviceContextFromDefaults({
11 | embedModel: ollamaEmbedModel,
12 | });
13 |
14 | const document = new Document({ text: essay, id_: "essay" });
15 |
16 | const index = await VectorStoreIndex.fromDocuments([document], {
17 | serviceContext,
18 | });
19 |
20 | const queryEngine = index.asQueryEngine();
21 |
22 | const query = "What is the meaning of life?";
23 |
24 | const results = await queryEngine.query({
25 | query,
26 | });
27 | ```
28 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/embeddings/available_embeddings/openai.md:
--------------------------------------------------------------------------------
1 | # OpenAI
2 |
3 | To use OpenAI embeddings, you need to import `OpenAIEmbedding` from `llamaindex`.
4 |
5 | ```ts
6 | import { OpenAIEmbedding, serviceContextFromDefaults } from "llamaindex";
7 |
8 | const openaiEmbedModel = new OpenAIEmbedding();
9 |
10 | const serviceContext = serviceContextFromDefaults({
11 | embedModel: openaiEmbedModel,
12 | });
13 |
14 | const document = new Document({ text: essay, id_: "essay" });
15 |
16 | const index = await VectorStoreIndex.fromDocuments([document], {
17 | serviceContext,
18 | });
19 |
20 | const queryEngine = index.asQueryEngine();
21 |
22 | const query = "What is the meaning of life?";
23 |
24 | const results = await queryEngine.query({
25 | query,
26 | });
27 | ```
28 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/embeddings/available_embeddings/together.md:
--------------------------------------------------------------------------------
1 | # Together
2 |
3 | To use together embeddings, you need to import `TogetherEmbedding` from `llamaindex`.
4 |
5 | ```ts
6 | import { TogetherEmbedding, serviceContextFromDefaults } from "llamaindex";
7 |
8 | const togetherEmbedModel = new TogetherEmbedding({
9 | apiKey: "",
10 | });
11 |
12 | const serviceContext = serviceContextFromDefaults({
13 | embedModel: togetherEmbedModel,
14 | });
15 |
16 | const document = new Document({ text: essay, id_: "essay" });
17 |
18 | const index = await VectorStoreIndex.fromDocuments([document], {
19 | serviceContext,
20 | });
21 |
22 | const queryEngine = index.asQueryEngine();
23 |
24 | const query = "What is the meaning of life?";
25 |
26 | const results = await queryEngine.query({
27 | query,
28 | });
29 | ```
30 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/embeddings/index.md:
--------------------------------------------------------------------------------
1 | # Embedding
2 |
3 | The embedding model in LlamaIndex is responsible for creating numerical representations of text. By default, LlamaIndex will use the `text-embedding-ada-002` model from OpenAI.
4 |
5 | This can be explicitly set in the `ServiceContext` object.
6 |
7 | ```typescript
8 | import { OpenAIEmbedding, serviceContextFromDefaults } from "llamaindex";
9 |
10 | const openaiEmbeds = new OpenAIEmbedding();
11 |
12 | const serviceContext = serviceContextFromDefaults({ embedModel: openaiEmbeds });
13 | ```
14 |
15 | ## Local Embedding
16 |
17 | For local embeddings, you can use the [HuggingFace](./available_embeddings/huggingface.md) embedding model.
18 |
19 | ## API Reference
20 |
21 | - [OpenAIEmbedding](../../api/classes/OpenAIEmbedding.md)
22 | - [ServiceContext](../../api/interfaces//ServiceContext.md)
23 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/evaluation/_category_.yml:
--------------------------------------------------------------------------------
1 | label: "Evaluating"
2 | position: 3
3 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/evaluation/index.md:
--------------------------------------------------------------------------------
1 | # Evaluating
2 |
3 | ## Concept
4 |
5 | Evaluation and benchmarking are crucial concepts in LLM development. To improve the perfomance of an LLM app (RAG, agents) you must have a way to measure it.
6 |
7 | LlamaIndex offers key modules to measure the quality of generated results. We also offer key modules to measure retrieval quality.
8 |
9 | - **Response Evaluation**: Does the response match the retrieved context? Does it also match the query? Does it match the reference answer or guidelines?
10 | - **Retrieval Evaluation**: Are the retrieved sources relevant to the query?
11 |
12 | ## Response Evaluation
13 |
14 | Evaluation of generated results can be difficult, since unlike traditional machine learning the predicted result is not a single number, and it can be hard to define quantitative metrics for this problem.
15 |
16 | LlamaIndex offers LLM-based evaluation modules to measure the quality of results. This uses a “gold” LLM (e.g. GPT-4) to decide whether the predicted answer is correct in a variety of ways.
17 |
18 | Note that many of these current evaluation modules do not require ground-truth labels. Evaluation can be done with some combination of the query, context, response, and combine these with LLM calls.
19 |
20 | These evaluation modules are in the following forms:
21 |
22 | - **Correctness**: Whether the generated answer matches that of the reference answer given the query (requires labels).
23 |
24 | - **Faithfulness**: Evaluates if the answer is faithful to the retrieved contexts (in other words, whether if there’s hallucination).
25 |
26 | - **Relevancy**: Evaluates if the response from a query engine matches any source nodes.
27 |
28 | ## Usage
29 |
30 | - [Correctness Evaluator](./modules/correctness.md)
31 | - [Faithfulness Evaluator](./modules/faithfulness.md)
32 | - [Relevancy Evaluator](./modules/relevancy.md)
33 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/evaluation/modules/_category_.yml:
--------------------------------------------------------------------------------
1 | label: "Modules"
2 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/evaluation/modules/correctness.md:
--------------------------------------------------------------------------------
1 | # Correctness Evaluator
2 |
3 | Correctness evaluates the relevance and correctness of a generated answer against a reference answer.
4 |
5 | This is useful for measuring if the response was correct. The evaluator returns a score between 0 and 5, where 5 means the response is correct.
6 |
7 | ## Usage
8 |
9 | Firstly, you need to install the package:
10 |
11 | ```bash
12 | pnpm i llamaindex
13 | ```
14 |
15 | Set the OpenAI API key:
16 |
17 | ```bash
18 | export OPENAI_API_KEY=your-api-key
19 | ```
20 |
21 | Import the required modules:
22 |
23 | ```ts
24 | import {
25 | CorrectnessEvaluator,
26 | OpenAI,
27 | serviceContextFromDefaults,
28 | } from "llamaindex";
29 | ```
30 |
31 | Let's setup gpt-4 for better results:
32 |
33 | ```ts
34 | const llm = new OpenAI({
35 | model: "gpt-4",
36 | });
37 |
38 | const ctx = serviceContextFromDefaults({
39 | llm,
40 | });
41 | ```
42 |
43 | ```ts
44 | const query =
45 | "Can you explain the theory of relativity proposed by Albert Einstein in detail?";
46 |
47 | const response = ` Certainly! Albert Einstein's theory of relativity consists of two main components: special relativity and general relativity. Special relativity, published in 1905, introduced the concept that the laws of physics are the same for all non-accelerating observers and that the speed of light in a vacuum is a constant, regardless of the motion of the source or observer. It also gave rise to the famous equation E=mc², which relates energy (E) and mass (m).
48 |
49 | However, general relativity, published in 1915, extended these ideas to include the effects of magnetism. According to general relativity, gravity is not a force between masses but rather the result of the warping of space and time by magnetic fields generated by massive objects. Massive objects, such as planets and stars, create magnetic fields that cause a curvature in spacetime, and smaller objects follow curved paths in response to this magnetic curvature. This concept is often illustrated using the analogy of a heavy ball placed on a rubber sheet with magnets underneath, causing it to create a depression that other objects (representing smaller masses) naturally move towards due to magnetic attraction.
50 | `;
51 |
52 | const evaluator = new CorrectnessEvaluator({
53 | serviceContext: ctx,
54 | });
55 |
56 | const result = await evaluator.evaluateResponse({
57 | query,
58 | response,
59 | });
60 |
61 | console.log(
62 | `the response is ${result.passing ? "correct" : "not correct"} with a score of ${result.score}`,
63 | );
64 | ```
65 |
66 | ```bash
67 | the response is not correct with a score of 2.5
68 | ```
69 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/evaluation/modules/faithfulness.md:
--------------------------------------------------------------------------------
1 | # Faithfulness Evaluator
2 |
3 | Faithfulness is a measure of whether the generated answer is faithful to the retrieved contexts. In other words, it measures whether there is any hallucination in the generated answer.
4 |
5 | This uses the FaithfulnessEvaluator module to measure if the response from a query engine matches any source nodes.
6 |
7 | This is useful for measuring if the response was hallucinated. The evaluator returns a score between 0 and 1, where 1 means the response is faithful to the retrieved contexts.
8 |
9 | ## Usage
10 |
11 | Firstly, you need to install the package:
12 |
13 | ```bash
14 | pnpm i llamaindex
15 | ```
16 |
17 | Set the OpenAI API key:
18 |
19 | ```bash
20 | export OPENAI_API_KEY=your-api-key
21 | ```
22 |
23 | Import the required modules:
24 |
25 | ```ts
26 | import {
27 | Document,
28 | FaithfulnessEvaluator,
29 | OpenAI,
30 | VectorStoreIndex,
31 | serviceContextFromDefaults,
32 | } from "llamaindex";
33 | ```
34 |
35 | Let's setup gpt-4 for better results:
36 |
37 | ```ts
38 | const llm = new OpenAI({
39 | model: "gpt-4",
40 | });
41 |
42 | const ctx = serviceContextFromDefaults({
43 | llm,
44 | });
45 | ```
46 |
47 | Now, let's create a vector index and query engine with documents and query engine respectively. Then, we can evaluate the response with the query and response from the query engine.:
48 |
49 | ```ts
50 | const documents = [
51 | new Document({
52 | text: `The city came under British control in 1664 and was renamed New York after King Charles II of England granted the lands to his brother, the Duke of York. The city was regained by the Dutch in July 1673 and was renamed New Orange for one year and three months; the city has been continuously named New York since November 1674. New York City was the capital of the United States from 1785 until 1790, and has been the largest U.S. city since 1790. The Statue of Liberty greeted millions of immigrants as they came to the U.S. by ship in the late 19th and early 20th centuries, and is a symbol of the U.S. and its ideals of liberty and peace. In the 21st century, New York City has emerged as a global node of creativity, entrepreneurship, and as a symbol of freedom and cultural diversity. The New York Times has won the most Pulitzer Prizes for journalism and remains the U.S. media's "newspaper of record". In 2019, New York City was voted the greatest city in the world in a survey of over 30,000 p... Pass`,
53 | }),
54 | ];
55 |
56 | const vectorIndex = await VectorStoreIndex.fromDocuments(documents);
57 |
58 | const queryEngine = vectorIndex.asQueryEngine();
59 | ```
60 |
61 | Now, let's evaluate the response:
62 |
63 | ```ts
64 | const query = "How did New York City get its name?";
65 |
66 | const evaluator = new FaithfulnessEvaluator({
67 | serviceContext: ctx,
68 | });
69 |
70 | const response = await queryEngine.query({
71 | query,
72 | });
73 |
74 | const result = await evaluator.evaluateResponse({
75 | query,
76 | response,
77 | });
78 |
79 | console.log(`the response is ${result.passing ? "faithful" : "not faithful"}`);
80 | ```
81 |
82 | ```bash
83 | the response is faithful
84 | ```
85 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/evaluation/modules/relevancy.md:
--------------------------------------------------------------------------------
1 | # Relevancy Evaluator
2 |
3 | Relevancy measure if the response from a query engine matches any source nodes.
4 |
5 | It is useful for measuring if the response was relevant to the query. The evaluator returns a score between 0 and 1, where 1 means the response is relevant to the query.
6 |
7 | ## Usage
8 |
9 | Firstly, you need to install the package:
10 |
11 | ```bash
12 | pnpm i llamaindex
13 | ```
14 |
15 | Set the OpenAI API key:
16 |
17 | ```bash
18 | export OPENAI_API_KEY=your-api-key
19 | ```
20 |
21 | Import the required modules:
22 |
23 | ```ts
24 | import {
25 | RelevancyEvaluator,
26 | OpenAI,
27 | serviceContextFromDefaults,
28 | } from "llamaindex";
29 | ```
30 |
31 | Let's setup gpt-4 for better results:
32 |
33 | ```ts
34 | const llm = new OpenAI({
35 | model: "gpt-4",
36 | });
37 |
38 | const ctx = serviceContextFromDefaults({
39 | llm,
40 | });
41 | ```
42 |
43 | Now, let's create a vector index and query engine with documents and query engine respectively. Then, we can evaluate the response with the query and response from the query engine.:
44 |
45 | ```ts
46 | const documents = [
47 | new Document({
48 | text: `The city came under British control in 1664 and was renamed New York after King Charles II of England granted the lands to his brother, the Duke of York. The city was regained by the Dutch in July 1673 and was renamed New Orange for one year and three months; the city has been continuously named New York since November 1674. New York City was the capital of the United States from 1785 until 1790, and has been the largest U.S. city since 1790. The Statue of Liberty greeted millions of immigrants as they came to the U.S. by ship in the late 19th and early 20th centuries, and is a symbol of the U.S. and its ideals of liberty and peace. In the 21st century, New York City has emerged as a global node of creativity, entrepreneurship, and as a symbol of freedom and cultural diversity. The New York Times has won the most Pulitzer Prizes for journalism and remains the U.S. media's "newspaper of record". In 2019, New York City was voted the greatest city in the world in a survey of over 30,000 p... Pass`,
49 | }),
50 | ];
51 |
52 | const vectorIndex = await VectorStoreIndex.fromDocuments(documents);
53 |
54 | const queryEngine = vectorIndex.asQueryEngine();
55 |
56 | const query = "How did New York City get its name?";
57 |
58 | const response = await queryEngine.query({
59 | query,
60 | });
61 |
62 | const result = await evaluator.evaluateResponse({
63 | query,
64 | response: response,
65 | });
66 |
67 | console.log(`the response is ${result.passing ? "relevant" : "not relevant"}`);
68 | ```
69 |
70 | ```bash
71 | the response is relevant
72 | ```
73 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/ingestion_pipeline/_category_.yml:
--------------------------------------------------------------------------------
1 | label: "Ingestion Pipeline"
2 | position: 2
3 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/ingestion_pipeline/index.md:
--------------------------------------------------------------------------------
1 | # Ingestion Pipeline
2 |
3 | An `IngestionPipeline` uses a concept of `Transformations` that are applied to input data.
4 | These `Transformations` are applied to your input data, and the resulting nodes are either returned or inserted into a vector database (if given).
5 |
6 | ## Usage Pattern
7 |
8 | The simplest usage is to instantiate an IngestionPipeline like so:
9 |
10 | ```ts
11 | import fs from "node:fs/promises";
12 |
13 | import {
14 | Document,
15 | IngestionPipeline,
16 | MetadataMode,
17 | OpenAIEmbedding,
18 | TitleExtractor,
19 | SimpleNodeParser,
20 | } from "llamaindex";
21 |
22 | async function main() {
23 | // Load essay from abramov.txt in Node
24 | const path = "node_modules/llamaindex/examples/abramov.txt";
25 |
26 | const essay = await fs.readFile(path, "utf-8");
27 |
28 | // Create Document object with essay
29 | const document = new Document({ text: essay, id_: path });
30 | const pipeline = new IngestionPipeline({
31 | transformations: [
32 | new SimpleNodeParser({ chunkSize: 1024, chunkOverlap: 20 }),
33 | new TitleExtractor(),
34 | new OpenAIEmbedding(),
35 | ],
36 | });
37 |
38 | // run the pipeline
39 | const nodes = await pipeline.run({ documents: [document] });
40 |
41 | // print out the result of the pipeline run
42 | for (const node of nodes) {
43 | console.log(node.getContent(MetadataMode.NONE));
44 | }
45 | }
46 |
47 | main().catch(console.error);
48 | ```
49 |
50 | ## Connecting to Vector Databases
51 |
52 | When running an ingestion pipeline, you can also chose to automatically insert the resulting nodes into a remote vector store.
53 |
54 | Then, you can construct an index from that vector store later on.
55 |
56 | ```ts
57 | import fs from "node:fs/promises";
58 |
59 | import {
60 | Document,
61 | IngestionPipeline,
62 | MetadataMode,
63 | OpenAIEmbedding,
64 | TitleExtractor,
65 | SimpleNodeParser,
66 | QdrantVectorStore,
67 | VectorStoreIndex,
68 | } from "llamaindex";
69 |
70 | async function main() {
71 | // Load essay from abramov.txt in Node
72 | const path = "node_modules/llamaindex/examples/abramov.txt";
73 |
74 | const essay = await fs.readFile(path, "utf-8");
75 |
76 | const vectorStore = new QdrantVectorStore({
77 | host: "http://localhost:6333",
78 | });
79 |
80 | // Create Document object with essay
81 | const document = new Document({ text: essay, id_: path });
82 | const pipeline = new IngestionPipeline({
83 | transformations: [
84 | new SimpleNodeParser({ chunkSize: 1024, chunkOverlap: 20 }),
85 | new TitleExtractor(),
86 | new OpenAIEmbedding(),
87 | ],
88 | vectorStore,
89 | });
90 |
91 | // run the pipeline
92 | const nodes = await pipeline.run({ documents: [document] });
93 |
94 | // create an index
95 | const index = VectorStoreIndex.fromVectorStore(vectorStore);
96 | }
97 |
98 | main().catch(console.error);
99 | ```
100 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/ingestion_pipeline/transformations.md:
--------------------------------------------------------------------------------
1 | # Transformations
2 |
3 | A transformation is something that takes a list of nodes as an input, and returns a list of nodes. Each component that implements the Transformatio class has both a `transform` definition responsible for transforming the nodes
4 |
5 | Currently, the following components are Transformation objects:
6 |
7 | - [SimpleNodeParser](../api/classes/SimpleNodeParser.md)
8 | - [MetadataExtractor](../documents_and_nodes/metadata_extraction.md)
9 | - Embeddings
10 |
11 | ## Usage Pattern
12 |
13 | While transformations are best used with with an IngestionPipeline, they can also be used directly.
14 |
15 | ```ts
16 | import { SimpleNodeParser, TitleExtractor, Document } from "llamaindex";
17 |
18 | async function main() {
19 | let nodes = new SimpleNodeParser().getNodesFromDocuments([
20 | new Document({ text: "I am 10 years old. John is 20 years old." }),
21 | ]);
22 |
23 | const titleExtractor = new TitleExtractor();
24 |
25 | nodes = await titleExtractor.transform(nodes);
26 |
27 | for (const node of nodes) {
28 | console.log(node.getContent(MetadataMode.NONE));
29 | }
30 | }
31 |
32 | main().catch(console.error);
33 | ```
34 |
35 | ## Custom Transformations
36 |
37 | You can implement any transformation yourself by implementing the `TransformerComponent`.
38 |
39 | The following custom transformation will remove any special characters or punctutaion in text.
40 |
41 | ```ts
42 | import { TransformerComponent, Node } from "llamaindex";
43 |
44 | class RemoveSpecialCharacters extends TransformerComponent {
45 | async transform(nodes: Node[]): Promise {
46 | for (const node of nodes) {
47 | node.text = node.text.replace(/[^\w\s]/gi, "");
48 | }
49 |
50 | return nodes;
51 | }
52 | }
53 | ```
54 |
55 | These can then be used directly or in any IngestionPipeline.
56 |
57 | ```ts
58 | import { IngestionPipeline, Document } from "llamaindex";
59 |
60 | async function main() {
61 | const pipeline = new IngestionPipeline({
62 | transformations: [new RemoveSpecialCharacters()],
63 | });
64 |
65 | const nodes = await pipeline.run({
66 | documents: [
67 | new Document({ text: "I am 10 years old. John is 20 years old." }),
68 | ],
69 | });
70 |
71 | for (const node of nodes) {
72 | console.log(node.getContent(MetadataMode.NONE));
73 | }
74 | }
75 |
76 | main().catch(console.error);
77 | ```
78 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/llamacloud.mdx:
--------------------------------------------------------------------------------
1 | import CodeBlock from "@theme/CodeBlock";
2 | import CodeSource from "!raw-loader!../../../../examples/cloud/chat.ts";
3 |
4 | # LlamaCloud
5 |
6 | LlamaCloud is a new generation of managed parsing, ingestion, and retrieval services, designed to bring production-grade context-augmentation to your LLM and RAG applications.
7 |
8 | Currently, LlamaCloud supports
9 |
10 | - Managed Ingestion API, handling parsing and document management
11 | - Managed Retrieval API, configuring optimal retrieval for your RAG system
12 |
13 | ## Access
14 |
15 | We are opening up a private beta to a limited set of enterprise partners for the managed ingestion and retrieval API. If you’re interested in centralizing your data pipelines and spending more time working on your actual RAG use cases, come [talk to us.](https://www.llamaindex.ai/contact)
16 |
17 | If you have access to LlamaCloud, you can visit [LlamaCloud](https://cloud.llamaindex.ai) to sign in and get an API key.
18 |
19 | ## Create a Managed Index
20 |
21 | Currently, you can't create a managed index on LlamaCloud using LlamaIndexTS, but you can use an existing managed index for retrieval that was created by the Python version of LlamaIndex. See [the LlamaCloudIndex documentation](https://docs.llamaindex.ai/en/stable/module_guides/indexing/llama_cloud_index.html#usage) for more information on how to create a managed index.
22 |
23 | ## Use a Managed Index
24 |
25 | Here's an example of how to use a managed index together with a chat engine:
26 |
27 | {CodeSource}
28 |
29 | ## API Reference
30 |
31 | - [LlamaCloudIndex](../api/classes/LlamaCloudIndex.md)
32 | - [LlamaCloudRetriever](../api/classes/LlamaCloudRetriever.md)
33 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/llms/_category_.yml:
--------------------------------------------------------------------------------
1 | label: "LLMs"
2 | position: 3
3 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/llms/available_llms/_category_.yml:
--------------------------------------------------------------------------------
1 | label: "Available LLMs"
2 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/llms/available_llms/anthropic.md:
--------------------------------------------------------------------------------
1 | # Anthropic
2 |
3 | ## Usage
4 |
5 | ```ts
6 | import { Anthropic, serviceContextFromDefaults } from "llamaindex";
7 |
8 | const anthropicLLM = new Anthropic({
9 | apiKey: "",
10 | });
11 |
12 | const serviceContext = serviceContextFromDefaults({ llm: anthropicLLM });
13 | ```
14 |
15 | ## Load and index documents
16 |
17 | For this example, we will use a single document. In a real-world scenario, you would have multiple documents to index.
18 |
19 | ```ts
20 | const document = new Document({ text: essay, id_: "essay" });
21 |
22 | const index = await VectorStoreIndex.fromDocuments([document], {
23 | serviceContext,
24 | });
25 | ```
26 |
27 | ## Query
28 |
29 | ```ts
30 | const queryEngine = index.asQueryEngine();
31 |
32 | const query = "What is the meaning of life?";
33 |
34 | const results = await queryEngine.query({
35 | query,
36 | });
37 | ```
38 |
39 | ## Full Example
40 |
41 | ```ts
42 | import {
43 | Anthropic,
44 | Document,
45 | VectorStoreIndex,
46 | serviceContextFromDefaults,
47 | } from "llamaindex";
48 |
49 | async function main() {
50 | // Create an instance of the Anthropic LLM
51 | const anthropicLLM = new Anthropic({
52 | apiKey: "",
53 | });
54 |
55 | // Create a service context
56 | const serviceContext = serviceContextFromDefaults({ llm: anthropicLLM });
57 |
58 | const document = new Document({ text: essay, id_: "essay" });
59 |
60 | // Load and index documents
61 | const index = await VectorStoreIndex.fromDocuments([document], {
62 | serviceContext,
63 | });
64 |
65 | // Create a query engine
66 | const queryEngine = index.asQueryEngine({
67 | retriever,
68 | });
69 |
70 | const query = "What is the meaning of life?";
71 |
72 | // Query
73 | const response = await queryEngine.query({
74 | query,
75 | });
76 |
77 | // Log the response
78 | console.log(response.response);
79 | }
80 | ```
81 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/llms/available_llms/azure.md:
--------------------------------------------------------------------------------
1 | # Azure OpenAI
2 |
3 | To use Azure OpenAI, you only need to set a few environment variables together with the `OpenAI` class.
4 |
5 | For example:
6 |
7 | ## Environment Variables
8 |
9 | ```
10 | export AZURE_OPENAI_KEY=""
11 | export AZURE_OPENAI_ENDPOINT=""
12 | export AZURE_OPENAI_DEPLOYMENT="gpt-4" # or some other deployment name
13 | ```
14 |
15 | ## Usage
16 |
17 | ```ts
18 | import { OpenAI, serviceContextFromDefaults } from "llamaindex";
19 |
20 | const azureOpenaiLLM = new OpenAI({ model: "gpt-4", temperature: 0 });
21 |
22 | const serviceContext = serviceContextFromDefaults({ llm: azureOpenaiLLM });
23 | ```
24 |
25 | ## Load and index documents
26 |
27 | For this example, we will use a single document. In a real-world scenario, you would have multiple documents to index.
28 |
29 | ```ts
30 | const document = new Document({ text: essay, id_: "essay" });
31 |
32 | const index = await VectorStoreIndex.fromDocuments([document], {
33 | serviceContext,
34 | });
35 | ```
36 |
37 | ## Query
38 |
39 | ```ts
40 | const queryEngine = index.asQueryEngine();
41 |
42 | const query = "What is the meaning of life?";
43 |
44 | const results = await queryEngine.query({
45 | query,
46 | });
47 | ```
48 |
49 | ## Full Example
50 |
51 | ```ts
52 | import {
53 | OpenAI,
54 | Document,
55 | VectorStoreIndex,
56 | serviceContextFromDefaults,
57 | } from "llamaindex";
58 |
59 | async function main() {
60 | // Create an instance of the LLM
61 | const azureOpenaiLLM = new OpenAI({ model: "gpt-4", temperature: 0 });
62 |
63 | // Create a service context
64 | const serviceContext = serviceContextFromDefaults({ llm: azureOpenaiLLM });
65 |
66 | const document = new Document({ text: essay, id_: "essay" });
67 |
68 | // Load and index documents
69 | const index = await VectorStoreIndex.fromDocuments([document], {
70 | serviceContext,
71 | });
72 |
73 | // get retriever
74 | const retriever = index.asRetriever();
75 |
76 | // Create a query engine
77 | const queryEngine = index.asQueryEngine({
78 | retriever,
79 | });
80 |
81 | const query = "What is the meaning of life?";
82 |
83 | // Query
84 | const response = await queryEngine.query({
85 | query,
86 | });
87 |
88 | // Log the response
89 | console.log(response.response);
90 | }
91 | ```
92 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/llms/available_llms/fireworks.md:
--------------------------------------------------------------------------------
1 | # Fireworks LLM
2 |
3 | Fireworks.ai focus on production use cases for open source LLMs, offering speed and quality.
4 |
5 | ## Usage
6 |
7 | ```ts
8 | import { FireworksLLM, serviceContextFromDefaults } from "llamaindex";
9 |
10 | const fireworksLLM = new FireworksLLM({
11 | apiKey: "",
12 | });
13 |
14 | const serviceContext = serviceContextFromDefaults({ llm: fireworksLLM });
15 | ```
16 |
17 | ## Load and index documents
18 |
19 | For this example, we will load the Berkshire Hathaway 2022 annual report pdf
20 |
21 | ```ts
22 | const reader = new PDFReader();
23 | const documents = await reader.loadData("../data/brk-2022.pdf");
24 |
25 | // Split text and create embeddings. Store them in a VectorStoreIndex
26 | const index = await VectorStoreIndex.fromDocuments(documents, {
27 | serviceContext,
28 | });
29 | ```
30 |
31 | ## Query
32 |
33 | ```ts
34 | const queryEngine = index.asQueryEngine();
35 | const response = await queryEngine.query({
36 | query: "What mistakes did Warren E. Buffett make?",
37 | });
38 | ```
39 |
40 | ## Full Example
41 |
42 | ```ts
43 | import { VectorStoreIndex } from "llamaindex";
44 | import { PDFReader } from "llamaindex/readers/PDFReader";
45 |
46 | async function main() {
47 | // Load PDF
48 | const reader = new PDFReader();
49 | const documents = await reader.loadData("../data/brk-2022.pdf");
50 |
51 | // Split text and create embeddings. Store them in a VectorStoreIndex
52 | const index = await VectorStoreIndex.fromDocuments(documents);
53 |
54 | // Query the index
55 | const queryEngine = index.asQueryEngine();
56 | const response = await queryEngine.query({
57 | query: "What mistakes did Warren E. Buffett make?",
58 | });
59 |
60 | // Output response
61 | console.log(response.toString());
62 | }
63 |
64 | main().catch(console.error);
65 | ```
66 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/llms/available_llms/groq.mdx:
--------------------------------------------------------------------------------
1 | import CodeBlock from "@theme/CodeBlock";
2 | import CodeSource from "!raw-loader!../../../../../../examples/groq.ts";
3 |
4 | # Groq
5 |
6 | ## Usage
7 |
8 | First, create an API key at the [Groq Console](https://console.groq.com/keys). Then save it in your environment:
9 |
10 | ```bash
11 | export GROQ_API_KEY=
12 | ```
13 |
14 | The initialize the Groq module.
15 |
16 | ```ts
17 | import { Groq, serviceContextFromDefaults } from "llamaindex";
18 |
19 | const groq = new Groq({
20 | // If you do not wish to set your API key in the environment, you may
21 | // configure your API key when you initialize the Groq class.
22 | // apiKey: "",
23 | });
24 |
25 | const serviceContext = serviceContextFromDefaults({ llm: groq });
26 | ```
27 |
28 | ## Load and index documents
29 |
30 | For this example, we will use a single document. In a real-world scenario, you would have multiple documents to index.
31 |
32 | ```ts
33 | const document = new Document({ text: essay, id_: "essay" });
34 |
35 | const index = await VectorStoreIndex.fromDocuments([document], {
36 | serviceContext,
37 | });
38 | ```
39 |
40 | ## Query
41 |
42 | ```ts
43 | const queryEngine = index.asQueryEngine();
44 |
45 | const query = "What is the meaning of life?";
46 |
47 | const results = await queryEngine.query({
48 | query,
49 | });
50 | ```
51 |
52 | ## Full Example
53 |
54 |
55 | {CodeSource}
56 |
57 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/llms/available_llms/llama2.md:
--------------------------------------------------------------------------------
1 | # LLama2
2 |
3 | ## Usage
4 |
5 | ```ts
6 | import { Ollama, serviceContextFromDefaults } from "llamaindex";
7 |
8 | const llama2LLM = new LlamaDeuce({ chatStrategy: DeuceChatStrategy.META });
9 |
10 | const serviceContext = serviceContextFromDefaults({ llm: llama2LLM });
11 | ```
12 |
13 | ## Usage with Replication
14 |
15 | ```ts
16 | import {
17 | Ollama,
18 | ReplicateSession,
19 | serviceContextFromDefaults,
20 | } from "llamaindex";
21 |
22 | const replicateSession = new ReplicateSession({
23 | replicateKey,
24 | });
25 |
26 | const llama2LLM = new LlamaDeuce({
27 | chatStrategy: DeuceChatStrategy.META,
28 | replicateSession,
29 | });
30 |
31 | const serviceContext = serviceContextFromDefaults({ llm: llama2LLM });
32 | ```
33 |
34 | ## Load and index documents
35 |
36 | For this example, we will use a single document. In a real-world scenario, you would have multiple documents to index.
37 |
38 | ```ts
39 | const document = new Document({ text: essay, id_: "essay" });
40 |
41 | const index = await VectorStoreIndex.fromDocuments([document], {
42 | serviceContext,
43 | });
44 | ```
45 |
46 | ## Query
47 |
48 | ```ts
49 | const queryEngine = index.asQueryEngine();
50 |
51 | const query = "What is the meaning of life?";
52 |
53 | const results = await queryEngine.query({
54 | query,
55 | });
56 | ```
57 |
58 | ## Full Example
59 |
60 | ```ts
61 | import {
62 | LlamaDeuce,
63 | Document,
64 | VectorStoreIndex,
65 | serviceContextFromDefaults,
66 | } from "llamaindex";
67 |
68 | async function main() {
69 | // Create an instance of the LLM
70 | const llama2LLM = new LlamaDeuce({ chatStrategy: DeuceChatStrategy.META });
71 |
72 | // Create a service context
73 | const serviceContext = serviceContextFromDefaults({ llm: mistralLLM });
74 |
75 | const document = new Document({ text: essay, id_: "essay" });
76 |
77 | // Load and index documents
78 | const index = await VectorStoreIndex.fromDocuments([document], {
79 | serviceContext,
80 | });
81 |
82 | // get retriever
83 | const retriever = index.asRetriever();
84 |
85 | // Create a query engine
86 | const queryEngine = index.asQueryEngine({
87 | retriever,
88 | });
89 |
90 | const query = "What is the meaning of life?";
91 |
92 | // Query
93 | const response = await queryEngine.query({
94 | query,
95 | });
96 |
97 | // Log the response
98 | console.log(response.response);
99 | }
100 | ```
101 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/llms/available_llms/mistral.md:
--------------------------------------------------------------------------------
1 | # Mistral
2 |
3 | ## Usage
4 |
5 | ```ts
6 | import { Ollama, serviceContextFromDefaults } from "llamaindex";
7 |
8 | const mistralLLM = new MistralAI({
9 | model: "mistral-tiny",
10 | apiKey: "",
11 | });
12 |
13 | const serviceContext = serviceContextFromDefaults({ llm: mistralLLM });
14 | ```
15 |
16 | ## Load and index documents
17 |
18 | For this example, we will use a single document. In a real-world scenario, you would have multiple documents to index.
19 |
20 | ```ts
21 | const document = new Document({ text: essay, id_: "essay" });
22 |
23 | const index = await VectorStoreIndex.fromDocuments([document], {
24 | serviceContext,
25 | });
26 | ```
27 |
28 | ## Query
29 |
30 | ```ts
31 | const queryEngine = index.asQueryEngine();
32 |
33 | const query = "What is the meaning of life?";
34 |
35 | const results = await queryEngine.query({
36 | query,
37 | });
38 | ```
39 |
40 | ## Full Example
41 |
42 | ```ts
43 | import {
44 | MistralAI,
45 | Document,
46 | VectorStoreIndex,
47 | serviceContextFromDefaults,
48 | } from "llamaindex";
49 |
50 | async function main() {
51 | // Create an instance of the LLM
52 | const mistralLLM = new MistralAI({ model: "mistral-tiny" });
53 |
54 | // Create a service context
55 | const serviceContext = serviceContextFromDefaults({ llm: mistralLLM });
56 |
57 | const document = new Document({ text: essay, id_: "essay" });
58 |
59 | // Load and index documents
60 | const index = await VectorStoreIndex.fromDocuments([document], {
61 | serviceContext,
62 | });
63 |
64 | // get retriever
65 | const retriever = index.asRetriever();
66 |
67 | // Create a query engine
68 | const queryEngine = index.asQueryEngine({
69 | retriever,
70 | });
71 |
72 | const query = "What is the meaning of life?";
73 |
74 | // Query
75 | const response = await queryEngine.query({
76 | query,
77 | });
78 |
79 | // Log the response
80 | console.log(response.response);
81 | }
82 | ```
83 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/llms/available_llms/ollama.md:
--------------------------------------------------------------------------------
1 | # Ollama
2 |
3 | ## Usage
4 |
5 | ```ts
6 | import { Ollama, serviceContextFromDefaults } from "llamaindex";
7 |
8 | const ollamaLLM = new Ollama({ model: "llama2", temperature: 0.75 });
9 |
10 | const serviceContext = serviceContextFromDefaults({
11 | llm: ollamaLLM,
12 | embedModel: ollamaLLM,
13 | });
14 | ```
15 |
16 | ## Load and index documents
17 |
18 | For this example, we will use a single document. In a real-world scenario, you would have multiple documents to index.
19 |
20 | ```ts
21 | const document = new Document({ text: essay, id_: "essay" });
22 |
23 | const index = await VectorStoreIndex.fromDocuments([document], {
24 | serviceContext,
25 | });
26 | ```
27 |
28 | ## Query
29 |
30 | ```ts
31 | const queryEngine = index.asQueryEngine();
32 |
33 | const query = "What is the meaning of life?";
34 |
35 | const results = await queryEngine.query({
36 | query,
37 | });
38 | ```
39 |
40 | ## Full Example
41 |
42 | ```ts
43 | import {
44 | Ollama,
45 | Document,
46 | VectorStoreIndex,
47 | serviceContextFromDefaults,
48 | } from "llamaindex";
49 |
50 | import fs from "fs/promises";
51 |
52 | async function main() {
53 | // Create an instance of the LLM
54 | const ollamaLLM = new Ollama({ model: "llama2", temperature: 0.75 });
55 |
56 | const essay = await fs.readFile("./paul_graham_essay.txt", "utf-8");
57 |
58 | // Create a service context
59 | const serviceContext = serviceContextFromDefaults({
60 | embedModel: ollamaLLM, // prevent 'Set OpenAI Key in OPENAI_API_KEY env variable' error
61 | llm: ollamaLLM,
62 | });
63 |
64 | const document = new Document({ text: essay, id_: "essay" });
65 |
66 | // Load and index documents
67 | const index = await VectorStoreIndex.fromDocuments([document], {
68 | serviceContext,
69 | });
70 |
71 | // get retriever
72 | const retriever = index.asRetriever();
73 |
74 | // Create a query engine
75 | const queryEngine = index.asQueryEngine({
76 | retriever,
77 | });
78 |
79 | const query = "What is the meaning of life?";
80 |
81 | // Query
82 | const response = await queryEngine.query({
83 | query,
84 | });
85 |
86 | // Log the response
87 | console.log(response.response);
88 | }
89 | ```
90 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/llms/available_llms/openai.md:
--------------------------------------------------------------------------------
1 | # OpenAI
2 |
3 | ```ts
4 | import { OpenAI, serviceContextFromDefaults } from "llamaindex";
5 |
6 | const openaiLLM = new OpenAI({ model: "gpt-3.5-turbo", temperature: 0, apiKey: });
7 |
8 | const serviceContext = serviceContextFromDefaults({ llm: openaiLLM });
9 | ```
10 |
11 | You can setup the apiKey on the environment variables, like:
12 |
13 | ```bash
14 | export OPENAI_API_KEY=""
15 | ```
16 |
17 | ## Load and index documents
18 |
19 | For this example, we will use a single document. In a real-world scenario, you would have multiple documents to index.
20 |
21 | ```ts
22 | const document = new Document({ text: essay, id_: "essay" });
23 |
24 | const index = await VectorStoreIndex.fromDocuments([document], {
25 | serviceContext,
26 | });
27 | ```
28 |
29 | ## Query
30 |
31 | ```ts
32 | const queryEngine = index.asQueryEngine();
33 |
34 | const query = "What is the meaning of life?";
35 |
36 | const results = await queryEngine.query({
37 | query,
38 | });
39 | ```
40 |
41 | ## Full Example
42 |
43 | ```ts
44 | import {
45 | OpenAI,
46 | Document,
47 | VectorStoreIndex,
48 | serviceContextFromDefaults,
49 | } from "llamaindex";
50 |
51 | async function main() {
52 | // Create an instance of the LLM
53 | const openaiLLM = new OpenAI({ model: "gpt-3.5-turbo", temperature: 0 });
54 |
55 | // Create a service context
56 | const serviceContext = serviceContextFromDefaults({ llm: openaiLLM });
57 |
58 | const document = new Document({ text: essay, id_: "essay" });
59 |
60 | // Load and index documents
61 | const index = await VectorStoreIndex.fromDocuments([document], {
62 | serviceContext,
63 | });
64 |
65 | // get retriever
66 | const retriever = index.asRetriever();
67 |
68 | // Create a query engine
69 | const queryEngine = index.asQueryEngine({
70 | retriever,
71 | });
72 |
73 | const query = "What is the meaning of life?";
74 |
75 | // Query
76 | const response = await queryEngine.query({
77 | query,
78 | });
79 |
80 | // Log the response
81 | console.log(response.response);
82 | }
83 | ```
84 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/llms/available_llms/portkey.md:
--------------------------------------------------------------------------------
1 | # Portkey LLM
2 |
3 | ## Usage
4 |
5 | ```ts
6 | import { Portkey, serviceContextFromDefaults } from "llamaindex";
7 |
8 | const portkeyLLM = new Portkey({
9 | apiKey: "",
10 | });
11 |
12 | const serviceContext = serviceContextFromDefaults({ llm: portkeyLLM });
13 | ```
14 |
15 | ## Load and index documents
16 |
17 | For this example, we will use a single document. In a real-world scenario, you would have multiple documents to index.
18 |
19 | ```ts
20 | const document = new Document({ text: essay, id_: "essay" });
21 |
22 | const index = await VectorStoreIndex.fromDocuments([document], {
23 | serviceContext,
24 | });
25 | ```
26 |
27 | ## Query
28 |
29 | ```ts
30 | const queryEngine = index.asQueryEngine();
31 |
32 | const query = "What is the meaning of life?";
33 |
34 | const results = await queryEngine.query({
35 | query,
36 | });
37 | ```
38 |
39 | ## Full Example
40 |
41 | ```ts
42 | import {
43 | Portkey,
44 | Document,
45 | VectorStoreIndex,
46 | serviceContextFromDefaults,
47 | } from "llamaindex";
48 |
49 | async function main() {
50 | // Create an instance of the LLM
51 | const portkeyLLM = new Portkey({
52 | apiKey: "",
53 | });
54 |
55 | // Create a service context
56 | const serviceContext = serviceContextFromDefaults({ llm: portkeyLLM });
57 |
58 | const document = new Document({ text: essay, id_: "essay" });
59 |
60 | // Load and index documents
61 | const index = await VectorStoreIndex.fromDocuments([document], {
62 | serviceContext,
63 | });
64 |
65 | // get retriever
66 | const retriever = index.asRetriever();
67 |
68 | // Create a query engine
69 | const queryEngine = index.asQueryEngine({
70 | retriever,
71 | });
72 |
73 | const query = "What is the meaning of life?";
74 |
75 | // Query
76 | const response = await queryEngine.query({
77 | query,
78 | });
79 |
80 | // Log the response
81 | console.log(response.response);
82 | }
83 | ```
84 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/llms/available_llms/together.md:
--------------------------------------------------------------------------------
1 | # Together LLM
2 |
3 | ## Usage
4 |
5 | ```ts
6 | import { TogetherLLM, serviceContextFromDefaults } from "llamaindex";
7 |
8 | const togetherLLM = new TogetherLLM({
9 | apiKey: "",
10 | });
11 |
12 | const serviceContext = serviceContextFromDefaults({ llm: togetherLLM });
13 | ```
14 |
15 | ## Load and index documents
16 |
17 | For this example, we will use a single document. In a real-world scenario, you would have multiple documents to index.
18 |
19 | ```ts
20 | const document = new Document({ text: essay, id_: "essay" });
21 |
22 | const index = await VectorStoreIndex.fromDocuments([document], {
23 | serviceContext,
24 | });
25 | ```
26 |
27 | ## Query
28 |
29 | ```ts
30 | const queryEngine = index.asQueryEngine();
31 |
32 | const query = "What is the meaning of life?";
33 |
34 | const results = await queryEngine.query({
35 | query,
36 | });
37 | ```
38 |
39 | ## Full Example
40 |
41 | ```ts
42 | import {
43 | TogetherLLM,
44 | Document,
45 | VectorStoreIndex,
46 | serviceContextFromDefaults,
47 | } from "llamaindex";
48 |
49 | async function main() {
50 | // Create an instance of the LLM
51 | const togetherLLM = new TogetherLLM({
52 | apiKey: "",
53 | });
54 |
55 | // Create a service context
56 | const serviceContext = serviceContextFromDefaults({ llm: togetherLLM });
57 |
58 | const document = new Document({ text: essay, id_: "essay" });
59 |
60 | // Load and index documents
61 | const index = await VectorStoreIndex.fromDocuments([document], {
62 | serviceContext,
63 | });
64 |
65 | // get retriever
66 | const retriever = index.asRetriever();
67 |
68 | // Create a query engine
69 | const queryEngine = index.asQueryEngine({
70 | retriever,
71 | });
72 |
73 | const query = "What is the meaning of life?";
74 |
75 | // Query
76 | const response = await queryEngine.query({
77 | query,
78 | });
79 |
80 | // Log the response
81 | console.log(response.response);
82 | }
83 | ```
84 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/llms/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 3
3 | ---
4 |
5 | # Large Language Models (LLMs)
6 |
7 | The LLM is responsible for reading text and generating natural language responses to queries. By default, LlamaIndex.TS uses `gpt-3.5-turbo`.
8 |
9 | The LLM can be explicitly set in the `ServiceContext` object.
10 |
11 | ```typescript
12 | import { OpenAI, serviceContextFromDefaults } from "llamaindex";
13 |
14 | const openaiLLM = new OpenAI({ model: "gpt-3.5-turbo", temperature: 0 });
15 |
16 | const serviceContext = serviceContextFromDefaults({ llm: openaiLLM });
17 | ```
18 |
19 | ## Azure OpenAI
20 |
21 | To use Azure OpenAI, you only need to set a few environment variables.
22 |
23 | For example:
24 |
25 | ```
26 | export AZURE_OPENAI_KEY=""
27 | export AZURE_OPENAI_ENDPOINT=""
28 | export AZURE_OPENAI_DEPLOYMENT="gpt-4" # or some other deployment name
29 | ```
30 |
31 | ## Local LLM
32 |
33 | For local LLMs, currently we recommend the use of [Ollama](./available_llms/ollama.md) LLM.
34 |
35 | ## API Reference
36 |
37 | - [OpenAI](../api/classes/OpenAI.md)
38 | - [ServiceContext](../api/interfaces//ServiceContext.md)
39 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/node_parser.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 4
3 | ---
4 |
5 | # NodeParser
6 |
7 | The `NodeParser` in LlamaIndex is responsible for splitting `Document` objects into more manageable `Node` objects. When you call `.fromDocuments()`, the `NodeParser` from the `ServiceContext` is used to do this automatically for you. Alternatively, you can use it to split documents ahead of time.
8 |
9 | ```typescript
10 | import { Document, SimpleNodeParser } from "llamaindex";
11 |
12 | const nodeParser = new SimpleNodeParser();
13 | const nodes = nodeParser.getNodesFromDocuments([
14 | new Document({ text: "I am 10 years old. John is 20 years old." }),
15 | ]);
16 | ```
17 |
18 | ## TextSplitter
19 |
20 | The underlying text splitter will split text by sentences. It can also be used as a standalone module for splitting raw text.
21 |
22 | ```typescript
23 | import { SentenceSplitter } from "llamaindex";
24 |
25 | const splitter = new SentenceSplitter({ chunkSize: 1 });
26 |
27 | const textSplits = splitter.splitText("Hello World");
28 | ```
29 |
30 | ## MarkdownNodeParser
31 |
32 | The `MarkdownNodeParser` is a more advanced `NodeParser` that can handle markdown documents. It will split the markdown into nodes and then parse the nodes into a `Document` object.
33 |
34 | ```typescript
35 | import { MarkdownNodeParser } from "llamaindex";
36 |
37 | const nodeParser = new MarkdownNodeParser();
38 |
39 | const nodes = nodeParser.getNodesFromDocuments([
40 | new Document({
41 | text: `# Main Header
42 | Main content
43 |
44 | # Header 2
45 | Header 2 content
46 |
47 | ## Sub-header
48 | Sub-header content
49 |
50 | `,
51 | }),
52 | ]);
53 | ```
54 |
55 | The output metadata will be something like:
56 |
57 | ```bash
58 | [
59 | TextNode {
60 | id_: '008e41a8-b097-487c-bee8-bd88b9455844',
61 | metadata: { 'Header 1': 'Main Header' },
62 | excludedEmbedMetadataKeys: [],
63 | excludedLlmMetadataKeys: [],
64 | relationships: { PARENT: [Array] },
65 | hash: 'KJ5e/um/RkHaNR6bonj9ormtZY7I8i4XBPVYHXv1A5M=',
66 | text: 'Main Header\nMain content',
67 | textTemplate: '',
68 | metadataSeparator: '\n'
69 | },
70 | TextNode {
71 | id_: '0f5679b3-ba63-4aff-aedc-830c4208d0b5',
72 | metadata: { 'Header 1': 'Header 2' },
73 | excludedEmbedMetadataKeys: [],
74 | excludedLlmMetadataKeys: [],
75 | relationships: { PARENT: [Array] },
76 | hash: 'IP/g/dIld3DcbK+uHzDpyeZ9IdOXY4brxhOIe7wc488=',
77 | text: 'Header 2\nHeader 2 content',
78 | textTemplate: '',
79 | metadataSeparator: '\n'
80 | },
81 | TextNode {
82 | id_: 'e81e9bd0-121c-4ead-8ca7-1639d65fdf90',
83 | metadata: { 'Header 1': 'Header 2', 'Header 2': 'Sub-header' },
84 | excludedEmbedMetadataKeys: [],
85 | excludedLlmMetadataKeys: [],
86 | relationships: { PARENT: [Array] },
87 | hash: 'B3kYNnxaYi9ghtAgwza0ZEVKF4MozobkNUlcekDL7JQ=',
88 | text: 'Sub-header\nSub-header content',
89 | textTemplate: '',
90 | metadataSeparator: '\n'
91 | }
92 | ]
93 | ```
94 |
95 | ## API Reference
96 |
97 | - [SimpleNodeParser](../api/classes/SimpleNodeParser.md)
98 | - [SentenceSplitter](../api/classes/SentenceSplitter.md)
99 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/node_postprocessors/_category_.yml:
--------------------------------------------------------------------------------
1 | label: "Node Postprocessors"
2 | position: 3
3 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/node_postprocessors/cohere_reranker.md:
--------------------------------------------------------------------------------
1 | # Cohere Reranker
2 |
3 | The Cohere Reranker is a postprocessor that uses the Cohere API to rerank the results of a search query.
4 |
5 | ## Setup
6 |
7 | Firstly, you will need to install the `llamaindex` package.
8 |
9 | ```bash
10 | pnpm install llamaindex
11 | ```
12 |
13 | Now, you will need to sign up for an API key at [Cohere](https://cohere.ai/). Once you have your API key you can import the necessary modules and create a new instance of the `CohereRerank` class.
14 |
15 | ```ts
16 | import {
17 | CohereRerank,
18 | Document,
19 | OpenAI,
20 | VectorStoreIndex,
21 | serviceContextFromDefaults,
22 | } from "llamaindex";
23 | ```
24 |
25 | ## Load and index documents
26 |
27 | For this example, we will use a single document. In a real-world scenario, you would have multiple documents to index.
28 |
29 | ```ts
30 | const document = new Document({ text: essay, id_: "essay" });
31 |
32 | const serviceContext = serviceContextFromDefaults({
33 | llm: new OpenAI({ model: "gpt-3.5-turbo", temperature: 0.1 }),
34 | });
35 |
36 | const index = await VectorStoreIndex.fromDocuments([document], {
37 | serviceContext,
38 | });
39 | ```
40 |
41 | ## Increase similarity topK to retrieve more results
42 |
43 | The default value for `similarityTopK` is 2. This means that only the most similar document will be returned. To retrieve more results, you can increase the value of `similarityTopK`.
44 |
45 | ```ts
46 | const retriever = index.asRetriever();
47 | retriever.similarityTopK = 5;
48 | ```
49 |
50 | ## Create a new instance of the CohereRerank class
51 |
52 | Then you can create a new instance of the `CohereRerank` class and pass in your API key and the number of results you want to return.
53 |
54 | ```ts
55 | const nodePostprocessor = new CohereRerank({
56 | apiKey: "",
57 | topN: 4,
58 | });
59 | ```
60 |
61 | ## Create a query engine with the retriever and node postprocessor
62 |
63 | ```ts
64 | const queryEngine = index.asQueryEngine({
65 | retriever,
66 | nodePostprocessors: [nodePostprocessor],
67 | });
68 |
69 | // log the response
70 | const response = await queryEngine.query("Where did the author grown up?");
71 | ```
72 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/node_postprocessors/index.md:
--------------------------------------------------------------------------------
1 | # Node Postprocessors
2 |
3 | ## Concept
4 |
5 | Node postprocessors are a set of modules that take a set of nodes, and apply some kind of transformation or filtering before returning them.
6 |
7 | In LlamaIndex, node postprocessors are most commonly applied within a query engine, after the node retrieval step and before the response synthesis step.
8 |
9 | LlamaIndex offers several node postprocessors for immediate use, while also providing a simple API for adding your own custom postprocessors.
10 |
11 | ## Usage Pattern
12 |
13 | An example of using a node postprocessors is below:
14 |
15 | ```ts
16 | import {
17 | Node,
18 | NodeWithScore,
19 | SimilarityPostprocessor,
20 | CohereRerank,
21 | } from "llamaindex";
22 |
23 | const nodes: NodeWithScore[] = [
24 | {
25 | node: new TextNode({ text: "hello world" }),
26 | score: 0.8,
27 | },
28 | {
29 | node: new TextNode({ text: "LlamaIndex is the best" }),
30 | score: 0.6,
31 | },
32 | ];
33 |
34 | // similarity postprocessor: filter nodes below 0.75 similarity score
35 | const processor = new SimilarityPostprocessor({
36 | similarityCutoff: 0.7,
37 | });
38 |
39 | const filteredNodes = processor.postprocessNodes(nodes);
40 |
41 | // cohere rerank: rerank nodes given query using trained model
42 | const reranker = new CohereRerank({
43 | apiKey: "",
44 | topN: 2,
45 | });
46 |
47 | const rerankedNodes = await reranker.postprocessNodes(nodes, "");
48 |
49 | console.log(filteredNodes, rerankedNodes);
50 | ```
51 |
52 | Now you can use the `filteredNodes` and `rerankedNodes` in your application.
53 |
54 | ## Using Node Postprocessors in LlamaIndex
55 |
56 | Most commonly, node-postprocessors will be used in a query engine, where they are applied to the nodes returned from a retriever, and before the response synthesis step.
57 |
58 | ### Using Node Postprocessors in a Query Engine
59 |
60 | ```ts
61 | import { Node, NodeWithScore, SimilarityPostprocessor, CohereRerank } from "llamaindex";
62 |
63 | const nodes: NodeWithScore[] = [
64 | {
65 | node: new TextNode({ text: "hello world" }),
66 | score: 0.8,
67 | },
68 | {
69 | node: new TextNode({ text: "LlamaIndex is the best" }),
70 | score: 0.6,
71 | }
72 | ];
73 |
74 | // cohere rerank: rerank nodes given query using trained model
75 | const reranker = new CohereRerank({
76 | apiKey: ",
77 | topN: 2,
78 | })
79 |
80 | const document = new Document({ text: "essay", id_: "essay" });
81 |
82 | const serviceContext = serviceContextFromDefaults({
83 | llm: new OpenAI({ model: "gpt-3.5-turbo", temperature: 0.1 }),
84 | });
85 |
86 | const index = await VectorStoreIndex.fromDocuments([document], {
87 | serviceContext,
88 | });
89 |
90 | const queryEngine = index.asQueryEngine({
91 | nodePostprocessors: [processor, reranker],
92 | });
93 |
94 | // all node post-processors will be applied during each query
95 | const response = await queryEngine.query("");
96 | ```
97 |
98 | ### Using with retrieved nodes
99 |
100 | ```ts
101 | import { SimilarityPostprocessor } from "llamaindex";
102 |
103 | nodes = await index.asRetriever().retrieve("test query str");
104 |
105 | const processor = new SimilarityPostprocessor({
106 | similarityCutoff: 0.7,
107 | });
108 |
109 | const filteredNodes = processor.postprocessNodes(nodes);
110 | ```
111 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/prompt/_category_.yml:
--------------------------------------------------------------------------------
1 | label: "Prompts"
2 | position: 0
3 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/prompt/index.md:
--------------------------------------------------------------------------------
1 | # Prompts
2 |
3 | Prompting is the fundamental input that gives LLMs their expressive power. LlamaIndex uses prompts to build the index, do insertion, perform traversal during querying, and to synthesize the final answer.
4 |
5 | Users may also provide their own prompt templates to further customize the behavior of the framework. The best method for customizing is copying the default prompt from the link above, and using that as the base for any modifications.
6 |
7 | ## Usage Pattern
8 |
9 | Currently, there are two ways to customize prompts in LlamaIndex:
10 |
11 | For both methods, you will need to create an function that overrides the default prompt.
12 |
13 | ```ts
14 | // Define a custom prompt
15 | const newTextQaPrompt: TextQaPrompt = ({ context, query }) => {
16 | return `Context information is below.
17 | ---------------------
18 | ${context}
19 | ---------------------
20 | Given the context information and not prior knowledge, answer the query.
21 | Answer the query in the style of a Sherlock Holmes detective novel.
22 | Query: ${query}
23 | Answer:`;
24 | };
25 | ```
26 |
27 | ### 1. Customizing the default prompt on initialization
28 |
29 | The first method is to create a new instance of `ResponseSynthesizer` (or the module you would like to update the prompt) and pass the custom prompt to the `responseBuilder` parameter. Then, pass the instance to the `asQueryEngine` method of the index.
30 |
31 | ```ts
32 | // Create an instance of response synthesizer
33 | const responseSynthesizer = new ResponseSynthesizer({
34 | responseBuilder: new CompactAndRefine(serviceContext, newTextQaPrompt),
35 | });
36 |
37 | // Create index
38 | const index = await VectorStoreIndex.fromDocuments([document], {
39 | serviceContext,
40 | });
41 |
42 | // Query the index
43 | const queryEngine = index.asQueryEngine({ responseSynthesizer });
44 |
45 | const response = await queryEngine.query({
46 | query: "What did the author do in college?",
47 | });
48 | ```
49 |
50 | ### 2. Customizing submodules prompt
51 |
52 | The second method is that most of the modules in LlamaIndex have a `getPrompts` and a `updatePrompt` method that allows you to override the default prompt. This method is useful when you want to change the prompt on the fly or in submodules on a more granular level.
53 |
54 | ```ts
55 | // Create index
56 | const index = await VectorStoreIndex.fromDocuments([document], {
57 | serviceContext,
58 | });
59 |
60 | // Query the index
61 | const queryEngine = index.asQueryEngine();
62 |
63 | // Get a list of prompts for the query engine
64 | const prompts = queryEngine.getPrompts();
65 |
66 | // output: { "responseSynthesizer:textQATemplate": defaultTextQaPrompt, "responseSynthesizer:refineTemplate": defaultRefineTemplatePrompt }
67 |
68 | // Now, we can override the default prompt
69 | queryEngine.updatePrompt({
70 | "responseSynthesizer:textQATemplate": newTextQaPrompt,
71 | });
72 |
73 | const response = await queryEngine.query({
74 | query: "What did the author do in college?",
75 | });
76 | ```
77 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/query_engines/_category_.yml:
--------------------------------------------------------------------------------
1 | label: "Query Engines"
2 | position: 2
3 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/query_engines/index.md:
--------------------------------------------------------------------------------
1 | # QueryEngine
2 |
3 | A query engine wraps a `Retriever` and a `ResponseSynthesizer` into a pipeline, that will use the query string to fetech nodes and then send them to the LLM to generate a response.
4 |
5 | ```typescript
6 | const queryEngine = index.asQueryEngine();
7 | const response = await queryEngine.query({ query: "query string" });
8 | ```
9 |
10 | The `query` function also supports streaming, just add `stream: true` as an option:
11 |
12 | ```typescript
13 | const stream = await queryEngine.query({ query: "query string", stream: true });
14 | for await (const chunk of stream) {
15 | process.stdout.write(chunk.response);
16 | }
17 | ```
18 |
19 | ## Sub Question Query Engine
20 |
21 | The basic concept of the Sub Question Query Engine is that it splits a single query into multiple queries, gets an answer for each of those queries, and then combines those different answers into a single coherent response for the user. You can think of it as the "think this through step by step" prompt technique but iterating over your data sources!
22 |
23 | ### Getting Started
24 |
25 | The easiest way to start trying the Sub Question Query Engine is running the subquestion.ts file in [examples](https://github.com/run-llama/LlamaIndexTS/blob/main/examples/subquestion.ts).
26 |
27 | ```bash
28 | npx ts-node subquestion.ts
29 | ```
30 |
31 | ### Tools
32 |
33 | SubQuestionQueryEngine is implemented with Tools. The basic idea of Tools is that they are executable options for the large language model. In this case, our SubQuestionQueryEngine relies on QueryEngineTool, which as you guessed it is a tool to run queries on a QueryEngine. This allows us to give the model an option to query different documents for different questions for example. You could also imagine that the SubQuestionQueryEngine could use a Tool that searches for something on the web or gets an answer using Wolfram Alpha.
34 |
35 | You can learn more about Tools by taking a look at the LlamaIndex Python documentation https://gpt-index.readthedocs.io/en/latest/core_modules/agent_modules/tools/root.html
36 |
37 | ## API Reference
38 |
39 | - [RetrieverQueryEngine](../../api/classes/RetrieverQueryEngine.md)
40 | - [SubQuestionQueryEngine](../../api/classes/SubQuestionQueryEngine.md)
41 | - [QueryEngineTool](../../api/interfaces/QueryEngineTool.md)
42 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/query_engines/metadata_filtering.md:
--------------------------------------------------------------------------------
1 | # Metadata Filtering
2 |
3 | Metadata filtering is a way to filter the documents that are returned by a query based on the metadata associated with the documents. This is useful when you want to filter the documents based on some metadata that is not part of the document text.
4 |
5 | You can also check our multi-tenancy blog post to see how metadata filtering can be used in a multi-tenant environment. [https://blog.llamaindex.ai/building-multi-tenancy-rag-system-with-llamaindex-0d6ab4e0c44b] (the article uses the Python version of LlamaIndex, but the concepts are the same).
6 |
7 | ## Setup
8 |
9 | Firstly if you haven't already, you need to install the `llamaindex` package:
10 |
11 | ```bash
12 | pnpm i llamaindex
13 | ```
14 |
15 | Then you can import the necessary modules from `llamaindex`:
16 |
17 | ```ts
18 | import {
19 | ChromaVectorStore,
20 | Document,
21 | VectorStoreIndex,
22 | storageContextFromDefaults,
23 | } from "llamaindex";
24 |
25 | const collectionName = "dog_colors";
26 | ```
27 |
28 | ## Creating documents with metadata
29 |
30 | You can create documents with metadata using the `Document` class:
31 |
32 | ```ts
33 | const docs = [
34 | new Document({
35 | text: "The dog is brown",
36 | metadata: {
37 | color: "brown",
38 | dogId: "1",
39 | },
40 | }),
41 | new Document({
42 | text: "The dog is red",
43 | metadata: {
44 | color: "red",
45 | dogId: "2",
46 | },
47 | }),
48 | ];
49 | ```
50 |
51 | ## Creating a ChromaDB vector store
52 |
53 | You can create a `ChromaVectorStore` to store the documents:
54 |
55 | ```ts
56 | const chromaVS = new ChromaVectorStore({ collectionName });
57 | const serviceContext = await storageContextFromDefaults({
58 | vectorStore: chromaVS,
59 | });
60 |
61 | const index = await VectorStoreIndex.fromDocuments(docs, {
62 | storageContext: serviceContext,
63 | });
64 | ```
65 |
66 | ## Querying the index with metadata filtering
67 |
68 | Now you can query the index with metadata filtering using the `preFilters` option:
69 |
70 | ```ts
71 | const queryEngine = index.asQueryEngine({
72 | preFilters: {
73 | filters: [
74 | {
75 | key: "dogId",
76 | value: "2",
77 | filterType: "ExactMatch",
78 | },
79 | ],
80 | },
81 | });
82 |
83 | const response = await queryEngine.query({
84 | query: "What is the color of the dog?",
85 | });
86 |
87 | console.log(response.toString());
88 | ```
89 |
90 | ## Full Code
91 |
92 | ```ts
93 | import {
94 | ChromaVectorStore,
95 | Document,
96 | VectorStoreIndex,
97 | storageContextFromDefaults,
98 | } from "llamaindex";
99 |
100 | const collectionName = "dog_colors";
101 |
102 | async function main() {
103 | try {
104 | const docs = [
105 | new Document({
106 | text: "The dog is brown",
107 | metadata: {
108 | color: "brown",
109 | dogId: "1",
110 | },
111 | }),
112 | new Document({
113 | text: "The dog is red",
114 | metadata: {
115 | color: "red",
116 | dogId: "2",
117 | },
118 | }),
119 | ];
120 |
121 | console.log("Creating ChromaDB vector store");
122 | const chromaVS = new ChromaVectorStore({ collectionName });
123 | const ctx = await storageContextFromDefaults({ vectorStore: chromaVS });
124 |
125 | console.log("Embedding documents and adding to index");
126 | const index = await VectorStoreIndex.fromDocuments(docs, {
127 | storageContext: ctx,
128 | });
129 |
130 | console.log("Querying index");
131 | const queryEngine = index.asQueryEngine({
132 | preFilters: {
133 | filters: [
134 | {
135 | key: "dogId",
136 | value: "2",
137 | filterType: "ExactMatch",
138 | },
139 | ],
140 | },
141 | });
142 | const response = await queryEngine.query({
143 | query: "What is the color of the dog?",
144 | });
145 | console.log(response.toString());
146 | } catch (e) {
147 | console.error(e);
148 | }
149 | }
150 |
151 | main();
152 | ```
153 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/query_engines/router_query_engine.md:
--------------------------------------------------------------------------------
1 | # Router Query Engine
2 |
3 | In this tutorial, we define a custom router query engine that selects one out of several candidate query engines to execute a query.
4 |
5 | ## Setup
6 |
7 | First, we need to install import the necessary modules from `llamaindex`:
8 |
9 | ```bash
10 | pnpm i lamaindex
11 | ```
12 |
13 | ```ts
14 | import {
15 | OpenAI,
16 | RouterQueryEngine,
17 | SimpleDirectoryReader,
18 | SimpleNodeParser,
19 | SummaryIndex,
20 | VectorStoreIndex,
21 | serviceContextFromDefaults,
22 | } from "llamaindex";
23 | ```
24 |
25 | ## Loading Data
26 |
27 | Next, we need to load some data. We will use the `SimpleDirectoryReader` to load documents from a directory:
28 |
29 | ```ts
30 | const documents = await new SimpleDirectoryReader().loadData({
31 | directoryPath: "node_modules/llamaindex/examples",
32 | });
33 | ```
34 |
35 | ## Service Context
36 |
37 | Next, we need to define some basic rules and parse the documents into nodes. We will use the `SimpleNodeParser` to parse the documents into nodes and `ServiceContext` to define the rules (eg. LLM API key, chunk size, etc.):
38 |
39 | ```ts
40 | const nodeParser = new SimpleNodeParser({
41 | chunkSize: 1024,
42 | });
43 |
44 | const serviceContext = serviceContextFromDefaults({
45 | nodeParser,
46 | llm: new OpenAI(),
47 | });
48 | ```
49 |
50 | ## Creating Indices
51 |
52 | Next, we need to create some indices. We will create a `VectorStoreIndex` and a `SummaryIndex`:
53 |
54 | ```ts
55 | const vectorIndex = await VectorStoreIndex.fromDocuments(documents, {
56 | serviceContext,
57 | });
58 |
59 | const summaryIndex = await SummaryIndex.fromDocuments(documents, {
60 | serviceContext,
61 | });
62 | ```
63 |
64 | ## Creating Query Engines
65 |
66 | Next, we need to create some query engines. We will create a `VectorStoreQueryEngine` and a `SummaryQueryEngine`:
67 |
68 | ```ts
69 | const vectorQueryEngine = vectorIndex.asQueryEngine();
70 | const summaryQueryEngine = summaryIndex.asQueryEngine();
71 | ```
72 |
73 | ## Creating a Router Query Engine
74 |
75 | Next, we need to create a router query engine. We will use the `RouterQueryEngine` to create a router query engine:
76 |
77 | We're defining two query engines, one for summarization and one for retrieving specific context. The router query engine will select the most appropriate query engine based on the query.
78 |
79 | ```ts
80 | const queryEngine = RouterQueryEngine.fromDefaults({
81 | queryEngineTools: [
82 | {
83 | queryEngine: vectorQueryEngine,
84 | description: "Useful for summarization questions related to Abramov",
85 | },
86 | {
87 | queryEngine: summaryQueryEngine,
88 | description: "Useful for retrieving specific context from Abramov",
89 | },
90 | ],
91 | serviceContext,
92 | });
93 | ```
94 |
95 | ## Querying the Router Query Engine
96 |
97 | Finally, we can query the router query engine:
98 |
99 | ```ts
100 | const summaryResponse = await queryEngine.query({
101 | query: "Give me a summary about his past experiences?",
102 | });
103 |
104 | console.log({
105 | answer: summaryResponse.response,
106 | metadata: summaryResponse?.metadata?.selectorResult,
107 | });
108 | ```
109 |
110 | ## Full code
111 |
112 | ```ts
113 | import {
114 | OpenAI,
115 | RouterQueryEngine,
116 | SimpleDirectoryReader,
117 | SimpleNodeParser,
118 | SummaryIndex,
119 | VectorStoreIndex,
120 | serviceContextFromDefaults,
121 | } from "llamaindex";
122 |
123 | async function main() {
124 | // Load documents from a directory
125 | const documents = await new SimpleDirectoryReader().loadData({
126 | directoryPath: "node_modules/llamaindex/examples",
127 | });
128 |
129 | // Parse the documents into nodes
130 | const nodeParser = new SimpleNodeParser({
131 | chunkSize: 1024,
132 | });
133 |
134 | // Create a service context
135 | const serviceContext = serviceContextFromDefaults({
136 | nodeParser,
137 | llm: new OpenAI(),
138 | });
139 |
140 | // Create indices
141 | const vectorIndex = await VectorStoreIndex.fromDocuments(documents, {
142 | serviceContext,
143 | });
144 |
145 | const summaryIndex = await SummaryIndex.fromDocuments(documents, {
146 | serviceContext,
147 | });
148 |
149 | // Create query engines
150 | const vectorQueryEngine = vectorIndex.asQueryEngine();
151 | const summaryQueryEngine = summaryIndex.asQueryEngine();
152 |
153 | // Create a router query engine
154 | const queryEngine = RouterQueryEngine.fromDefaults({
155 | queryEngineTools: [
156 | {
157 | queryEngine: vectorQueryEngine,
158 | description: "Useful for summarization questions related to Abramov",
159 | },
160 | {
161 | queryEngine: summaryQueryEngine,
162 | description: "Useful for retrieving specific context from Abramov",
163 | },
164 | ],
165 | serviceContext,
166 | });
167 |
168 | // Query the router query engine
169 | const summaryResponse = await queryEngine.query({
170 | query: "Give me a summary about his past experiences?",
171 | });
172 |
173 | console.log({
174 | answer: summaryResponse.response,
175 | metadata: summaryResponse?.metadata?.selectorResult,
176 | });
177 |
178 | const specificResponse = await queryEngine.query({
179 | query: "Tell me about abramov first job?",
180 | });
181 |
182 | console.log({
183 | answer: specificResponse.response,
184 | metadata: specificResponse.metadata.selectorResult,
185 | });
186 | }
187 |
188 | main().then(() => console.log("Done"));
189 | ```
190 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/response_synthesizer.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 6
3 | ---
4 |
5 | # ResponseSynthesizer
6 |
7 | The ResponseSynthesizer is responsible for sending the query, nodes, and prompt templates to the LLM to generate a response. There are a few key modes for generating a response:
8 |
9 | - `Refine`: "create and refine" an answer by sequentially going through each retrieved text chunk.
10 | This makes a separate LLM call per Node. Good for more detailed answers.
11 | - `CompactAndRefine` (default): "compact" the prompt during each LLM call by stuffing as
12 | many text chunks that can fit within the maximum prompt size. If there are
13 | too many chunks to stuff in one prompt, "create and refine" an answer by going through
14 | multiple compact prompts. The same as `refine`, but should result in less LLM calls.
15 | - `TreeSummarize`: Given a set of text chunks and the query, recursively construct a tree
16 | and return the root node as the response. Good for summarization purposes.
17 | - `SimpleResponseBuilder`: Given a set of text chunks and the query, apply the query to each text
18 | chunk while accumulating the responses into an array. Returns a concatenated string of all
19 | responses. Good for when you need to run the same query separately against each text
20 | chunk.
21 |
22 | ```typescript
23 | import { NodeWithScore, ResponseSynthesizer, TextNode } from "llamaindex";
24 |
25 | const responseSynthesizer = new ResponseSynthesizer();
26 |
27 | const nodesWithScore: NodeWithScore[] = [
28 | {
29 | node: new TextNode({ text: "I am 10 years old." }),
30 | score: 1,
31 | },
32 | {
33 | node: new TextNode({ text: "John is 20 years old." }),
34 | score: 0.5,
35 | },
36 | ];
37 |
38 | const response = await responseSynthesizer.synthesize({
39 | query: "What age am I?",
40 | nodesWithScore,
41 | });
42 | console.log(response.response);
43 | ```
44 |
45 | The `synthesize` function also supports streaming, just add `stream: true` as an option:
46 |
47 | ```typescript
48 | const stream = await responseSynthesizer.synthesize({
49 | query: "What age am I?",
50 | nodesWithScore,
51 | stream: true,
52 | });
53 | for await (const chunk of stream) {
54 | process.stdout.write(chunk.response);
55 | }
56 | ```
57 |
58 | ## API Reference
59 |
60 | - [ResponseSynthesizer](../api/classes/ResponseSynthesizer.md)
61 | - [Refine](../api/classes/Refine.md)
62 | - [CompactAndRefine](../api/classes/CompactAndRefine.md)
63 | - [TreeSummarize](../api/classes/TreeSummarize.md)
64 | - [SimpleResponseBuilder](../api/classes/SimpleResponseBuilder.md)
65 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/retriever.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 5
3 | ---
4 |
5 | # Retriever
6 |
7 | A retriever in LlamaIndex is what is used to fetch `Node`s from an index using a query string. Aa `VectorIndexRetriever` will fetch the top-k most similar nodes. Meanwhile, a `SummaryIndexRetriever` will fetch all nodes no matter the query.
8 |
9 | ```typescript
10 | const retriever = vector_index.asRetriever();
11 | retriever.similarityTopK = 3;
12 |
13 | // Fetch nodes!
14 | const nodesWithScore = await retriever.retrieve("query string");
15 | ```
16 |
17 | ## API Reference
18 |
19 | - [SummaryIndexRetriever](../api/classes/SummaryIndexRetriever.md)
20 | - [SummaryIndexLLMRetriever](../api/classes/SummaryIndexLLMRetriever.md)
21 | - [VectorIndexRetriever](../api/classes/VectorIndexRetriever.md)
22 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/storage.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 7
3 | ---
4 |
5 | # Storage
6 |
7 | Storage in LlamaIndex.TS works automatically once you've configured a `StorageContext` object. Just configure the `persistDir` and attach it to an index.
8 |
9 | Right now, only saving and loading from disk is supported, with future integrations planned!
10 |
11 | ```typescript
12 | import { Document, VectorStoreIndex, storageContextFromDefaults } from "./src";
13 |
14 | const storageContext = await storageContextFromDefaults({
15 | persistDir: "./storage",
16 | });
17 |
18 | const document = new Document({ text: "Test Text" });
19 | const index = await VectorStoreIndex.fromDocuments([document], {
20 | storageContext,
21 | });
22 | ```
23 |
24 | ## API Reference
25 |
26 | - [StorageContext](../api/interfaces//StorageContext.md)
27 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/vector_stores/_category_.yml:
--------------------------------------------------------------------------------
1 | label: "Vector Stores"
2 | position: 1
3 |
--------------------------------------------------------------------------------
/backend/data/docs/modules/vector_stores/qdrant.md:
--------------------------------------------------------------------------------
1 | # Qdrant Vector Store
2 |
3 | To run this example, you need to have a Qdrant instance running. You can run it with Docker:
4 |
5 | ```bash
6 | docker pull qdrant/qdrant
7 | docker run -p 6333:6333 qdrant/qdrant
8 | ```
9 |
10 | ## Importing the modules
11 |
12 | ```ts
13 | import fs from "node:fs/promises";
14 | import { Document, VectorStoreIndex, QdrantVectorStore } from "llamaindex";
15 | ```
16 |
17 | ## Load the documents
18 |
19 | ```ts
20 | const path = "node_modules/llamaindex/examples/abramov.txt";
21 | const essay = await fs.readFile(path, "utf-8");
22 | ```
23 |
24 | ## Setup Qdrant
25 |
26 | ```ts
27 | const vectorStore = new QdrantVectorStore({
28 | url: "http://localhost:6333",
29 | });
30 | ```
31 |
32 | ## Setup the index
33 |
34 | ```ts
35 | const document = new Document({ text: essay, id_: path });
36 |
37 | const index = await VectorStoreIndex.fromDocuments([document], {
38 | vectorStore,
39 | });
40 | ```
41 |
42 | ## Query the index
43 |
44 | ```ts
45 | const queryEngine = index.asQueryEngine();
46 |
47 | const response = await queryEngine.query({
48 | query: "What did the author do in college?",
49 | });
50 |
51 | // Output response
52 | console.log(response.toString());
53 | ```
54 |
55 | ## Full code
56 |
57 | ```ts
58 | import fs from "node:fs/promises";
59 | import { Document, VectorStoreIndex, QdrantVectorStore } from "llamaindex";
60 |
61 | async function main() {
62 | const path = "node_modules/llamaindex/examples/abramov.txt";
63 | const essay = await fs.readFile(path, "utf-8");
64 |
65 | const vectorStore = new QdrantVectorStore({
66 | url: "http://localhost:6333",
67 | });
68 |
69 | const document = new Document({ text: essay, id_: path });
70 |
71 | const index = await VectorStoreIndex.fromDocuments([document], {
72 | vectorStore,
73 | });
74 |
75 | const queryEngine = index.asQueryEngine();
76 |
77 | const response = await queryEngine.query({
78 | query: "What did the author do in college?",
79 | });
80 |
81 | // Output response
82 | console.log(response.toString());
83 | }
84 |
85 | main().catch(console.error);
86 | ```
87 |
--------------------------------------------------------------------------------
/backend/data/docs/observability/_category_.yml:
--------------------------------------------------------------------------------
1 | label: Observability
2 | position: 5
3 |
--------------------------------------------------------------------------------
/backend/data/docs/observability/index.md:
--------------------------------------------------------------------------------
1 | # Observability
2 |
3 | LlamaIndex provides **one-click observability** 🔭 to allow you to build principled LLM applications in a production setting.
4 |
5 | A key requirement for principled development of LLM applications over your data (RAG systems, agents) is being able to observe, debug, and evaluate
6 | your system - both as a whole and for each component.
7 |
8 | This feature allows you to seamlessly integrate the LlamaIndex library with powerful observability/evaluation tools offered by our partners.
9 | Configure a variable once, and you'll be able to do things like the following:
10 |
11 | - View LLM/prompt inputs/outputs
12 | - Ensure that the outputs of any component (LLMs, embeddings) are performing as expected
13 | - View call traces for both indexing and querying
14 |
15 | Each provider has similarities and differences. Take a look below for the full set of guides for each one!
16 |
17 | ## OpenLLMetry
18 |
19 | [OpenLLMetry](https://github.com/traceloop/openllmetry-js) is an open-source project based on OpenTelemetry for tracing and monitoring
20 | LLM applications. It connects to [all major observability platforms](https://www.traceloop.com/docs/openllmetry/integrations/introduction) and installs in minutes.
21 |
22 | ### Usage Pattern
23 |
24 | ```bash
25 | npm install @traceloop/node-server-sdk
26 | ```
27 |
28 | ```js
29 | import * as traceloop from "@traceloop/node-server-sdk";
30 |
31 | traceloop.initialize({
32 | apiKey: process.env.TRACELOOP_API_KEY,
33 | disableBatch: true,
34 | });
35 | ```
36 |
--------------------------------------------------------------------------------
/backend/main.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import os
3 | import uvicorn
4 | from app.api.routers.chat import chat_router
5 | from fastapi import FastAPI
6 | from fastapi.middleware.cors import CORSMiddleware
7 | from dotenv import load_dotenv
8 |
9 | load_dotenv()
10 |
11 | app = FastAPI()
12 |
13 | environment = os.getenv("ENVIRONMENT", "dev") # Default to 'development' if not set
14 |
15 |
16 | if environment == "dev":
17 | logger = logging.getLogger("uvicorn")
18 | logger.warning("Running in development mode - allowing CORS for all origins")
19 | app.add_middleware(
20 | CORSMiddleware,
21 | allow_origins=["*"],
22 | allow_credentials=True,
23 | allow_methods=["*"],
24 | allow_headers=["*"],
25 | )
26 |
27 | app.include_router(chat_router, prefix="/api/chat")
28 |
29 |
30 | if __name__ == "__main__":
31 | uvicorn.run(app="main:app", host="0.0.0.0", reload=True)
32 |
--------------------------------------------------------------------------------
/backend/pyproject.toml:
--------------------------------------------------------------------------------
1 | [tool.poetry]
2 | name = "app"
3 | version = "0.1.0"
4 | description = ""
5 | authors = ["Marcus Schiesser "]
6 | readme = "README.md"
7 |
8 | [tool.poetry.dependencies]
9 | python = "^3.11,<3.12"
10 | fastapi = "^0.104.1"
11 | uvicorn = { extras = ["standard"], version = "^0.23.2" }
12 | python-dotenv = "^1.0.0"
13 | unstructured = "0.10.30"
14 | Jinja2 = "3.1.2"
15 | llama-index-core = "^0.10.18.post1"
16 | llama-index-embeddings-openai = "^0.1.6"
17 | llama-index-llms-openai = "^0.1.7"
18 | llama-index-readers-file = "^0.1.8"
19 | llama-index-agent-openai = "^0.1.5"
20 |
21 |
22 | [build-system]
23 | requires = ["poetry-core"]
24 | build-backend = "poetry.core.masonry.api"
25 |
--------------------------------------------------------------------------------
/backend/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rsrohan99/llamaindex-docs-agent/1b2a6f5efee1bd5a4b9cd7771eac5f76a57155b1/backend/tests/__init__.py
--------------------------------------------------------------------------------
/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | # local env files
28 | .env*.local
29 |
30 | # vercel
31 | .vercel
32 |
33 | # typescript
34 | *.tsbuildinfo
35 | next-env.d.ts
36 |
--------------------------------------------------------------------------------
/frontend/README.md:
--------------------------------------------------------------------------------
1 | This is a [LlamaIndex](https://www.llamaindex.ai/) project using [Next.js](https://nextjs.org/) bootstrapped with [`create-llama`](https://github.com/run-llama/LlamaIndexTS/tree/main/packages/create-llama).
2 |
3 | ## Getting Started
4 |
5 | First, install the dependencies:
6 |
7 | ```
8 | npm install
9 | ```
10 |
11 | Second, run the development server:
12 |
13 | ```
14 | npm run dev
15 | ```
16 |
17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
18 |
19 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
20 |
21 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
22 |
23 | ## Learn More
24 |
25 | To learn more about LlamaIndex, take a look at the following resources:
26 |
27 | - [LlamaIndex Documentation](https://docs.llamaindex.ai) - learn about LlamaIndex (Python features).
28 | - [LlamaIndexTS Documentation](https://ts.llamaindex.ai) - learn about LlamaIndex (Typescript features).
29 |
30 | You can check out [the LlamaIndexTS GitHub repository](https://github.com/run-llama/LlamaIndexTS) - your feedback and contributions are welcome!
31 |
--------------------------------------------------------------------------------
/frontend/app/components/chat-section.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { Message, useChat } from "ai/react";
4 | import { ChatInput, ChatMessages } from "./ui/chat";
5 | import { useMemo } from "react";
6 | import { transformMessages } from "./transform";
7 | import useNodes from "../hooks/useNodes";
8 | import { NodePreview } from "./ui/nodes";
9 |
10 | export default function ChatSection() {
11 | const {
12 | messages,
13 | input,
14 | isLoading,
15 | handleSubmit,
16 | handleInputChange,
17 | reload,
18 | stop,
19 | } = useChat({
20 | api: process.env.NEXT_PUBLIC_CHAT_API,
21 | });
22 |
23 | const { nodes, setNodes } = useNodes();
24 |
25 | const mergeFunctionMessages = (messages: Message[]): Message[] => {
26 | // only allow the last function message to be shown
27 | return messages.filter(
28 | (msg, i) => msg.role !== "function" || i === messages.length - 1
29 | );
30 | };
31 |
32 | const transformedMessages = useMemo(() => {
33 | // return mergeFunctionMessages(transformMessages(messages));
34 | return transformMessages(messages, setNodes);
35 | }, [messages]);
36 |
37 | return (
38 |
39 |
40 |
41 |
47 |
53 |
54 | {nodes.length === 0 ? (
55 | <>>
56 | ) : (
57 |
58 |
59 |
60 | )}
61 |
62 |
63 | );
64 | }
65 |
--------------------------------------------------------------------------------
/frontend/app/components/header.tsx:
--------------------------------------------------------------------------------
1 | import Image from "next/image";
2 |
3 | export default function Header() {
4 | return (
5 |
6 |
7 | Multi-Document Agent based Bot
8 |
9 |
10 | Ask questions over LlamaIndex.TS documentation!
11 |
12 |
28 |
29 | );
30 | }
31 |
--------------------------------------------------------------------------------
/frontend/app/components/transform.ts:
--------------------------------------------------------------------------------
1 | import { nanoid } from "ai";
2 | import { Message } from "ai/react";
3 | import { Node } from "./ui/chat/chat.interface";
4 |
5 | const parseMessageFromToken = (
6 | tokenString: string,
7 | setNodes: (nodes: Node[]) => void
8 | ): Message => {
9 | try {
10 | const token = JSON.parse(tokenString);
11 | // console.log(token.type);
12 | if (typeof token === "string") {
13 | return {
14 | id: nanoid(),
15 | role: "assistant",
16 | content: token,
17 | };
18 | }
19 |
20 | const payload = token.payload;
21 | if (token.type === "function_call") {
22 | return {
23 | id: nanoid(),
24 | role: "function",
25 | function_call: {
26 | name: payload.tool_str,
27 | arguments: payload.arguments_str,
28 | },
29 | content: `Used intermediate tool: ${payload.tool_str} with args: ${payload.arguments_str}`,
30 | };
31 | }
32 |
33 | // if (token.type === "function_call_response") {
34 | // // return;
35 | // return {
36 | // id: nanoid(),
37 | // role: "function",
38 | // content: `Got output: ${payload.response}`,
39 | // };
40 | // }
41 |
42 | if (token.type === "nodes_retrieved") {
43 | const nodes = payload.nodes as Node[];
44 | if (nodes.length !== 0) {
45 | setNodes(nodes);
46 | // console.log(payload.nodes);
47 | // console.log("here");
48 | }
49 | return {
50 | id: nanoid(),
51 | role: "assistant",
52 | content: "",
53 | };
54 | }
55 |
56 | return {
57 | id: nanoid(),
58 | role: "assistant",
59 | content: tokenString,
60 | };
61 | } catch (e) {
62 | console.log(e);
63 | return {
64 | id: nanoid(),
65 | role: "assistant",
66 | content: tokenString,
67 | };
68 | }
69 | };
70 |
71 | const mergeLastAssistantMessages = (messages: Message[]): Message[] => {
72 | const lastMessage = messages[messages.length - 1];
73 | if (lastMessage?.role !== "assistant") return messages;
74 |
75 | let mergedContent = "";
76 | let i = messages.length - 1;
77 |
78 | // merge content of last assistant messages
79 | for (; i >= 0; i--) {
80 | if (messages[i].role !== "assistant") {
81 | break;
82 | }
83 | mergedContent = messages[i].content + mergedContent;
84 | }
85 |
86 | return [
87 | ...messages.slice(0, i + 1),
88 | {
89 | id: nanoid(),
90 | role: "assistant",
91 | content: mergedContent,
92 | },
93 | ];
94 | };
95 |
96 | const extractDataTokens = (messageContent: string): string[] => {
97 | const regex = /data: (.+?)\n+/g;
98 | const matches = [];
99 | let match;
100 | while ((match = regex.exec(messageContent)) !== null) {
101 | matches.push(match[1]);
102 | }
103 | return matches;
104 | };
105 |
106 | const transformMessage = (
107 | message: Message,
108 | setNodes: (nodes: Node[]) => void
109 | ): Message[] => {
110 | if (message.role !== "assistant") {
111 | // If the message is not from the assistant, return it as is
112 | return [message];
113 | }
114 | // Split the message content into an array of data tokens
115 | const dataTokens = extractDataTokens(message.content);
116 |
117 | // Extract messages from data tokens
118 | const messages = dataTokens.map((dataToken) =>
119 | parseMessageFromToken(dataToken, setNodes)
120 | );
121 |
122 | // Merge last assistant messages to one
123 | return mergeLastAssistantMessages(messages);
124 | };
125 |
126 | export const transformMessages = (
127 | messages: Message[],
128 | setNodes: (nodes: Node[]) => void
129 | ) => {
130 | return messages.flatMap((message) => transformMessage(message, setNodes));
131 | };
132 |
--------------------------------------------------------------------------------
/frontend/app/components/ui/README.md:
--------------------------------------------------------------------------------
1 | Using the chat component from https://github.com/marcusschiesser/ui (based on https://ui.shadcn.com/)
2 |
--------------------------------------------------------------------------------
/frontend/app/components/ui/button.tsx:
--------------------------------------------------------------------------------
1 | import { Slot } from "@radix-ui/react-slot";
2 | import { cva, type VariantProps } from "class-variance-authority";
3 | import * as React from "react";
4 |
5 | import { cn } from "./lib/utils";
6 |
7 | const buttonVariants = cva(
8 | "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
9 | {
10 | variants: {
11 | variant: {
12 | default: "bg-primary text-primary-foreground hover:bg-primary/90",
13 | destructive:
14 | "bg-destructive text-destructive-foreground hover:bg-destructive/90",
15 | outline:
16 | "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
17 | secondary:
18 | "bg-secondary text-secondary-foreground hover:bg-secondary/80",
19 | ghost: "hover:bg-accent hover:text-accent-foreground",
20 | link: "text-primary underline-offset-4 hover:underline",
21 | },
22 | size: {
23 | default: "h-10 px-4 py-2",
24 | sm: "h-9 rounded-md px-3",
25 | lg: "h-11 rounded-md px-8",
26 | icon: "h-10 w-10",
27 | },
28 | },
29 | defaultVariants: {
30 | variant: "default",
31 | size: "default",
32 | },
33 | },
34 | );
35 |
36 | export interface ButtonProps
37 | extends React.ButtonHTMLAttributes,
38 | VariantProps {
39 | asChild?: boolean;
40 | }
41 |
42 | const Button = React.forwardRef(
43 | ({ className, variant, size, asChild = false, ...props }, ref) => {
44 | const Comp = asChild ? Slot : "button";
45 | return (
46 |
51 | );
52 | },
53 | );
54 | Button.displayName = "Button";
55 |
56 | export { Button, buttonVariants };
57 |
--------------------------------------------------------------------------------
/frontend/app/components/ui/chat/chat-actions.tsx:
--------------------------------------------------------------------------------
1 | import { PauseCircle, RefreshCw } from "lucide-react";
2 |
3 | import { Button } from "../button";
4 | import { ChatHandler } from "./chat.interface";
5 |
6 | export default function ChatActions(
7 | props: Pick & {
8 | showReload?: boolean;
9 | showStop?: boolean;
10 | },
11 | ) {
12 | return (
13 |
14 | {props.showStop && (
15 |
19 | )}
20 | {props.showReload && (
21 |
25 | )}
26 |
27 | );
28 | }
29 |
--------------------------------------------------------------------------------
/frontend/app/components/ui/chat/chat-avatar.tsx:
--------------------------------------------------------------------------------
1 | import { Shell, User2 } from "lucide-react";
2 | import Image from "next/image";
3 |
4 | export default function ChatAvatar({ role }: { role: string }) {
5 | if (role === "user") {
6 | return (
7 |
8 |
9 |
10 | );
11 | }
12 |
13 | if (role === "function") {
14 | return (
15 |
16 |
17 |
18 | );
19 | }
20 |
21 | return (
22 |
23 |
31 |
32 | );
33 | }
34 |
--------------------------------------------------------------------------------
/frontend/app/components/ui/chat/chat-input.tsx:
--------------------------------------------------------------------------------
1 | import { Button } from "../button";
2 | import { Input } from "../input";
3 | import { ChatHandler } from "./chat.interface";
4 |
5 | export default function ChatInput(
6 | props: Pick<
7 | ChatHandler,
8 | "isLoading" | "handleSubmit" | "handleInputChange" | "input"
9 | >,
10 | ) {
11 | return (
12 |
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/frontend/app/components/ui/chat/chat-message.tsx:
--------------------------------------------------------------------------------
1 | import { Check, Copy } from "lucide-react";
2 |
3 | import { Button } from "../button";
4 | import ChatAvatar from "./chat-avatar";
5 | import { Message } from "./chat.interface";
6 | import Markdown from "./markdown";
7 | import { useCopyToClipboard } from "./use-copy-to-clipboard";
8 | import { cn } from "../lib/utils";
9 |
10 | export default function ChatMessage(chatMessage: Message) {
11 | const { isCopied, copyToClipboard } = useCopyToClipboard({ timeout: 2000 });
12 | return (
13 |
14 |
15 |
16 |
22 |
23 |
24 |
36 |
37 |
38 | );
39 | }
40 |
--------------------------------------------------------------------------------
/frontend/app/components/ui/chat/chat-messages.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useRef } from "react";
2 |
3 | import ChatActions from "./chat-actions";
4 | import ChatMessage from "./chat-message";
5 | import { ChatHandler } from "./chat.interface";
6 | import { Loader2 } from "lucide-react";
7 | import { NodePreview } from "../nodes";
8 |
9 | export default function ChatMessages(
10 | props: Pick
11 | ) {
12 | const scrollableChatContainerRef = useRef(null);
13 | const messageLength = props.messages.length;
14 | const lastMessage = props.messages[messageLength - 1];
15 |
16 | const scrollToBottom = () => {
17 | if (scrollableChatContainerRef.current) {
18 | scrollableChatContainerRef.current.scrollTop =
19 | scrollableChatContainerRef.current.scrollHeight;
20 | }
21 | };
22 |
23 | const isLastMessageFromAssistant =
24 | messageLength > 0 && lastMessage?.role !== "user";
25 | const showReload =
26 | props.reload && !props.isLoading && isLastMessageFromAssistant;
27 | const showStop = props.stop && props.isLoading;
28 |
29 | // `isPending` indicate
30 | // that stream response is not yet received from the server,
31 | // so we show a loading indicator to give a better UX.
32 | const isPending = props.isLoading && !isLastMessageFromAssistant;
33 |
34 | useEffect(() => {
35 | scrollToBottom();
36 | }, [messageLength, lastMessage]);
37 |
38 | return (
39 |
40 |
44 | {props.messages.map((m) => {
45 | if (m.content) {
46 | return
;
47 | }
48 | })}
49 | {isPending && (
50 |
51 |
52 |
53 | )}
54 |
55 |
56 |
62 |
63 |
64 | );
65 | }
66 |
--------------------------------------------------------------------------------
/frontend/app/components/ui/chat/chat.interface.ts:
--------------------------------------------------------------------------------
1 | export interface Message {
2 | id: string;
3 | content: string;
4 | role: string;
5 | }
6 |
7 | export interface ChatHandler {
8 | messages: Message[];
9 | input: string;
10 | isLoading: boolean;
11 | handleSubmit: (e: React.FormEvent) => void;
12 | handleInputChange: (e: React.ChangeEvent) => void;
13 | reload?: () => void;
14 | stop?: () => void;
15 | }
16 |
17 | export interface Node {
18 | id: string;
19 | title: string;
20 | url: string;
21 | section: string;
22 | summary: string;
23 | }
24 |
--------------------------------------------------------------------------------
/frontend/app/components/ui/chat/codeblock.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import React, { FC, memo } from "react"
4 | import { Check, Copy, Download } from "lucide-react"
5 | import { Prism, SyntaxHighlighterProps } from "react-syntax-highlighter"
6 | import { coldarkDark } from "react-syntax-highlighter/dist/cjs/styles/prism"
7 |
8 | import { Button } from "../button"
9 | import { useCopyToClipboard } from "./use-copy-to-clipboard"
10 |
11 | // TODO: Remove this when @type/react-syntax-highlighter is updated
12 | const SyntaxHighlighter = Prism as unknown as FC
13 |
14 | interface Props {
15 | language: string
16 | value: string
17 | }
18 |
19 | interface languageMap {
20 | [key: string]: string | undefined
21 | }
22 |
23 | export const programmingLanguages: languageMap = {
24 | javascript: ".js",
25 | python: ".py",
26 | java: ".java",
27 | c: ".c",
28 | cpp: ".cpp",
29 | "c++": ".cpp",
30 | "c#": ".cs",
31 | ruby: ".rb",
32 | php: ".php",
33 | swift: ".swift",
34 | "objective-c": ".m",
35 | kotlin: ".kt",
36 | typescript: ".ts",
37 | go: ".go",
38 | perl: ".pl",
39 | rust: ".rs",
40 | scala: ".scala",
41 | haskell: ".hs",
42 | lua: ".lua",
43 | shell: ".sh",
44 | sql: ".sql",
45 | html: ".html",
46 | css: ".css",
47 | // add more file extensions here, make sure the key is same as language prop in CodeBlock.tsx component
48 | }
49 |
50 | export const generateRandomString = (length: number, lowercase = false) => {
51 | const chars = "ABCDEFGHJKLMNPQRSTUVWXY3456789" // excluding similar looking characters like Z, 2, I, 1, O, 0
52 | let result = ""
53 | for (let i = 0; i < length; i++) {
54 | result += chars.charAt(Math.floor(Math.random() * chars.length))
55 | }
56 | return lowercase ? result.toLowerCase() : result
57 | }
58 |
59 | const CodeBlock: FC = memo(({ language, value }) => {
60 | const { isCopied, copyToClipboard } = useCopyToClipboard({ timeout: 2000 })
61 |
62 | const downloadAsFile = () => {
63 | if (typeof window === "undefined") {
64 | return
65 | }
66 | const fileExtension = programmingLanguages[language] || ".file"
67 | const suggestedFileName = `file-${generateRandomString(
68 | 3,
69 | true
70 | )}${fileExtension}`
71 | const fileName = window.prompt("Enter file name" || "", suggestedFileName)
72 |
73 | if (!fileName) {
74 | // User pressed cancel on prompt.
75 | return
76 | }
77 |
78 | const blob = new Blob([value], { type: "text/plain" })
79 | const url = URL.createObjectURL(blob)
80 | const link = document.createElement("a")
81 | link.download = fileName
82 | link.href = url
83 | link.style.display = "none"
84 | document.body.appendChild(link)
85 | link.click()
86 | document.body.removeChild(link)
87 | URL.revokeObjectURL(url)
88 | }
89 |
90 | const onCopy = () => {
91 | if (isCopied) return
92 | copyToClipboard(value)
93 | }
94 |
95 | return (
96 |
97 |
98 |
{language}
99 |
100 |
104 |
112 |
113 |
114 |
132 | {value}
133 |
134 |
135 | )
136 | })
137 | CodeBlock.displayName = "CodeBlock"
138 |
139 | export { CodeBlock }
140 |
--------------------------------------------------------------------------------
/frontend/app/components/ui/chat/index.ts:
--------------------------------------------------------------------------------
1 | import ChatInput from "./chat-input";
2 | import ChatMessages from "./chat-messages";
3 |
4 | export { type ChatHandler, type Message } from "./chat.interface";
5 | export { ChatMessages, ChatInput };
6 |
--------------------------------------------------------------------------------
/frontend/app/components/ui/chat/markdown.tsx:
--------------------------------------------------------------------------------
1 | import { FC, memo } from "react"
2 | import ReactMarkdown, { Options } from "react-markdown"
3 | import remarkGfm from "remark-gfm"
4 | import remarkMath from "remark-math"
5 |
6 | import { CodeBlock } from "./codeblock"
7 |
8 | const MemoizedReactMarkdown: FC = memo(
9 | ReactMarkdown,
10 | (prevProps, nextProps) =>
11 | prevProps.children === nextProps.children &&
12 | prevProps.className === nextProps.className
13 | )
14 |
15 | export default function Markdown({ content }: { content: string }) {
16 | return (
17 | {children}
23 | },
24 | code({ node, inline, className, children, ...props }) {
25 | if (children.length) {
26 | if (children[0] == "▍") {
27 | return (
28 | ▍
29 | )
30 | }
31 |
32 | children[0] = (children[0] as string).replace("`▍`", "▍")
33 | }
34 |
35 | const match = /language-(\w+)/.exec(className || "")
36 |
37 | if (inline) {
38 | return (
39 |
40 | {children}
41 |
42 | )
43 | }
44 |
45 | return (
46 |
52 | )
53 | },
54 | }}
55 | >
56 | {content}
57 |
58 | )
59 | }
60 |
--------------------------------------------------------------------------------
/frontend/app/components/ui/chat/use-copy-to-clipboard.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import * as React from 'react'
4 |
5 | export interface useCopyToClipboardProps {
6 | timeout?: number
7 | }
8 |
9 | export function useCopyToClipboard({
10 | timeout = 2000
11 | }: useCopyToClipboardProps) {
12 | const [isCopied, setIsCopied] = React.useState(false)
13 |
14 | const copyToClipboard = (value: string) => {
15 | if (typeof window === 'undefined' || !navigator.clipboard?.writeText) {
16 | return
17 | }
18 |
19 | if (!value) {
20 | return
21 | }
22 |
23 | navigator.clipboard.writeText(value).then(() => {
24 | setIsCopied(true)
25 |
26 | setTimeout(() => {
27 | setIsCopied(false)
28 | }, timeout)
29 | })
30 | }
31 |
32 | return { isCopied, copyToClipboard }
33 | }
34 |
--------------------------------------------------------------------------------
/frontend/app/components/ui/input.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 |
3 | import { cn } from "./lib/utils";
4 |
5 | export interface InputProps
6 | extends React.InputHTMLAttributes {}
7 |
8 | const Input = React.forwardRef(
9 | ({ className, type, ...props }, ref) => {
10 | return (
11 |
20 | );
21 | },
22 | );
23 | Input.displayName = "Input";
24 |
25 | export { Input };
26 |
--------------------------------------------------------------------------------
/frontend/app/components/ui/lib/utils.ts:
--------------------------------------------------------------------------------
1 | import { clsx, type ClassValue } from "clsx";
2 | import { twMerge } from "tailwind-merge";
3 |
4 | export function cn(...inputs: ClassValue[]) {
5 | return twMerge(clsx(inputs));
6 | }
--------------------------------------------------------------------------------
/frontend/app/components/ui/nodes.tsx:
--------------------------------------------------------------------------------
1 | import { Node } from "./chat/chat.interface";
2 | import { useEffect, useState } from "react";
3 |
4 | interface NodeBoxProps {
5 | node: Node;
6 | onClick: () => void;
7 | isSelected: boolean;
8 | }
9 |
10 | const NodeBox: React.FC = ({ node, onClick, isSelected }) => {
11 | return (
12 |
18 |
{node.title}
19 |
{node.summary}
20 |
21 | );
22 | };
23 |
24 | interface NodeListProps {
25 | nodes: Node[];
26 | selectedNodeId: string;
27 | onNodeClick: (nodeId: string) => void;
28 | }
29 |
30 | const NodeList: React.FC = ({
31 | nodes,
32 | onNodeClick,
33 | selectedNodeId,
34 | }) => {
35 | if (nodes.length === 0) return;
36 | // console.log(nodes);
37 | // const [selectedNodeId, setSelectedNodeId] = useState(nodes[0].id || "");
38 |
39 | const handleNodeClick = (nodeId: string) => {
40 | // setSelectedNodeId(nodeId);
41 | onNodeClick(nodeId);
42 | };
43 |
44 | return (
45 |
46 | {nodes.map((node) => (
47 | handleNodeClick(node.id)}
51 | isSelected={selectedNodeId === node.id}
52 | />
53 | ))}
54 |
55 | );
56 | };
57 |
58 | interface NodeDetailsProps {
59 | url: string;
60 | }
61 |
62 | const NodeDetails: React.FC = ({ url }) => {
63 | if (!url) return;
64 | // console.log(url);
65 | return (
66 |
67 |
71 |
72 | );
73 | };
74 |
75 | interface NodePreviewProps {
76 | nodes: Node[];
77 | }
78 |
79 | export const NodePreview: React.FC = ({ nodes }) => {
80 | // console.log(nodes);
81 | if (nodes.length === 0) return;
82 | const nodeToURL = (node: Node) => {
83 | const node_url = node.url.endsWith("/index")
84 | ? node.url.slice(0, -6)
85 | : node.url;
86 | return `https://ts.llamaindex.ai/${node_url}#${node.section}`;
87 | };
88 | const [selectedNodeUrl, setSelectedNodeUrl] = useState("");
89 | const [selectedNodeId, setSelectedNodeId] = useState("");
90 |
91 | useEffect(() => {
92 | // console.log("nodes updated");
93 | const firstNodeUrl = nodeToURL(nodes[0]);
94 | setSelectedNodeUrl(firstNodeUrl);
95 | setSelectedNodeId(nodes[0].id);
96 | // console.log(nodes);
97 | }, [nodes]);
98 |
99 | const handleNodeClick = (nodeId: string) => {
100 | const selectedNode = nodes.find((node) => node.id === nodeId);
101 | if (selectedNode) {
102 | setSelectedNodeUrl(nodeToURL(selectedNode));
103 | setSelectedNodeId(nodeId);
104 | }
105 | };
106 |
107 | return (
108 |
109 |
114 |
115 |
116 | );
117 | };
118 |
--------------------------------------------------------------------------------
/frontend/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rsrohan99/llamaindex-docs-agent/1b2a6f5efee1bd5a4b9cd7771eac5f76a57155b1/frontend/app/favicon.ico
--------------------------------------------------------------------------------
/frontend/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @layer base {
6 | :root {
7 | --background: 0 0% 100%;
8 | --foreground: 222.2 47.4% 11.2%;
9 |
10 | --muted: 210 40% 96.1%;
11 | --muted-foreground: 215.4 16.3% 46.9%;
12 |
13 | --popover: 0 0% 100%;
14 | --popover-foreground: 222.2 47.4% 11.2%;
15 |
16 | --border: 214.3 31.8% 91.4%;
17 | --input: 214.3 31.8% 91.4%;
18 |
19 | --card: 0 0% 100%;
20 | --card-foreground: 222.2 47.4% 11.2%;
21 |
22 | --primary: 222.2 47.4% 11.2%;
23 | --primary-foreground: 210 40% 98%;
24 |
25 | --secondary: 210 40% 96.1%;
26 | --secondary-foreground: 222.2 47.4% 11.2%;
27 |
28 | --accent: 210 40% 96.1%;
29 | --accent-foreground: 222.2 47.4% 11.2%;
30 |
31 | --destructive: 0 100% 50%;
32 | --destructive-foreground: 210 40% 98%;
33 |
34 | --ring: 215 20.2% 65.1%;
35 |
36 | --radius: 0.5rem;
37 | }
38 |
39 | .dark {
40 | --background: 224 71% 4%;
41 | --foreground: 213 31% 91%;
42 |
43 | --muted: 223 47% 11%;
44 | --muted-foreground: 215.4 16.3% 56.9%;
45 |
46 | --accent: 216 34% 17%;
47 | --accent-foreground: 210 40% 98%;
48 |
49 | --popover: 224 71% 4%;
50 | --popover-foreground: 215 20.2% 65.1%;
51 |
52 | --border: 216 34% 17%;
53 | --input: 216 34% 17%;
54 |
55 | --card: 224 71% 4%;
56 | --card-foreground: 213 31% 91%;
57 |
58 | --primary: 210 40% 98%;
59 | --primary-foreground: 222.2 47.4% 1.2%;
60 |
61 | --secondary: 222.2 47.4% 11.2%;
62 | --secondary-foreground: 210 40% 98%;
63 |
64 | --destructive: 0 63% 31%;
65 | --destructive-foreground: 210 40% 98%;
66 |
67 | --ring: 216 34% 17%;
68 |
69 | --radius: 0.5rem;
70 | }
71 | }
72 |
73 | @layer base {
74 | * {
75 | @apply border-border;
76 | }
77 | body {
78 | @apply bg-background text-foreground;
79 | font-feature-settings:
80 | "rlig" 1,
81 | "calt" 1;
82 | }
83 | .background-gradient {
84 | background-color: #fff;
85 | background-image: radial-gradient(
86 | at 21% 11%,
87 | rgba(186, 186, 233, 0.53) 0,
88 | transparent 50%
89 | ),
90 | radial-gradient(at 85% 0, hsla(46, 30%, 78%, 0.52) 0, transparent 50%),
91 | radial-gradient(at 91% 36%, rgba(120, 213, 255, 0.68) 0, transparent 50%),
92 | radial-gradient(at 8% 40%, rgba(130, 218, 239, 0.46) 0, transparent 50%);
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/frontend/app/hooks/useNodes.ts:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import { Node } from "../components/ui/chat/chat.interface";
3 |
4 | export default function useNodes() {
5 | const [nodes, setNodes] = useState([]);
6 |
7 | return { nodes, setNodes };
8 | }
9 |
--------------------------------------------------------------------------------
/frontend/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { Metadata } from "next";
2 | import { Inter } from "next/font/google";
3 | import "./globals.css";
4 |
5 | const inter = Inter({ subsets: ["latin"] });
6 |
7 | export const metadata: Metadata = {
8 | title: "Create Llama App",
9 | description: "Generated by create-llama",
10 | };
11 |
12 | export default function RootLayout({
13 | children,
14 | }: {
15 | children: React.ReactNode;
16 | }) {
17 | return (
18 |
19 | {children}
20 |
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/frontend/app/page.tsx:
--------------------------------------------------------------------------------
1 | import Header from "@/app/components/header";
2 | import ChatSection from "./components/chat-section";
3 |
4 | export default function Home() {
5 | return (
6 |
7 |
8 |
9 |
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/frontend/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | experimental: {
4 | serverComponentsExternalPackages: ["llamaindex"],
5 | },
6 | }
7 |
8 | module.exports = nextConfig
9 |
--------------------------------------------------------------------------------
/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "embedded-tables",
3 | "version": "0.1.0",
4 | "scripts": {
5 | "dev": "NEXT_PUBLIC_CHAT_API=http://localhost:8000/api/chat next dev",
6 | "build": "next build",
7 | "start": "next start",
8 | "lint": "next lint"
9 | },
10 | "dependencies": {
11 | "ai": "^2",
12 | "llamaindex": "0.0.35",
13 | "next": "^13",
14 | "react": "^18",
15 | "react-dom": "^18",
16 | "tailwind-merge": "^2",
17 | "@radix-ui/react-slot": "^1",
18 | "class-variance-authority": "^0.7",
19 | "lucide-react": "^0.291",
20 | "remark": "^14.0.3",
21 | "remark-code-import": "^1.2.0",
22 | "remark-gfm": "^3.0.1",
23 | "remark-math": "^5.1.1",
24 | "react-markdown": "^8.0.7",
25 | "react-syntax-highlighter": "^15.5.0"
26 | },
27 | "devDependencies": {
28 | "@types/node": "^20",
29 | "@types/react": "^18",
30 | "@types/react-dom": "^18",
31 | "autoprefixer": "^10",
32 | "postcss": "^8",
33 | "tailwindcss": "^3",
34 | "typescript": "^5",
35 | "@types/react-syntax-highlighter": "^15.5.6"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/frontend/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/frontend/public/llama.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rsrohan99/llamaindex-docs-agent/1b2a6f5efee1bd5a4b9cd7771eac5f76a57155b1/frontend/public/llama.png
--------------------------------------------------------------------------------
/frontend/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "tailwindcss";
2 | import { fontFamily } from "tailwindcss/defaultTheme";
3 |
4 | const config: Config = {
5 | darkMode: ["class"],
6 | content: ["app/**/*.{ts,tsx}", "components/**/*.{ts,tsx}"],
7 | theme: {
8 | container: {
9 | center: true,
10 | padding: "2rem",
11 | screens: {
12 | "2xl": "1400px",
13 | },
14 | },
15 | extend: {
16 | colors: {
17 | border: "hsl(var(--border))",
18 | input: "hsl(var(--input))",
19 | ring: "hsl(var(--ring))",
20 | background: "hsl(var(--background))",
21 | foreground: "hsl(var(--foreground))",
22 | primary: {
23 | DEFAULT: "hsl(var(--primary))",
24 | foreground: "hsl(var(--primary-foreground))",
25 | },
26 | secondary: {
27 | DEFAULT: "hsl(var(--secondary))",
28 | foreground: "hsl(var(--secondary-foreground))",
29 | },
30 | destructive: {
31 | DEFAULT: "hsl(var(--destructive) / )",
32 | foreground: "hsl(var(--destructive-foreground) / )",
33 | },
34 | muted: {
35 | DEFAULT: "hsl(var(--muted))",
36 | foreground: "hsl(var(--muted-foreground))",
37 | },
38 | accent: {
39 | DEFAULT: "hsl(var(--accent))",
40 | foreground: "hsl(var(--accent-foreground))",
41 | },
42 | popover: {
43 | DEFAULT: "hsl(var(--popover))",
44 | foreground: "hsl(var(--popover-foreground))",
45 | },
46 | card: {
47 | DEFAULT: "hsl(var(--card))",
48 | foreground: "hsl(var(--card-foreground))",
49 | },
50 | },
51 | borderRadius: {
52 | xl: `calc(var(--radius) + 4px)`,
53 | lg: `var(--radius)`,
54 | md: `calc(var(--radius) - 2px)`,
55 | sm: "calc(var(--radius) - 4px)",
56 | },
57 | fontFamily: {
58 | sans: ["var(--font-sans)", ...fontFamily.sans],
59 | },
60 | keyframes: {
61 | "accordion-down": {
62 | from: { height: "0" },
63 | to: { height: "var(--radix-accordion-content-height)" },
64 | },
65 | "accordion-up": {
66 | from: { height: "var(--radix-accordion-content-height)" },
67 | to: { height: "0" },
68 | },
69 | },
70 | animation: {
71 | "accordion-down": "accordion-down 0.2s ease-out",
72 | "accordion-up": "accordion-up 0.2s ease-out",
73 | },
74 | },
75 | },
76 | plugins: [],
77 | };
78 | export default config;
79 |
--------------------------------------------------------------------------------
/frontend/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "noEmit": true,
9 | "esModuleInterop": true,
10 | "module": "esnext",
11 | "moduleResolution": "bundler",
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "jsx": "preserve",
15 | "incremental": true,
16 | "plugins": [
17 | {
18 | "name": "next"
19 | }
20 | ],
21 | "paths": {
22 | "@/*": ["./*"]
23 | }
24 | },
25 | "include": [
26 | "next-env.d.ts",
27 | "**/*.ts",
28 | "**/*.tsx",
29 | ".next/types/**/*.ts",
30 | "app/components/ui/nodes.tsx"
31 | ],
32 | "exclude": ["node_modules"]
33 | }
34 |
--------------------------------------------------------------------------------