Skip to content

Commit

Permalink
Merge branch 'refactoring-backend-muggtschibidi' into 'main'
Browse files Browse the repository at this point in the history
Backend refactoren

See merge request kicc/mucgpt!13
  • Loading branch information
Meteord committed Jun 20, 2024
2 parents 3a971ee + 4bf99e7 commit 840503f
Show file tree
Hide file tree
Showing 21 changed files with 405 additions and 340 deletions.
278 changes: 65 additions & 213 deletions app/backend/app.py

Large diffs are not rendered by default.

17 changes: 12 additions & 5 deletions app/backend/approaches/brainstorm.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
from typing import Any, Optional
from langchain.chains import LLMChain

from langchain.prompts import PromptTemplate
from langchain.chains import SequentialChain
from langchain_community.callbacks import get_openai_callback
from langchain_core.runnables.base import RunnableSerializable

from core.types.LlmConfigs import LlmConfigs
from core.datahelper import Repository
from core.types.Config import ApproachConfig
from core.types.AzureChatGPTConfig import AzureChatGPTConfig
from core.datahelper import Requestinfo
from langchain_core.language_models.chat_models import BaseChatModel

class Brainstorm():
"""
Expand Down Expand Up @@ -53,9 +55,10 @@ class Brainstorm():
Text:
{brainstorm}"""

def __init__(self, llm: BaseChatModel, config: ApproachConfig, repo: Repository):
def __init__(self, llm: RunnableSerializable, config: ApproachConfig, model_info: AzureChatGPTConfig, repo: Repository):
self.llm = llm
self.config = config
self.model_info = model_info
self.repo = repo

def getBrainstormPrompt(self) -> PromptTemplate:
Expand All @@ -66,8 +69,12 @@ def getTranslationPrompt(self) -> PromptTemplate:


async def run(self, topic: str, language: str, department: Optional[str]) -> Any:
brainstormChain = LLMChain(llm=self.llm, prompt=self.getBrainstormPrompt(), output_key="brainstorm")
translationChain = LLMChain(llm=self.llm, prompt=self.getTranslationPrompt(), output_key="translation")
config: LlmConfigs = {
"llm_api_key": self.model_info["openai_api_key"]
}
llm = self.llm.with_config(configurable=config)
brainstormChain = LLMChain(llm=llm, prompt=self.getBrainstormPrompt(), output_key="brainstorm")
translationChain = LLMChain(llm=llm, prompt=self.getTranslationPrompt(), output_key="translation")
overall_chain = SequentialChain(
chains=[brainstormChain, translationChain],
input_variables=["language", "topic"],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,65 +1,50 @@
from typing import Any, AsyncGenerator, Callable, Optional, Sequence

from core.datahelper import Requestinfo
from core.modelhelper import num_tokens_from_message, num_tokens_from_messages
from core.datahelper import Repository
from core.types.Config import ApproachConfig
from core.types.Chunk import Chunk, ChunkInfo
from typing import Any, AsyncGenerator, Optional, Sequence
import asyncio
from langchain.prompts import (
ChatPromptTemplate,
MessagesPlaceholder,
HumanMessagePromptTemplate,
SystemMessagePromptTemplate
)

from langchain_core.language_models.chat_models import BaseChatModel
from langchain.chains import LLMChain
from langchain.memory import ConversationBufferMemory
from langchain.callbacks.streaming_aiter import AsyncIteratorCallbackHandler
from langchain_core.messages import AIMessage
from langchain_community.callbacks import get_openai_callback
from langchain_core.runnables.base import RunnableSerializable

import asyncio
from core.datahelper import Requestinfo
from core.modelhelper import num_tokens_from_message, num_tokens_from_messages
from core.datahelper import Repository
from core.types.Config import ApproachConfig
from core.types.Chunk import Chunk, ChunkInfo
from core.types.LlmConfigs import LlmConfigs
from core.types.AzureChatGPTConfig import AzureChatGPTConfig

class SimpleChatApproach():
class ChatApproach():

def __init__(self, createLLM: Callable[[int, float, AsyncIteratorCallbackHandler], BaseChatModel], config: ApproachConfig, repo: Repository, chatgpt_model: str):
self.createLLM = createLLM
def __init__(self, llm: RunnableSerializable, config: ApproachConfig, model_info: AzureChatGPTConfig, repo: Repository, chatgpt_model: str):
self.llm = llm
self.config = config
self.model_info = model_info
self.repo = repo
self.chatgpt_model = chatgpt_model

async def run_until_final_call(self, history: "Sequence[dict[str, str]]", llm: BaseChatModel, system_message: Optional[str]) -> Any:
user_q = history[-1]["user"]
messages = [
# The `variable_name` here is what must align with memory
MessagesPlaceholder(variable_name="chat_history"),
HumanMessagePromptTemplate.from_template("{question}")
]
if(system_message and system_message.strip() !=""):
messages.insert(0,
SystemMessagePromptTemplate.from_template(
system_message
))
prompt = ChatPromptTemplate(
messages=messages
)
# Notice that we `return_messages=True` to fit into the MessagesPlaceholder
# Notice that `"chat_history"` aligns with the MessagesPlaceholder name.
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
## initialize memory with our own chat model.
self.initializeMemory(history[:-1],memory=memory)
conversation = LLMChain(
llm=llm,
prompt=prompt,
memory=memory
)

async def run_until_final_call(self, history: "Sequence[dict[str, str]]", llm: RunnableSerializable, system_message: Optional[str]) -> Any:
user_q, conversation = self.init_conversation(history, llm, system_message)
chat_coroutine = conversation.acall({"question": user_q})
return (chat_coroutine)


async def run_with_streaming(self, history: 'list[dict[str, str]]',max_tokens: int, temperature: float, system_message: Optional[str], department: Optional[str]) -> AsyncGenerator[dict, None]:
handler = AsyncIteratorCallbackHandler()
llm = self.createLLM(max_tokens, temperature, handler)
config: LlmConfigs = {
"llm_max_tokens": max_tokens,
"llm_api_key": self.model_info["openai_api_key"],
"llm_temperature": temperature,
"llm_streaming": True,
"llm_callbacks": [handler],
}
llm = self.llm.with_config(configurable=config)
chat_coroutine = await self.run_until_final_call(history, llm=llm, system_message=system_message)
task = asyncio.create_task(chat_coroutine)
result = ""
Expand Down Expand Up @@ -92,9 +77,59 @@ async def run_with_streaming(self, history: 'list[dict[str, str]]',max_tokens: i

info = ChunkInfo(requesttokens=num_tokens_from_message(history[-1]["user"],self.chatgpt_model), streamedtokens=num_tokens_from_message(result,self.chatgpt_model))
yield Chunk(type="I", message=info, order=position)

def run_without_streaming(self, history: "Sequence[dict[str, str]]", max_tokens: int, temperature: float, system_message: Optional[str], department: Optional[str]) -> str:
config: LlmConfigs = {
"llm_max_tokens": max_tokens,
"llm_api_key": self.model_info["openai_api_key"],
"llm_temperature": temperature,
"llm_streaming": False,
}
llm = self.llm.with_config(configurable=config)
user_q, conversation = self.init_conversation(history, llm, system_message)
with get_openai_callback() as cb:
ai_message: AIMessage = conversation.invoke({"question": user_q})
total_tokens = cb.total_tokens

info = ChunkInfo(requesttokens=cb.prompt_tokens, streamedtokens=cb.completion_tokens)
if self.config["log_tokens"]:
self.repo.addInfo(Requestinfo(
tokencount = total_tokens,
department = department,
messagecount= 1,
method = "Brainstorm"))
return ai_message["chat_history"][-1].content

def init_conversation(self, history, llm, system_message):
user_q = history[-1]["user"]
messages = [
# The `variable_name` here is what must align with memory
MessagesPlaceholder(variable_name="chat_history"),
HumanMessagePromptTemplate.from_template("{question}")
]
if(system_message and system_message.strip() !=""):
messages.insert(0,
SystemMessagePromptTemplate.from_template(
system_message
))
prompt = ChatPromptTemplate(
messages=messages
)
# Notice that we `return_messages=True` to fit into the MessagesPlaceholder
# Notice that `"chat_history"` aligns with the MessagesPlaceholder name.
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
## initialize memory with our own chat model.
self.init_mem(history[:-1],memory=memory)
conversation = LLMChain(
llm=llm,
prompt=prompt,
memory=memory
)

return user_q,conversation


def initializeMemory(self, messages:"Sequence[dict[str, str]]", memory: ConversationBufferMemory) :
def init_mem(self, messages:"Sequence[dict[str, str]]", memory: ConversationBufferMemory) :
for conversation in messages:
if("user" in conversation and conversation["user"]):
userMsg = conversation["user"]
Expand Down
38 changes: 31 additions & 7 deletions app/backend/approaches/summarize.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
from concurrent.futures import ThreadPoolExecutor
from typing import Any, List, Optional

import json
import re
from langchain.prompts import PromptTemplate
from langchain.chains import SequentialChain, LLMChain
from langchain_core.language_models.chat_models import BaseChatModel
from langchain_community.callbacks import get_openai_callback
from langchain_core.runnables.base import RunnableSerializable

from core.datahelper import Repository
from core.types.Config import ApproachConfig
from core.datahelper import Requestinfo
import json
import re
from core.textsplit import splitPDF, splitText
from core.types.AzureChatGPTConfig import AzureChatGPTConfig
from core.types.LlmConfigs import LlmConfigs


class Summarize():
"""Chain of Density prompting: https://arxiv.org/abs/2309.04269"""
Expand Down Expand Up @@ -79,10 +83,16 @@ class Summarize():



def __init__(self, llm: BaseChatModel, config: ApproachConfig, repo: Repository):
def __init__(self, llm: RunnableSerializable, config: ApproachConfig, model_info: AzureChatGPTConfig, repo: Repository, short_split = 2100, medium_split = 1500, long_split = 700):
self.llm = llm
self.config = config
self.model_info = model_info
self.repo = repo
self.switcher = {
"short": short_split,
"medium": medium_split,
"long": long_split,
}

def getSummarizationPrompt(self) -> PromptTemplate:
return PromptTemplate(input_variables=["text"], template=self.user_sum_prompt)
Expand All @@ -95,9 +105,13 @@ def getTranslationCleanupPrompt(self) -> PromptTemplate:


def setup(self) -> SequentialChain:
config: LlmConfigs = {
"llm_api_key": self.model_info["openai_api_key"]
}
llm = self.llm.with_config(configurable=config)
# setup model
summarizationChain = LLMChain(llm=self.llm, prompt=self.getSummarizationPrompt(), output_key="sum")
translationChain = LLMChain(llm=self.llm, prompt=self.getTranslationCleanupPrompt(), output_key="translation")
summarizationChain = LLMChain(llm=llm, prompt=self.getSummarizationPrompt(), output_key="sum")
translationChain = LLMChain(llm=llm, prompt=self.getTranslationCleanupPrompt(), output_key="translation")

return (summarizationChain, translationChain)

Expand Down Expand Up @@ -202,3 +216,13 @@ async def run(self, splits: List[str], language: str, department: Optional[str]

return {"answer": final_summarys}

def split(self, detaillevel: str, file = None, text = None):
splitsize = self.switcher.get(detaillevel, 700)

#TODO Wenn Cleanup tokenlimit sprengt, was machen? In mehreren Schritten übersetzen.
if(file is not None):
splits = splitPDF(file, splitsize, 0)
else:
splits = splitText(text, splitsize, 0)
return splits

89 changes: 49 additions & 40 deletions app/backend/core/llmhelper.py
Original file line number Diff line number Diff line change
@@ -1,46 +1,55 @@


from typing import Callable
from langchain_openai import AzureChatOpenAI
from langchain_core.language_models.chat_models import BaseChatModel
from langchain.callbacks.streaming_aiter import AsyncIteratorCallbackHandler

def getAzureChatGPT(chatgpt_model: str,
max_tokens: int,
n: int,
openai_api_key: str,
openai_api_base: str,
openai_api_version: str,
openai_api_type: str,
temperature: float) -> BaseChatModel:
return AzureChatOpenAI(
model=chatgpt_model,
max_tokens=max_tokens,
n=n,
deployment_name= "chat",
openai_api_key=openai_api_key,
azure_endpoint=openai_api_base,
openai_api_version=openai_api_version,
openai_api_type=openai_api_type,
temperature=temperature
)
from langchain_core.runnables import ConfigurableField
from langchain_community.llms.fake import FakeListLLM
from langchain_core.runnables.base import RunnableSerializable
from core.types.SupportedModels import SupportedModels

def createAzureChatGPT(chatgpt_model: str,
n: int,
openai_api_key: str,
openai_api_base: str,
openai_api_version: str,
openai_api_type: str,) -> Callable[[AsyncIteratorCallbackHandler], BaseChatModel]:
return lambda max_tokens, temperature, callback : AzureChatOpenAI(
model=chatgpt_model,
def getModel(chatgpt_model: str,
max_tokens: int,
n: int,
api_key: str,
api_base: str,
api_version: str,
api_type: str,
temperature: float,
streaming: bool) -> RunnableSerializable:
llm = AzureChatOpenAI(
model=chatgpt_model,
max_tokens=max_tokens,
n=n,
deployment_name= "chat",
openai_api_key=openai_api_key,
azure_endpoint=openai_api_base,
openai_api_version=openai_api_version,
openai_api_type=openai_api_type,
openai_api_key=api_key,
azure_endpoint=api_base,
openai_api_version=api_version,
openai_api_type=api_type,
temperature=temperature,
streaming=True,
callbacks=[callback]
)
streaming=streaming,
).configurable_fields(
temperature=ConfigurableField(
id="llm_temperature",
name="LLM Temperature",
description="The temperature of the LLM",
),
max_tokens= ConfigurableField(
id="llm_max_tokens",
name="LLM max Tokens",
description="The token Limit of the LLM",
),
openai_api_key = ConfigurableField(
id="llm_api_key",
name="The api key",
description="The api key"),
streaming = ConfigurableField(
id="llm_streaming",
name="Streaming",
description="Should the LLM Stream"),
callbacks = ConfigurableField(
id="llm_callbacks",
name="Callbacks",
description="Callbacks for the llm")

).configurable_alternatives(
ConfigurableField(id="llm"),
default_key=SupportedModels.AZURE_CHATGPT.value,
fake= FakeListLLM(responses=["Hi diggi"]))
return llm
16 changes: 4 additions & 12 deletions app/backend/core/types/AppConfig.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,18 @@
from typing import Any, Mapping, TypedDict
from azure.identity.aio import DefaultAzureCredential
from azure.core.credentials import AccessToken
from core.types.AzureChatGPTConfig import AzureChatGPTConfig
from approaches.summarize import Summarize
from approaches.simplechat import SimpleChatApproach
from approaches.chat import ChatApproach
from approaches.brainstorm import Brainstorm
from core.authentification import AuthentificationHelper
from core.types.Config import Config
from core.datahelper import Repository
from core.types.Config import BackendConfig

class OpenaiInfo(TypedDict):
model: str
openai_token: AccessToken
openai_api_key: str
openai_api_base: str
openai_api_version: str
openai_api_type: str

class AppConfig(TypedDict):
model_info: OpenaiInfo
model_info: AzureChatGPTConfig
azure_credential: DefaultAzureCredential
chat_approaches: SimpleChatApproach
chat_approaches: ChatApproach
sum_approaches: Summarize
brainstorm_approaches: Brainstorm
authentification_client: AuthentificationHelper
Expand Down
Loading

0 comments on commit 840503f

Please sign in to comment.