Skip to content

Commit 151faca

Browse files
Merge pull request #162 from MervinPraison/develop
Adding Support to Multimodal Model
2 parents 5c0a86f + d4c2a9a commit 151faca

File tree

7 files changed

+111
-19
lines changed

7 files changed

+111
-19
lines changed

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
FROM python:3.11-slim
22
WORKDIR /app
33
COPY . .
4-
RUN pip install flask praisonai==0.0.72 gunicorn markdown
4+
RUN pip install flask praisonai==0.0.73 gunicorn markdown
55
EXPOSE 8080
66
CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]

docs/api/praisonai/deploy.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ <h2 id="raises">Raises</h2>
110110
file.write(&#34;FROM python:3.11-slim\n&#34;)
111111
file.write(&#34;WORKDIR /app\n&#34;)
112112
file.write(&#34;COPY . .\n&#34;)
113-
file.write(&#34;RUN pip install flask praisonai==0.0.72 gunicorn markdown\n&#34;)
113+
file.write(&#34;RUN pip install flask praisonai==0.0.73 gunicorn markdown\n&#34;)
114114
file.write(&#34;EXPOSE 8080\n&#34;)
115115
file.write(&#39;CMD [&#34;gunicorn&#34;, &#34;-b&#34;, &#34;0.0.0.0:8080&#34;, &#34;api:app&#34;]\n&#39;)
116116

praisonai.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ class Praisonai < Formula
33

44
desc "AI tools for various AI applications"
55
homepage "https://github.com/MervinPraison/PraisonAI"
6-
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/0.0.72.tar.gz"
6+
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/0.0.73.tar.gz"
77
sha256 "1828fb9227d10f991522c3f24f061943a254b667196b40b1a3e4a54a8d30ce32" # Replace with actual SHA256 checksum
88
license "MIT"
99

praisonai/deploy.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def create_dockerfile(self):
5656
file.write("FROM python:3.11-slim\n")
5757
file.write("WORKDIR /app\n")
5858
file.write("COPY . .\n")
59-
file.write("RUN pip install flask praisonai==0.0.72 gunicorn markdown\n")
59+
file.write("RUN pip install flask praisonai==0.0.73 gunicorn markdown\n")
6060
file.write("EXPOSE 8080\n")
6161
file.write('CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]\n')
6262

praisonai/ui/chat.py

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
from tavily import TavilyClient
1818
from crawl4ai import WebCrawler
1919
import asyncio
20+
from PIL import Image
21+
import io
22+
import base64
2023

2124
# Set up logging
2225
logger = logging.getLogger(__name__)
@@ -292,11 +295,32 @@ async def main(message: cl.Message):
292295
message_history = cl.user_session.get("message_history", [])
293296
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
294297

295-
# Add the current date and time to the user's message
298+
# Check if an image was uploaded with this message
299+
image = None
300+
if message.elements and isinstance(message.elements[0], cl.Image):
301+
image_element = message.elements[0]
302+
try:
303+
# Open the image and keep it in memory
304+
image = Image.open(image_element.path)
305+
image.load() # This ensures the file is fully loaded into memory
306+
cl.user_session.set("image", image)
307+
except Exception as e:
308+
logger.error(f"Error processing image: {str(e)}")
309+
await cl.Message(content="There was an error processing the uploaded image. Please try again.").send()
310+
return
311+
312+
# Prepare user message
296313
user_message = f"""
297-
Answer the question and use tools if needed:\n{message.content}.\n\n
314+
Answer the question and use tools if needed:\n
315+
298316
Current Date and Time: {now}
317+
318+
User Question: {message.content}
299319
"""
320+
321+
if image:
322+
user_message = f"Image uploaded. {user_message}"
323+
300324
message_history.append({"role": "user", "content": user_message})
301325

302326
msg = cl.Message(content="")
@@ -309,6 +333,19 @@ async def main(message: cl.Message):
309333
"stream": True,
310334
}
311335

336+
# If an image is uploaded, include it in the message
337+
if image:
338+
buffered = io.BytesIO()
339+
image.save(buffered, format="PNG")
340+
img_str = base64.b64encode(buffered.getvalue()).decode()
341+
342+
completion_params["messages"][-1] = {
343+
"role": "user",
344+
"content": [
345+
{"type": "text", "text": user_message},
346+
{"type": "image_url", "image_url": {"url": f"data:image/png;base64,{img_str}"}}
347+
]
348+
}
312349
# Only add tools and tool_choice if Tavily API key is available
313350
if tavily_api_key:
314351
completion_params["tools"] = tools
@@ -359,6 +396,7 @@ async def main(message: cl.Message):
359396
cl.user_session.set("message_history", message_history)
360397
await msg.update()
361398

399+
# Handle tool calls if any
362400
if tavily_api_key and tool_calls:
363401
available_functions = {
364402
"tavily_web_search": tavily_web_search,
@@ -411,7 +449,7 @@ async def main(message: cl.Message):
411449
msg.content = full_response
412450
await msg.update()
413451
else:
414-
# If no tool calls or Tavily API key is not set, the full_response is already set
452+
# If no tool calls, the full_response is already set
415453
msg.content = full_response
416454
await msg.update()
417455

@@ -433,7 +471,7 @@ async def send_count():
433471
).send()
434472

435473
@cl.on_chat_resume
436-
async def on_chat_resume(thread: ThreadDict): # Change the type hint here
474+
async def on_chat_resume(thread: ThreadDict):
437475
logger.info(f"Resuming chat: {thread['id']}")
438476
model_name = load_setting("model_name") or os.getenv("MODEL_NAME") or "gpt-4o-mini"
439477
logger.debug(f"Model name: {model_name}")
@@ -481,3 +519,10 @@ async def on_chat_resume(thread: ThreadDict): # Change the type hint here
481519
logger.warning(f"Message without recognized type: {message}")
482520

483521
cl.user_session.set("message_history", message_history)
522+
523+
# Check if there's an image in the thread metadata
524+
image_data = metadata.get("image")
525+
if image_data:
526+
image = Image.open(io.BytesIO(base64.b64decode(image_data)))
527+
cl.user_session.set("image", image)
528+
await cl.Message(content="Previous image loaded. You can continue asking questions about it or upload a new image.").send()

praisonai/ui/code.py

Lines changed: 57 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
from tavily import TavilyClient
1919
from datetime import datetime
2020
from crawl4ai import WebCrawler
21+
from PIL import Image
22+
import io
23+
import base64
2124

2225
# Set up logging
2326
logger = logging.getLogger(__name__)
@@ -303,28 +306,65 @@ def tavily_web_search(query):
303306
async def main(message: cl.Message):
304307
model_name = load_setting("model_name") or os.getenv("MODEL_NAME") or "gpt-4o-mini"
305308
message_history = cl.user_session.get("message_history", [])
306-
message_history.append({"role": "user", "content": message.content})
307309
gatherer = ContextGatherer()
308310
context, token_count, context_tree = gatherer.run()
309311
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
310-
prompt_history = message_history
311-
prompt_history.append({"role": "user", "content": """
312-
Answer the question and use tools if needed:\n{question}.\n\n
313-
Current Date and Time: {now}
314-
Below is the Context:\n{context}\n\n"""
315-
.format(context=context, question=message.content, now=now)})
312+
313+
# Check if an image was uploaded with this message
314+
image = None
315+
if message.elements and isinstance(message.elements[0], cl.Image):
316+
image_element = message.elements[0]
317+
try:
318+
# Open the image and keep it in memory
319+
image = Image.open(image_element.path)
320+
image.load() # This ensures the file is fully loaded into memory
321+
cl.user_session.set("image", image)
322+
except Exception as e:
323+
logger.error(f"Error processing image: {str(e)}")
324+
await cl.Message(content="There was an error processing the uploaded image. Please try again.").send()
325+
return
326+
327+
# Prepare user message
328+
user_message = f"""
329+
Answer the question and use tools if needed:\n{message.content}.\n\n
330+
Current Date and Time: {now}
331+
332+
Context:
333+
{context}
334+
"""
335+
336+
if image:
337+
user_message = f"Image uploaded. {user_message}"
338+
339+
message_history.append({"role": "user", "content": user_message})
316340

317341
msg = cl.Message(content="")
318342
await msg.send()
319343

320344
# Prepare the completion parameters
321345
completion_params = {
322346
"model": model_name,
323-
"messages": prompt_history,
347+
"messages": message_history,
324348
"stream": True,
325349
}
326350

327-
# Only add tools and tool_choice if Tavily API key is available
351+
# If an image is uploaded, include it in the message
352+
if image:
353+
buffered = io.BytesIO()
354+
image.save(buffered, format="PNG")
355+
img_str = base64.b64encode(buffered.getvalue()).decode()
356+
357+
completion_params["messages"][-1] = {
358+
"role": "user",
359+
"content": [
360+
{"type": "text", "text": user_message},
361+
{"type": "image_url", "image_url": {"url": f"data:image/png;base64,{img_str}"}}
362+
]
363+
}
364+
# Use a vision-capable model when an image is present
365+
completion_params["model"] = "gpt-4-vision-preview" # Adjust this to your actual vision-capable model
366+
367+
# Only add tools and tool_choice if Tavily API key is available and no image is uploaded
328368
if tavily_api_key:
329369
completion_params["tools"] = tools
330370
completion_params["tool_choice"] = "auto"
@@ -380,7 +420,7 @@ async def main(message: cl.Message):
380420
available_functions = {
381421
"tavily_web_search": tavily_web_search,
382422
}
383-
messages = prompt_history + [{"role": "assistant", "content": None, "function_call": {
423+
messages = message_history + [{"role": "assistant", "content": None, "function_call": {
384424
"name": tool_calls[0]['function']['name'],
385425
"arguments": tool_calls[0]['function']['arguments']
386426
}}]
@@ -497,3 +537,10 @@ async def on_chat_resume(thread: ThreadDict):
497537
logger.warning(f"Message without recognized type: {message}")
498538

499539
cl.user_session.set("message_history", message_history)
540+
541+
# Check if there's an image in the thread metadata
542+
image_data = metadata.get("image")
543+
if image_data:
544+
image = Image.open(io.BytesIO(base64.b64decode(image_data)))
545+
cl.user_session.set("image", image)
546+
await cl.Message(content="Previous image loaded. You can continue asking questions about it, upload a new image, or just chat.").send()

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "PraisonAI"
3-
version = "0.0.72"
3+
version = "0.0.73"
44
description = "PraisonAI application combines AutoGen and CrewAI or similar frameworks into a low-code solution for building and managing multi-agent LLM systems, focusing on simplicity, customization, and efficient human-agent collaboration."
55
authors = ["Mervin Praison"]
66
license = ""

0 commit comments

Comments
 (0)