GianJSX commited on
Commit
ebe3bf0
Β·
1 Parent(s): 34d0c86

Upload 6 files

Browse files
Files changed (6) hide show
  1. .chainlit/.langchain.db +0 -0
  2. .chainlit/config.toml +63 -0
  3. Dockerfile +11 -0
  4. app.py +146 -0
  5. requirements.txt +4 -0
  6. tools.py +132 -0
.chainlit/.langchain.db ADDED
Binary file (12.3 kB). View file
 
.chainlit/config.toml ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [project]
2
+ # If true (default), the app will be available to anonymous users.
3
+ # If false, users will need to authenticate and be part of the project to use the app.
4
+ public = true
5
+
6
+ # The project ID (found on https://cloud.chainlit.io).
7
+ # The project ID is required when public is set to false or when using the cloud database.
8
+ #id = ""
9
+
10
+ # Uncomment if you want to persist the chats.
11
+ # local will create a database in your .chainlit directory (requires node.js installed).
12
+ # cloud will use the Chainlit cloud database.
13
+ # custom will load use your custom client.
14
+ # database = "local"
15
+
16
+ # Whether to enable telemetry (default: true). No personal data is collected.
17
+ enable_telemetry = false
18
+
19
+ # List of environment variables to be provided by each user to use the app.
20
+ user_env = []
21
+
22
+ # Duration (in seconds) during which the session is saved when the connection is lost
23
+ session_timeout = 3600
24
+
25
+ [UI]
26
+ # Name of the app and chatbot.
27
+ name = "Chatbot"
28
+
29
+ # Description of the app and chatbot. This is used for HTML tags.
30
+ # description = ""
31
+
32
+ # The default value for the expand messages settings.
33
+ default_expand_messages = false
34
+
35
+ # Hide the chain of thought details from the user in the UI.
36
+ hide_cot = false
37
+
38
+ # Link to your github repo. This will add a github button in the UI's header.
39
+ # github = ""
40
+
41
+ # Override default MUI light theme. (Check theme.ts)
42
+ [UI.theme.light]
43
+ #background = "#FAFAFA"
44
+ #paper = "#FFFFFF"
45
+
46
+ [UI.theme.light.primary]
47
+ #main = "#F80061"
48
+ #dark = "#980039"
49
+ #light = "#FFE7EB"
50
+
51
+ # Override default MUI dark theme. (Check theme.ts)
52
+ [UI.theme.dark]
53
+ #background = "#FAFAFA"
54
+ #paper = "#FFFFFF"
55
+
56
+ [UI.theme.dark.primary]
57
+ #main = "#F80061"
58
+ #dark = "#980039"
59
+ #light = "#FFE7EB"
60
+
61
+
62
+ [meta]
63
+ generated_by = "0.6.2"
Dockerfile ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11
2
+ RUN useradd -m -u 1000 user
3
+ USER user
4
+ ENV HOME=/home/user \
5
+ PATH=/home/user/.local/bin:$PATH
6
+ WORKDIR $HOME/app
7
+ COPY --chown=user . $HOME/app
8
+ COPY ./requirements.txt ~/app/requirements.txt
9
+ RUN pip install -r requirements.txt
10
+ COPY . .
11
+ CMD ["chainlit", "run", "app.py", "--port", "7860"]
app.py ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langchain.agents import AgentExecutor, AgentType, initialize_agent
2
+ from langchain.agents.structured_chat.prompt import SUFFIX
3
+ from langchain.chat_models import ChatOpenAI
4
+ from langchain.memory import ConversationBufferMemory
5
+ from tools import generate_image_tool, describe_image_tool, gpt_vision_call, process_images, handle_image_history
6
+
7
+ import chainlit as cl
8
+ from chainlit.action import Action
9
+ from chainlit.input_widget import Select, Switch, Slider
10
+
11
+
12
+ #@cl.author_rename
13
+ def rename(orig_author):
14
+ """
15
+ Rename the author of messages as displayed in the "Thinking" section.
16
+
17
+ This is useful to make the chat look more natural, or add some fun to it!
18
+ """
19
+ mapping = {
20
+ "AgentExecutor": "The LLM Brain",
21
+ "LLMChain": "The Assistant",
22
+ "GenerateImage": "DALL-E 3",
23
+ "ChatOpenAI": "GPT-4 Turbo",
24
+ "Chatbot": "Coolest App",
25
+ }
26
+ return mapping.get(orig_author, orig_author)
27
+
28
+
29
+ @cl.cache
30
+ def get_memory():
31
+ """
32
+ This is used to track the conversation history and allow our agent to
33
+ remember what was said before.
34
+ """
35
+ return ConversationBufferMemory(memory_key="chat_history")
36
+
37
+
38
+ @cl.on_chat_start
39
+ async def start():
40
+ """
41
+ This is called when the Chainlit chat is started!
42
+
43
+ We can add some settings to our application to allow users to select the appropriate model, and more!
44
+ """
45
+ cl.user_session.set("image_history", [{"role": "system", "content": "You are a helpful assistant. You are developed with GPT-4-vision-preview, if the user uploads an image, you have the ability to understand it."}])
46
+
47
+ settings = await cl.ChatSettings(
48
+ [
49
+ Select(
50
+ id="Model",
51
+ label="OpenAI - Model",
52
+ values=["gpt-3.5-turbo", "gpt-4-1106-preview"],
53
+ initial_index=1,
54
+ ),
55
+ Switch(id="Streaming", label="OpenAI - Stream Tokens", initial=True),
56
+ Slider(
57
+ id="Temperature",
58
+ label="OpenAI - Temperature",
59
+ initial=0,
60
+ min=0,
61
+ max=2,
62
+ step=0.1,
63
+ ),
64
+ ]
65
+ ).send()
66
+ await setup_agent(settings)
67
+
68
+
69
+ @cl.on_settings_update
70
+ async def setup_agent(settings):
71
+ print("Setup agent with following settings: ", settings)
72
+
73
+ # We set up our agent with the user selected (or default) settings here.
74
+ llm = ChatOpenAI(
75
+ temperature=settings["Temperature"],
76
+ streaming=settings["Streaming"],
77
+ model=settings["Model"],
78
+ )
79
+
80
+ # We get our memory here, which is used to track the conversation history.
81
+ memory = get_memory()
82
+
83
+ # This suffix is used to provide the chat history to the prompt.
84
+ _SUFFIX = "Chat history:\n{chat_history}\n\n" + SUFFIX
85
+
86
+ # We initialize our agent here, which is simply being used to decide between responding with text
87
+ # or an image
88
+ agent = initialize_agent(
89
+ llm=llm, # our LLM (default is GPT-4 Turbo)
90
+ tools=[
91
+ generate_image_tool,
92
+ describe_image_tool,
93
+ ], # our custom tool used to generate images with DALL-E 3
94
+ agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION, # the agent type we're using today
95
+ memory=memory, # our memory!
96
+ agent_kwargs={
97
+ "suffix": _SUFFIX, # adding our chat history suffix
98
+ "input_variables": ["input", "agent_scratchpad", "chat_history"],
99
+ },
100
+ )
101
+ cl.user_session.set("agent", agent) # storing our agent in the user session
102
+
103
+
104
+ @cl.on_message
105
+ async def main(message: cl.Message):
106
+ """
107
+ This function is going to intercept all messages sent by the user, and
108
+ move through our agent flow to generate a response.
109
+
110
+ There are ultimately two different options for the agent to respond with:
111
+ 1. Text
112
+ 2. Image
113
+
114
+ If the agent responds with text, we simply send the text back to the user.
115
+
116
+ If the agent responds with an image, we need to generate the image and send
117
+ it back to the user.
118
+ """
119
+
120
+ if message.elements:
121
+ cl.user_session.set("image_id", message.elements[0].name)
122
+ handle_image_history(message)
123
+ message.content = message.content + ". image_id: " + message.elements[0].name
124
+
125
+ agent = cl.user_session.get("agent")
126
+ cl.user_session.set("generated_image", None)
127
+
128
+ res = await cl.make_async(agent.run)(
129
+ input=message.content, callbacks=[cl.LangchainCallbackHandler()]
130
+ )
131
+
132
+ elements = []
133
+ actions = []
134
+
135
+ generated_image_name = cl.user_session.get("generated_image")
136
+ generated_image = cl.user_session.get(generated_image_name)
137
+ if generated_image:
138
+ elements = [
139
+ cl.Image(
140
+ content=generated_image,
141
+ name=generated_image_name,
142
+ display="inline",
143
+ )
144
+ ]
145
+
146
+ await cl.Message(content=res, elements=elements, actions=actions).send()
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ langchain
2
+ tiktoken
3
+ chainlit
4
+ openai
tools.py ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import io
2
+ import os
3
+ from openai import OpenAI
4
+ from langchain.tools import StructuredTool, Tool
5
+ from io import BytesIO
6
+ import requests
7
+ import json
8
+ from io import BytesIO
9
+ import base64
10
+ import chainlit as cl
11
+
12
+
13
+ def get_image_name():
14
+ """
15
+ We need to keep track of images we generate, so we can reference them later
16
+ and display them correctly to our users.
17
+ """
18
+ image_count = cl.user_session.get("image_count")
19
+ if image_count is None:
20
+ image_count = 0
21
+ else:
22
+ image_count += 1
23
+
24
+ cl.user_session.set("image_count", image_count)
25
+
26
+ return f"image-{image_count}"
27
+
28
+
29
+ def _generate_image(prompt: str):
30
+ """
31
+ This function is used to generate an image from a text prompt using
32
+ DALL-E 3.
33
+
34
+ We use the OpenAI API to generate the image, and then store it in our
35
+ user session so we can reference it later.
36
+ """
37
+ client = OpenAI()
38
+
39
+ response = client.images.generate(
40
+ model="dall-e-3",
41
+ prompt=prompt,
42
+ size="1024x1024",
43
+ quality="standard",
44
+ n=1,
45
+ )
46
+
47
+ image_payload = requests.get(response.data[0].url, stream=True)
48
+
49
+ image_bytes = BytesIO(image_payload.content)
50
+
51
+ print(type(image_bytes))
52
+
53
+ name = get_image_name()
54
+ cl.user_session.set(name, image_bytes.getvalue())
55
+ cl.user_session.set("generated_image", name)
56
+ return name
57
+
58
+
59
+ def generate_image(prompt: str):
60
+ image_name = _generate_image(prompt)
61
+ return f"Here is {image_name}."
62
+
63
+
64
+ # this is our tool - which is what allows our agent to generate images in the first place!
65
+ # the `description` field is of utmost imporance as it is what the LLM "brain" uses to determine
66
+ # which tool to use for a given input.
67
+ generate_image_format = '{{"prompt": "prompt"}}'
68
+ generate_image_tool = Tool.from_function(
69
+ func=generate_image,
70
+ name="GenerateImage",
71
+ description=f"Useful to create an image from a text prompt. Input should be a single string strictly in the following JSON format: {generate_image_format}",
72
+ return_direct=True,
73
+ )
74
+
75
+
76
+ def gpt_vision_call(image_id: str):
77
+ #cl.user_session.set("image_id", image_id)
78
+ print("image_id", image_id)
79
+ client = OpenAI()
80
+ image_history = cl.user_session.get("image_history")
81
+ stream = client.chat.completions.create(
82
+ model="gpt-4-vision-preview",
83
+ messages=image_history,
84
+ max_tokens=350,
85
+ stream=False,
86
+ )
87
+
88
+ return stream
89
+
90
+ def handle_image_history(msg):
91
+ image_history = cl.user_session.get("image_history")
92
+ image_base64 = None
93
+ image_base64 = process_images(msg)
94
+
95
+ if image_base64:
96
+ # add the image to the image history
97
+ image_history.append(
98
+ {
99
+ "role": "user",
100
+ "content": [
101
+ {"type": "text", "text": msg.content},
102
+ {
103
+ "type": "image_url",
104
+ "image_url": {
105
+ "url": f"data:image/jpeg;base64,{image_base64}",
106
+ "detail": "low"
107
+ }
108
+ },
109
+ ],
110
+ }
111
+ )
112
+ cl.user_session.set("image_history", image_history)
113
+
114
+
115
+ def process_images(msg: cl.Message):
116
+ # Processing images exclusively
117
+ images = [file for file in msg.elements if "image" in file.mime]
118
+
119
+ # Accessing the bytes of a specific image
120
+ image_bytes = images[0].content # take the first image just for demo purposes
121
+
122
+ # we need base64 encoded image
123
+ image_base64 = base64.b64encode(image_bytes).decode('utf-8')
124
+ return image_base64
125
+
126
+ describe_image_format = '{{"image_id": "image_id"}}'
127
+ describe_image_tool = Tool.from_function(
128
+ func=gpt_vision_call,
129
+ name="DescribeImage",
130
+ description=f"Useful to describe an image. Input should be a single string strictly in the following JSON format: {describe_image_format}",
131
+ return_direct=False,
132
+ )