From 24ea84f1a317498cbe685423a9ab0bb63d7e2fa8 Mon Sep 17 00:00:00 2001
From: Marcus Schiesser <mail@marcusschiesser.de>
Date: Thu, 21 Dec 2023 16:13:35 +0700
Subject: [PATCH] feat[cl-fastapi]: draft for new fastapi structure (supporting
 engines)

---
 .../streaming/fastapi/README-template.md      |  2 +-
 .../streaming/fastapi/app/api/routers/chat.py |  7 ++--
 .../types/streaming/fastapi/app/context.py    | 11 ++++++
 .../fastapi/app/{utils => engine}/__init__.py |  0
 .../streaming/fastapi/app/engine/constants.py |  4 ++
 .../streaming/fastapi/app/engine/context.py   | 13 +++++++
 .../streaming/fastapi/app/engine/generate.py  | 30 +++++++++++++++
 .../streaming/fastapi/app/engine/index.py     | 25 ++++++++++++
 .../streaming/fastapi/app/utils/index.py      | 38 -------------------
 templates/types/streaming/fastapi/main.py     |  1 +
 10 files changed, 88 insertions(+), 43 deletions(-)
 create mode 100644 templates/types/streaming/fastapi/app/context.py
 rename templates/types/streaming/fastapi/app/{utils => engine}/__init__.py (100%)
 create mode 100644 templates/types/streaming/fastapi/app/engine/constants.py
 create mode 100644 templates/types/streaming/fastapi/app/engine/context.py
 create mode 100644 templates/types/streaming/fastapi/app/engine/generate.py
 create mode 100644 templates/types/streaming/fastapi/app/engine/index.py
 delete mode 100644 templates/types/streaming/fastapi/app/utils/index.py

diff --git a/templates/types/streaming/fastapi/README-template.md b/templates/types/streaming/fastapi/README-template.md
index f0bfa5e0..77fa8793 100644
--- a/templates/types/streaming/fastapi/README-template.md
+++ b/templates/types/streaming/fastapi/README-template.md
@@ -9,7 +9,7 @@ poetry install
 poetry shell
 ```
 
-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.
+By default, we use the OpenAI LLM (though you can customize, see `app/context.py`). As a result you need to specify an `OPENAI_API_KEY` in an .env file in this directory.
 
 Example `backend/.env` file:
 
diff --git a/templates/types/streaming/fastapi/app/api/routers/chat.py b/templates/types/streaming/fastapi/app/api/routers/chat.py
index c55b3bbe..9dd9eb6e 100644
--- a/templates/types/streaming/fastapi/app/api/routers/chat.py
+++ b/templates/types/streaming/fastapi/app/api/routers/chat.py
@@ -1,10 +1,10 @@
 from typing import List
 
 from fastapi.responses import StreamingResponse
+from llama_index.chat_engine.types import BaseChatEngine
 
-from app.utils.index import get_index
+from app.engine.index import get_chat_engine
 from fastapi import APIRouter, Depends, HTTPException, Request, status
-from llama_index import VectorStoreIndex
 from llama_index.llms.base import ChatMessage
 from llama_index.llms.types import MessageRole
 from pydantic import BaseModel
@@ -25,7 +25,7 @@ class _ChatData(BaseModel):
 async def chat(
     request: Request,
     data: _ChatData,
-    index: VectorStoreIndex = Depends(get_index),
+    chat_engine: BaseChatEngine = Depends(get_chat_engine),
 ):
     # check preconditions and get last message
     if len(data.messages) == 0:
@@ -49,7 +49,6 @@ async def chat(
     ]
 
     # query chat engine
-    chat_engine = index.as_chat_engine()
     response = chat_engine.stream_chat(lastMessage.content, messages)
 
     # stream response
diff --git a/templates/types/streaming/fastapi/app/context.py b/templates/types/streaming/fastapi/app/context.py
new file mode 100644
index 00000000..ae00de21
--- /dev/null
+++ b/templates/types/streaming/fastapi/app/context.py
@@ -0,0 +1,11 @@
+import os
+
+from llama_index import ServiceContext
+from llama_index.llms import OpenAI
+
+
+def create_base_context():
+    model = os.getenv("MODEL", "gpt-3.5-turbo")
+    return ServiceContext.from_defaults(
+        llm=OpenAI(model=model),
+    )
diff --git a/templates/types/streaming/fastapi/app/utils/__init__.py b/templates/types/streaming/fastapi/app/engine/__init__.py
similarity index 100%
rename from templates/types/streaming/fastapi/app/utils/__init__.py
rename to templates/types/streaming/fastapi/app/engine/__init__.py
diff --git a/templates/types/streaming/fastapi/app/engine/constants.py b/templates/types/streaming/fastapi/app/engine/constants.py
new file mode 100644
index 00000000..6dba7d2e
--- /dev/null
+++ b/templates/types/streaming/fastapi/app/engine/constants.py
@@ -0,0 +1,4 @@
+STORAGE_DIR = "./storage"  # directory to cache the generated index
+DATA_DIR = "./data"  # directory containing the documents to index
+CHUNK_SIZE = 1024
+CHUNK_OVERLAP = 20
diff --git a/templates/types/streaming/fastapi/app/engine/context.py b/templates/types/streaming/fastapi/app/engine/context.py
new file mode 100644
index 00000000..4756d813
--- /dev/null
+++ b/templates/types/streaming/fastapi/app/engine/context.py
@@ -0,0 +1,13 @@
+from llama_index import ServiceContext
+
+from app.context import create_base_context
+from app.engine.constants import CHUNK_SIZE, CHUNK_OVERLAP
+
+
+def create_service_context():
+    base = create_base_context()
+    return ServiceContext.from_defaults(
+        llm=base.llm,
+        chunk_size=CHUNK_SIZE,
+        chunk_overlap=CHUNK_OVERLAP,
+    )
diff --git a/templates/types/streaming/fastapi/app/engine/generate.py b/templates/types/streaming/fastapi/app/engine/generate.py
new file mode 100644
index 00000000..3abb7491
--- /dev/null
+++ b/templates/types/streaming/fastapi/app/engine/generate.py
@@ -0,0 +1,30 @@
+import logging
+
+from dotenv import load_dotenv
+
+from app.engine.constants import DATA_DIR, STORAGE_DIR
+from app.engine.context import create_service_context
+
+load_dotenv()
+
+from llama_index import (
+    SimpleDirectoryReader,
+    VectorStoreIndex,
+)
+
+logger = logging.getLogger("uvicorn")
+
+
+def generate_datasource(service_context):
+    logger.info("Creating new index")
+    # load the documents and create the index
+    documents = SimpleDirectoryReader(DATA_DIR).load_data()
+    index = VectorStoreIndex.from_documents(documents, service_context=service_context)
+    # store it for later
+    index.storage_context.persist(STORAGE_DIR)
+    logger.info(f"Finished creating new index. Stored in {STORAGE_DIR}")
+
+
+if __name__ == "__main__":
+    service_context = create_service_context()
+    generate_datasource(service_context)
diff --git a/templates/types/streaming/fastapi/app/engine/index.py b/templates/types/streaming/fastapi/app/engine/index.py
new file mode 100644
index 00000000..8f7d3603
--- /dev/null
+++ b/templates/types/streaming/fastapi/app/engine/index.py
@@ -0,0 +1,25 @@
+import logging
+import os
+from llama_index import (
+    StorageContext,
+    load_index_from_storage,
+)
+
+from app.engine.constants import STORAGE_DIR
+from app.engine.context import create_service_context
+
+
+def get_chat_engine():
+    service_context = create_service_context()
+    # check if storage already exists
+    if not os.path.exists(STORAGE_DIR):
+        raise Exception(
+            "StorageContext is empty - call 'npm run generate' to generate the storage first"
+        )
+    logger = logging.getLogger("uvicorn")
+    # load the existing index
+    logger.info(f"Loading index from {STORAGE_DIR}...")
+    storage_context = StorageContext.from_defaults(persist_dir=STORAGE_DIR)
+    index = load_index_from_storage(storage_context, service_context=service_context)
+    logger.info(f"Finished loading index from {STORAGE_DIR}")
+    return index.as_chat_engine()
diff --git a/templates/types/streaming/fastapi/app/utils/index.py b/templates/types/streaming/fastapi/app/utils/index.py
deleted file mode 100644
index cb16cdba..00000000
--- a/templates/types/streaming/fastapi/app/utils/index.py
+++ /dev/null
@@ -1,38 +0,0 @@
-import logging
-import os
-
-from llama_index import (
-    SimpleDirectoryReader,
-    StorageContext,
-    VectorStoreIndex,
-    load_index_from_storage,
-    ServiceContext,
-)
-from llama_index.llms import OpenAI
-
-
-STORAGE_DIR = "./storage"  # directory to cache the generated index
-DATA_DIR = "./data"  # directory containing the documents to index
-
-service_context = ServiceContext.from_defaults(
-    llm=OpenAI(model="gpt-3.5-turbo")
-)
-
-def get_index():
-    logger = logging.getLogger("uvicorn")
-    # check if storage already exists
-    if not os.path.exists(STORAGE_DIR):
-        logger.info("Creating new index")
-        # load the documents and create the index
-        documents = SimpleDirectoryReader(DATA_DIR).load_data()
-        index = VectorStoreIndex.from_documents(documents,service_context=service_context)
-        # store it for later
-        index.storage_context.persist(STORAGE_DIR)
-        logger.info(f"Finished creating new index. Stored in {STORAGE_DIR}")
-    else:
-        # load the existing index
-        logger.info(f"Loading index from {STORAGE_DIR}...")
-        storage_context = StorageContext.from_defaults(persist_dir=STORAGE_DIR)
-        index = load_index_from_storage(storage_context,service_context=service_context)
-        logger.info(f"Finished loading index from {STORAGE_DIR}")
-    return index
diff --git a/templates/types/streaming/fastapi/main.py b/templates/types/streaming/fastapi/main.py
index 00cb79c4..ba56f034 100644
--- a/templates/types/streaming/fastapi/main.py
+++ b/templates/types/streaming/fastapi/main.py
@@ -1,4 +1,5 @@
 from dotenv import load_dotenv
+
 load_dotenv()
 
 import logging
-- 
GitLab