Skip to content
This repository was archived by the owner on Jan 5, 2025. It is now read-only.

Commit cc35064

Browse files
authored
Merge pull request #238 from lvalics/main
MAJOR UPGRADE - LangChain/OpenAI libraries.
2 parents 3e5db01 + 39ca6da commit cc35064

16 files changed

+655
-354
lines changed

dj_backend_server/CHANGELOG.MD

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
2.17.2024
2+
- Incorporate 'Ollama' into your example.env configuration and make sure to reflect these changes in your .env file for compatibility.
3+
- We've expanded the logging capabilities within settings.py by deploying logging.debug for more detailed insights, although it remains inactive when the DEBUG mode is off.
4+
- We've refreshed the dependencies listed in requirements.txt to ensure the latest support and functionality.
5+
- Updates to LangChain and OpenAI libraries, now feature the exciting addition of Ollama, currently under development.
6+
- This version has a significant update that is still pending comprehensive testing; therefore, we advise against deploying it in production environments until it is fully vetted.
7+
- Modifications have been made to the history module to append the input message to requests sent to OpenAI. These changes are still being tested. (disabled for the moemnt)
8+
19
2.14.2024
210
- Added example.env to streamline environment setup.
311
- Implemented translation fixes to enhance application localization.

dj_backend_server/api/data_sources/codebase_handler.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from django.views.decorators.csrf import csrf_exempt
44
from langchain.text_splitter import RecursiveCharacterTextSplitter
55
from api.utils import get_embeddings
6-
from langchain.document_loaders import GitLoader
6+
from langchain_community.document_loaders import GitLoader
77
from api.utils import init_vector_store
88
from api.interfaces import StoreOptions
99

dj_backend_server/api/data_sources/pdf_handler.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313
from django.utils import timezone
1414
from django.shortcuts import get_object_or_404
1515
from langchain.text_splitter import RecursiveCharacterTextSplitter
16-
from langchain.document_loaders.directory import DirectoryLoader
17-
from langchain.document_loaders import PyPDFium2Loader
18-
from langchain.document_loaders import TextLoader
16+
from langchain_community.document_loaders.directory import DirectoryLoader
17+
from langchain_community.document_loaders import PyPDFium2Loader
18+
from langchain_community.document_loaders import TextLoader
1919
from api.utils import get_embeddings
2020
from api.utils import init_vector_store
2121
from api.interfaces import StoreOptions

dj_backend_server/api/data_sources/website_handler.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import os
22
from django.http import JsonResponse
33
import traceback
4-
from langchain.document_loaders.directory import DirectoryLoader
4+
from langchain_community.document_loaders.directory import DirectoryLoader
55
from langchain.text_splitter import RecursiveCharacterTextSplitter
6-
from langchain.document_loaders import TextLoader
6+
from langchain_community.document_loaders import TextLoader
77
from api.utils import init_vector_store
88
from api.utils.get_embeddings import get_embeddings
99
from api.interfaces import StoreOptions

dj_backend_server/api/enums/embedding_type.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ class EmbeddingProvider(Enum):
66
BARD = "bard"
77
azure = "azure"
88
llama2 = "llama2"
9+
ollama = "ollama"
910

dj_backend_server/api/utils/custom_pdf_loader.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import io
22
from langchain.docstore.base import Document
3-
from langchain.document_loaders.base import BaseLoader
4-
from langchain.document_loaders import PyPDFLoader
3+
from langchain_community.document_loaders.base import BaseLoader
4+
from langchain_community.document_loaders import PyPDFLoader
55

66
class BufferLoader(BaseLoader):
77
def __init__(self, filePathOrBlob):

dj_backend_server/api/utils/get_embeddings.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1-
from langchain.embeddings.openai import OpenAIEmbeddings
2-
from api.enums import EmbeddingProvider
31
import os
42
from dotenv import load_dotenv
53
from langchain.embeddings.base import Embeddings
6-
from langchain.embeddings import LlamaCppEmbeddings
4+
from langchain_community.embeddings import LlamaCppEmbeddings
5+
from langchain_openai import OpenAIEmbeddings
6+
from api.enums import EmbeddingProvider
77

88
load_dotenv()
9-
import os
109

1110

1211
def get_embedding_provider():
1312
"""Gets the chosen embedding provider from environment variables."""
1413
return os.environ.get("EMBEDDING_PROVIDER")
1514

15+
1616
def get_azure_embedding():
1717
"""Gets embeddings using the Azure embedding provider."""
1818
deployment = os.environ.get("AZURE_OPENAI_EMBEDDING_MODEL_NAME")
@@ -30,16 +30,18 @@ def get_azure_embedding():
3030
openai_api_version=openai_api_version
3131
)
3232

33+
3334
def get_openai_embedding():
3435
"""Gets embeddings using the OpenAI embedding provider."""
3536
openai_api_key = os.environ.get("OPENAI_API_KEY")
37+
return OpenAIEmbeddings(openai_api_key=openai_api_key, chunk_size=1)
3638

37-
return OpenAIEmbeddings(openai_api_key=openai_api_key, chunk_size=1)
3839

3940
def get_llama2_embedding():
4041
"""Gets embeddings using the llama2 embedding provider."""
4142
return LlamaCppEmbeddings(model_path="llama-2-7b-chat.ggmlv3.q4_K_M.bin")
4243

44+
4345
def choose_embedding_provider():
4446
"""Chooses and returns the appropriate embedding provider instance."""
4547
embedding_provider = get_embedding_provider()
@@ -52,7 +54,6 @@ def choose_embedding_provider():
5254

5355
elif embedding_provider == EmbeddingProvider.llama2.value:
5456
return get_llama2_embedding()
55-
5657

5758
else:
5859
available_providers = ", ".join([service.value for service in EmbeddingProvider])
@@ -61,6 +62,7 @@ def choose_embedding_provider():
6162
f"Available services: {available_providers}"
6263
)
6364

65+
6466
# Main function to get embeddings
6567
def get_embeddings() -> Embeddings:
6668
"""Gets embeddings using the chosen embedding provider."""
Lines changed: 146 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,33 @@
1-
from langchain.llms import AzureOpenAI, OpenAI
21
import os
32
from dotenv import load_dotenv
4-
from langchain.llms import LlamaCpp
5-
load_dotenv()
6-
from langchain import PromptTemplate, LLMChain
3+
import logging.config
4+
import traceback
5+
from django.utils.timezone import make_aware
6+
from datetime import datetime, timezone
7+
from uuid import uuid4
8+
from ollama import Client
9+
from openai import OpenAI
10+
from django.conf import settings
11+
from langchain_openai.chat_models import ChatOpenAI
12+
from langchain_community.llms import Ollama
13+
from langchain_community.llms import AzureOpenAI
14+
from langchain_community.llms import LlamaCpp
15+
from langchain.prompts import PromptTemplate
16+
from langchain.chains import LLMChain
717
from langchain.callbacks.manager import CallbackManager
818
from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler
9-
import traceback
1019
from web.models.failed_jobs import FailedJob
11-
from datetime import datetime
12-
from uuid import uuid4
20+
21+
load_dotenv()
22+
logging.config.dictConfig(settings.LOGGING)
23+
logger = logging.getLogger(__name__)
24+
1325

1426
def get_llama_llm():
1527
try:
1628
n_gpu_layers = 1 # Metal set to 1 is enough.
1729
n_batch = 512 # Should be between 1 and n_ctx, consider the amount of RAM of your Apple Silicon Chip.
18-
30+
1931
# Callbacks support token-wise streaming
2032
callback_manager = CallbackManager([StreamingStdOutCallbackHandler()])
2133
llm = LlamaCpp(
@@ -28,24 +40,44 @@ def get_llama_llm():
2840
verbose=True,
2941
temperature=0.2,
3042
)
31-
43+
3244
return llm
3345
except Exception as e:
34-
failed_job = FailedJob(uuid=str(uuid4()), connection='default', queue='default', payload='get_llama_llm', exception=str(e), failed_at=datetime.now())
46+
47+
logger.debug(f"Exception in get_llama_llm: {e}")
48+
failed_job = FailedJob(
49+
uuid=str(uuid4()),
50+
connection="default",
51+
queue="default",
52+
payload="get_llama_llm",
53+
exception=str(e),
54+
failed_at=make_aware(datetime.now(), timezone.utc),
55+
)
3556
failed_job.save()
3657
print(f"Exception occurred: {e}")
3758
traceback.print_exc()
3859

60+
3961
# Azure OpenAI Language Model client
4062
def get_azure_openai_llm():
4163
"""Returns AzureOpenAI instance configured from environment variables"""
4264
try:
43-
openai_api_type = os.environ['OPENAI_API_TYPE']
44-
openai_api_key = os.environ['AZURE_OPENAI_API_KEY']
45-
openai_deployment_name = os.environ['AZURE_OPENAI_DEPLOYMENT_NAME']
46-
openai_model_name = os.environ['AZURE_OPENAI_COMPLETION_MODEL']
47-
openai_api_version = os.environ['AZURE_OPENAI_API_VERSION']
48-
openai_api_base=os.environ['AZURE_OPENAI_API_BASE']
65+
if settings.DEBUG:
66+
openai_api_type = "openai" # JUST FOR DEVELOPMENT
67+
logging.debug(f"DEVELOPMENT Using API Type: {openai_api_type}")
68+
else:
69+
openai_api_type = os.environ["AZURE_OPENAI_API_TYPE"]
70+
71+
openai_api_key = os.environ["AZURE_OPENAI_API_KEY"]
72+
openai_deployment_name = os.environ["AZURE_OPENAI_DEPLOYMENT_NAME"]
73+
openai_model_name = os.environ["AZURE_OPENAI_COMPLETION_MODEL"]
74+
openai_api_version = os.environ["AZURE_OPENAI_API_VERSION"]
75+
openai_api_base = os.environ["AZURE_OPENAI_API_BASE"]
76+
openai_api_key = os.environ["AZURE_OPENAI_API_KEY"]
77+
openai_deployment_name = os.environ["AZURE_OPENAI_DEPLOYMENT_NAME"]
78+
openai_model_name = os.environ["AZURE_OPENAI_COMPLETION_MODEL"]
79+
openai_api_version = os.environ["AZURE_OPENAI_API_VERSION"]
80+
openai_api_base = os.environ["AZURE_OPENAI_API_BASE"]
4981
return AzureOpenAI(
5082
openai_api_base=openai_api_base,
5183
openai_api_key=openai_api_key,
@@ -54,51 +86,127 @@ def get_azure_openai_llm():
5486
openai_api_type=openai_api_type,
5587
openai_api_version=openai_api_version,
5688
temperature=0,
57-
batch_size=8
89+
batch_size=8,
5890
)
5991
except Exception as e:
60-
failed_job = FailedJob(uuid=str(uuid4()), connection='default', queue='default', payload='get_azure_openai_llm', exception=str(e), failed_at=datetime.now())
92+
logger.debug(f"Exception in get_azure_openai_llm: {e}")
93+
failed_job = FailedJob(
94+
uuid=str(uuid4()),
95+
connection="default",
96+
queue="default",
97+
payload="get_azure_openai_llm",
98+
exception=str(e),
99+
failed_at=make_aware(datetime.now(), timezone.utc),
100+
)
61101
failed_job.save()
62102
print(f"Exception occurred: {e}")
63103
traceback.print_exc()
64104

65-
# OpenAI Language Model client
105+
106+
# OpenAI Language Model client
66107
def get_openai_llm():
67108
"""Returns OpenAI instance configured from environment variables"""
68109
try:
69-
openai_api_key = os.environ['OPENAI_API_KEY']
110+
openai_api_key = os.environ.get("OPENAI_API_KEY")
111+
temperature = os.environ.get("OPENAI_API_TEMPERATURE")
112+
model = os.environ.get("OPENAI_API_MODEL", "gpt-3.5-turbo")
70113

71-
return OpenAI(
72-
temperature=float(os.environ.get('OPENAI_API_TEMPERATURE', '0')),
114+
logging.debug(
115+
f"We are in get_openai_llm: {openai_api_key} {temperature} {model}"
116+
)
117+
return ChatOpenAI(
118+
temperature=temperature,
73119
openai_api_key=openai_api_key,
74-
model_name=os.environ.get('OPENAI_API_MODEL', 'gpt-3.5-turbo'),
120+
model=model,
75121
)
76122
except Exception as e:
77-
failed_job = FailedJob(uuid=str(uuid4()), connection='default', queue='default', payload='get_openai_llm', exception=str(e), failed_at=datetime.now())
123+
logger.debug(f"Exception in get_openai_llm: {e}")
124+
failed_job = FailedJob(
125+
uuid=str(uuid4()),
126+
connection="default",
127+
queue="default",
128+
payload="get_openai_llm",
129+
exception=str(e),
130+
failed_at=make_aware(datetime.now(), timezone.utc),
131+
)
132+
failed_job.save()
133+
print(f"Exception occurred: {e}")
134+
traceback.print_exc()
135+
136+
137+
def get_ollama_llm(sanitized_question):
138+
"""Returns an Ollama Server instance configured from environment variables"""
139+
llm = Client(host=os.environ.get("OLLAMA_URL"))
140+
# Use the client to make a request
141+
try:
142+
if sanitized_question:
143+
response = llm.chat(
144+
model=os.environ.get("OLLAMA_MODEL_NAME"),
145+
messages=[{"role": "user", "content": sanitized_question}],
146+
)
147+
else:
148+
raise ValueError("Question cannot be None.")
149+
if response:
150+
return response
151+
else:
152+
raise ValueError("Invalid response from Ollama.")
153+
154+
except Exception as e:
155+
logger.debug(f"Exception in get_ollama_llm: {e}")
156+
failed_job = FailedJob(
157+
uuid=str(uuid4()),
158+
connection="default",
159+
queue="default",
160+
payload="get_openai_llm",
161+
exception=str(e),
162+
failed_at=make_aware(datetime.now(), timezone.utc),
163+
)
78164
failed_job.save()
79165
print(f"Exception occurred: {e}")
80166
traceback.print_exc()
81167

82-
83-
# recommend not caching initially, and optimizing only if you observe a clear performance benefit from caching the clients.
84-
# The simplest thing that works is often best to start.
85168

86169
def get_llm():
87170
"""Returns LLM client instance based on OPENAI_API_TYPE"""
88171
try:
89172
clients = {
90-
'azure': get_azure_openai_llm,
91-
'openai': get_openai_llm,
92-
'llama2': get_llama_llm
173+
"azure": get_azure_openai_llm,
174+
"openai": get_openai_llm,
175+
"llama2": get_llama_llm,
176+
"ollama": lambda: get_ollama_llm(),
93177
}
94-
95-
api_type = os.environ.get('OPENAI_API_TYPE')
178+
179+
api_type = os.environ.get("OPENAI_API_TYPE", "openai")
96180
if api_type not in clients:
97181
raise ValueError(f"Invalid OPENAI_API_TYPE: {api_type}")
98-
99-
return clients[api_type]()
182+
183+
logging.debug(f"Using LLM: {api_type}")
184+
185+
if api_type in clients:
186+
if api_type == "ollama":
187+
return clients[api_type]()
188+
elif api_type != "ollama":
189+
return clients[api_type]()
190+
else:
191+
raise ValueError(f"Invalid OPENAI_API_TYPE: {api_type}")
192+
100193
except Exception as e:
101-
failed_job = FailedJob(uuid=str(uuid4()), connection='default', queue='default', payload='get_llm', exception=str(e), failed_at=datetime.now())
102-
failed_job.save()
103-
print(f"Exception occurred: {e}")
104-
traceback.print_exc()
194+
failed_job = FailedJob(
195+
uuid=str(uuid4()),
196+
connection="default",
197+
queue="default",
198+
payload="get_llm",
199+
exception=str(e),
200+
failed_at=datetime.now(),
201+
)
202+
failed_job = FailedJob(
203+
uuid=str(uuid4()),
204+
connection="default",
205+
queue="default",
206+
payload="get_llm",
207+
exception=str(e),
208+
failed_at=make_aware(datetime.now(), timezone.utc),
209+
)
210+
failed_job.save() # Ensure datetime is timezone-aware
211+
print(f"Exception occurred in get_llm: {e}")
212+
traceback.print_exc()

dj_backend_server/api/utils/init_vector_store.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from langchain.docstore.document import Document
22
from langchain.vectorstores.qdrant import Qdrant
3-
from langchain.embeddings.openai import OpenAIEmbeddings
3+
from langchain_openai import OpenAIEmbeddings
44
from langchain.vectorstores.pinecone import Pinecone
55
from qdrant_client import QdrantClient
66
from qdrant_client import models
@@ -130,6 +130,18 @@ def delete_from_vector_store(namespace: str, filter_criteria: dict) -> None:
130130

131131

132132
def ensure_vector_database_exists(namespace):
133+
"""
134+
This function ensures that a vector database exists for a given namespace. If the database does not exist, it attempts to
135+
create it. The function uses the 'STORE' environment variable to determine the type of store to use. If the store type is
136+
'QDRANT', it uses a QdrantClient to interact with the Qdrant server.
137+
138+
Args:
139+
namespace (str): The namespace for which to ensure a vector database exists.
140+
141+
Raises:
142+
Exception: If the function fails to ensure or create the vector database after 3 attempts, it raises an exception.
143+
It also raises an exception if any other error occurs during the process.
144+
"""
133145
store_type = StoreType[os.environ['STORE']]
134146
try:
135147
if store_type == StoreType.QDRANT:

0 commit comments

Comments
 (0)