Тепер запускати LLM локально вже не новинка. Інструменти, як Ollama та LM Studio, зробили запуск LLM безперешкодним прямо на наших локальних машинах. Ці інструменти закривають розрив між складними технологіями ШІ та зручністю використання.
У цій статті ми розглянемо, як створити цікавий чат-бот на основі LLM за допомогою бібліотеки ollama-python, Streamlit та MongoDB. Протягом цього процесу ми:
- Розробимо простий додаток на Streamlit, який демонструє чат-компоненти Streamlit, що взаємодіють з Ollama.
- Дослідимо стан сеансу Streamlit для управління контекстом розмови.
- Додамо шар зберігання в MongoDB для збереження та отримання користувацьких розмов.
Давайте почнемо!
🧱 Створення основи
Перед тим як почати з кодом, переконайтеся, що у вас встановлений Ollama на вашій машині. Якщо ви ще не встановили його, відвідайте офіційний сайт Ollama для інструкцій з установки. Також потрібно завантажити модель мови на ваш вибір. У цьому посібнику ми будемо використовувати llama3.2:3b
.
Далі вам потрібно встановити кілька python-бібліотек. Вам знадобиться Streamlit для побудови інтерфейсу користувача та ollama-python package для взаємодії з локальним LLM.
Щоб їх встановити, виконайте наступні команди:
pip install streamlit
pip install ollama
Після завершення встановлення ви готові перейти до написання коду.
Розпочнемо з імпорту необхідних модулів:
import streamlit as st
from ollama import chat
Далі визначимо функцію під назвою get_response
, яка буде основною для взаємодії з LLM протягом усієї програми:
def get_response(messages, model='llama3.2:3b'):
try:
response = chat(model, messages=messages, stream=False)
return response['message']['content']
except Exception as e:
st.error(f"Error: {e}")
response = "Sorry, I couldn't process your request."
return response
Примітка: Як ми будемо просуватися, ми створимо нову функцію під назвою
get_streaming_response
, яка дозволить нам отримувати потокові відповіді від LLM.
Щоб налаштувати основу, додайте заголовок до вашого додатка:
st.title("🦙 Llama Chat")
Тепер використаємо два елементи чату Streamlit — st.chat_input
і st.chat_message
. Ці компоненти виконують основну роботу з створення інтерфейсу чату:
st.chat_message
відображає повідомлення від користувача або помічника.st.chat_input
надає віджет для введення повідомлень користувачем.
Для більш детальної інформації зверніться до документації Streamlit.
🏄 Створення простого потоку вводу та відображення
Першим кроком створимо текстове поле для вводу повідомлень користувача:
user_input = st.chat_input("Write your message here")
Цей ввід зберігається у змінній user_input
.
Далі обробимо ввід користувача, відобразимо його як повідомлення користувача, надішлемо його до LLM і відобразимо відповідь помічника:
if user_input:
# Display the user's message
with st.chat_message("user"):
st.markdown(user_input)
# Get and display the assistant's response
with st.chat_message("assistant"):
messages = [{'role': 'user', 'content': user_input}]
# LLM call
assistant_response = get_response(messages)
st.markdown(assistant_response)
📌 Об'єднуємо все разом
Давайте об'єднаємо весь код у файл і назвемо його simple_chat.py
import streamlit as st
from ollama import chat
def get_response(messages, model='llama3.2:3b'):
try:
response = chat(model, messages=messages, stream=False)
return response['message']['content']
except Exception as e:
st.error(f"Error: {e}")
response = "Sorry, I couldn't process your request."
return response
st.title("🦙 Llama Chat")
# Create a text input for the user
user_input = st.chat_input("Write your message here")
if user_input:
# Display the user's message
with st.chat_message("user"):
st.markdown(user_input)
# Get and display the assistant's response
with st.chat_message("assistant"):
messages = [{'role': 'user', 'content': user_input}]
assistant_response = get_response(messages)
st.markdown(assistant_response)
На цьому етапі додаток чудово працює для базових взаємодій. Однак є суттєве обмеження: повідомлення (як введення користувача, так і відповіді LLM) замінюються при кожній новій взаємодії. Це означає, що чат не зберігає історію розмов.
💾 Збереження розмов у Streamlit за допомогою Session State
Щоб вирішити це питання, ми можемо скористатися session state Streamlit. Session state дозволяє зберігати дані розмов для тривалості сесії додатка, дозволяючи відображати безперервний потік діалогу.
✨ Оновлюємо код для збереження історії чату
Оновимо додаток.
1. Ініціалізуємо Session State
Почнемо з того, що перевіримо наявність змінної session state під назвою messages
, щоб зберігати історію чату.
if "messages" not in st.session_state:
st.session_state.messages = []
Цей крок важливий — він ініціалізує порожній список для зберігання повідомлень поточної сесії, якщо він ще не був встановлений.
2. Відображення повідомлень у інтерфейсі чату
Далі ми проходимо по збережених messages
і відображаємо кожне повідомлення в інтерфейсі чату.
for message in st.session_state.messages:
with st.chat_message(message["role"]):
st.markdown(message["content"])
Це забезпечує відображення всієї історії розмов у будь-який момент. Кожне повідомлення асоціюється з певною роллю (або user, або assistant) і оформлюється відповідним чином у інтерфейсі чату.
3.
Динамічне зберігання повідомлень
Тепер ми підвищимо інтерактивність чату, динамічно зберігаючи повідомлення в session state.
- Після того як користувач надає повідомлення, воно додається до списку
messages
:
st.session_state.messages.append({"role": "user", "content": user_input})
- Аналогічно, коли LLM генерує відповідь, ми зберігаємо її в session state:
st.session_state.messages.append({"role": "assistant", "content": assistant_response})
📌 Об'єднуємо все разом
Збережемо повний код з усіма вищезгаданими змінами в файл chat_with_session_state.py
.
import streamlit as st
from ollama import chat
def get_response(messages, model='llama3.2:3b'):
try:
response = chat(model, messages=messages, stream=False)
return response['message']['content']
except Exception as e:
st.error(f"Error: {e}")
response = "Sorry, I couldn't process your request."
return response
st.title("🦙 Llama Chat")
# Ініціалізуємо session state для збереження повідомлень
if "messages" not in st.session_state:
st.session_state.messages = []
# Відображаємо всі повідомлення з session state
for message in st.session_state.messages:
with st.chat_message(message["role"]):
st.markdown(message["content"])
# Створюємо текстовий ввід для користувача
user_input = st.chat_input("Напишіть ваше повідомлення тут")
if user_input:
# Відображаємо повідомлення користувача
with st.chat_message("user"):
st.markdown(user_input)
# зберігаємо повідомлення користувача
st.session_state.messages.append({"role": "user", "content": user_input})
# Отримуємо і відображаємо відповідь помічника
with st.chat_message("assistant"):
messages = [{'role': 'user', 'content': user_input}]
assistant_response = get_response(messages)
st.markdown(assistant_response)
# зберігаємо повідомлення помічника
st.session_state.messages.append({"role": "assistant", "content": assistant_response})
На цьому етапі додаток працює відмінно для базових взаємодій. Однак є суттєве обмеження: повідомлення (як ввід користувача, так і відповіді LLM) замінюються при кожній новій взаємодії. Це означає, що чат не зберігає історію розмов.
💾 Збереження розмов у Streamlit за допомогою Session State
Щоб вирішити це питання, ми можемо скористатися session state Streamlit. Session state дозволяє зберігати дані розмов для тривалості сесії додатка, дозволяючи відображати безперервний потік діалогу.
✨ Оновлюємо код для збереження історії чату
Оновимо додаток.
1. Ініціалізуємо Session State
Почнемо з того, що перевіримо наявність змінної session state під назвою messages
, щоб зберігати історію чату.
if "messages" not in st.session_state:
st.session_state.messages = []
Цей крок важливий — він ініціалізує порожній список для зберігання повідомлень поточної сесії, якщо він ще не був встановлений.
2. Відображення повідомлень у інтерфейсі чату
Далі ми проходимо по збережених messages
і відображаємо кожне повідомлення в інтерфейсі чату.
for message in st.session_state.messages:
with st.chat_message(message["role"]):
st.markdown(message["content"])
Це забезпечує відображення всієї історії розмов у будь-який момент. Кожне повідомлення асоціюється з певною роллю (або user, або assistant) і оформлюється відповідним чином у інтерфейсі чату.
3.
Docker забезпечує чисте середовище та полегшує масштабування і підтримку додатка на різних системах, чи це MacOS, Linux, чи Windows.
Ми будемо використовувати docker-compose для запуску контейнера MongoDB.
🚀 Налаштування MongoDB з Docker Compose
Спочатку створіть файл з ім'ям docker-compose.yml
у вашій робочій директорії та вставте наступний вміст:
services:
mongodb:
image: mongo:latest
container_name: mongodb
ports:
- "27017:27017"
volumes:
- mongo-data:/data/db
volumes:
mongo-data:
Цей файл docker-compose.yml визначає конфігурацію для сервісу MongoDB. Розглянемо кожну секцію конфігурації:
- services: Описує сервіси, які буде запускати Docker Compose. У нашому випадку ми визначаємо один сервіс —
mongodb
. - mongodb: Це ім’я нашого сервісу.
- image: mongo:latest: Вказує на Docker образ, який використовується для сервісу MongoDB. Образ
mongo:latest
відноситься до останньої офіційної версії MongoDB. - container_name: mongodb: Призначає контейнеру ім’я
mongodb
, що спрощує його керування та звертання до нього. - ports: Прив'язує порт MongoDB (27017) всередині контейнера до того ж порту на хост-машині. Це дозволяє отримати доступ до екземпляра MongoDB на стандартному порту.
"27017:27017"
: Забезпечує зв'язок хост-машини з MongoDB через порт 27017 — стандартний порт MongoDB.- volumes: Монтуює том
mongo-data
в контейнер MongoDB за шляхом/data/db
, де MongoDB зберігає свої файли бази даних. mongo-data:/data/db
: Це змонтує іменований томmongo-data
до каталогу/data/db
всередині контейнера.- volumes: Ця секція визначає постійний том, який використовується контейнером MongoDB.
- mongo-data: Том
mongo-data
забезпечує збереження даних MongoDB поза файловою системою контейнера.
Монтуючи том mongo-data
до каталогу /data/db
всередині контейнера MongoDB, ми забезпечуємо, щоб MongoDB зберігала свої дані в постійному місці. Це гарантує, що наш додаток чату може отримувати історії розмов навіть після перезавантаження або відтворення контейнера MongoDB.
Тепер, коли ми налаштували MongoDB за допомогою Docker Compose та зрозуміли важливість постійного зберігання, наступним кроком є інтеграція бази даних з нашим додатком Streamlit для безперешкодного зберігання та отримання повідомлень чату.
🏗 Інтеграція MongoDB для зберігання чатів
Тепер, коли Docker запустив MongoDB у фоновому режимі, ми можемо змінити код додатка, щоб зберігати повідомлення чату в базі даних.
🎬 Запуск MongoDB з Docker
Для запуску контейнера MongoDB за допомогою конфігурації docker-compose запустіть наступну команду:
docker-compose up -d
Ця команда запустить контейнер MongoDB в режимі відокремленого виконання. Тепер, коли MongoDB працює, ми готові інтегрувати її в наш додаток.
📥 Встановлення pymongo для інтеграції MongoDB
Перш ніж ми зможемо взаємодіяти з MongoDB, нам потрібно встановити клієнт pymongo, який забезпечує доступ до MongoDB з Python. Для встановлення pymongo виконайте наступну команду:
pip install pymongo
Після встановлення ми зможемо використовувати MongoClient
для підключення до бази даних MongoDB.
⚙️ Налаштування підключення до MongoDB
У вашому Python коді імпортуйте MongoClient
з pymongo для встановлення з'єднання з базою даних MongoDB:
from pymongo import MongoClient
Тепер налаштуємо з'єднання з екземпляром MongoDB, створивши клієнта та вибравши базу даних:
client = MongoClient("mongodb://localhost:27017/")
db = client.chat_db
Ось що відбувається:
MongoClient("mongodb://localhost:27017/")
: Підключає до екземпляра MongoDB, що працює наlocalhost
на стандартному порту MongoDB (27017).db = client.chat_db
: Отримує доступ до бази данихchat_db
.
Docker забезпечує, щоб ми мали чисте середовище та полегшує масштабування і підтримку додатка на різних системах, чи це MacOS, Linux, чи Windows.
Ми будемо використовувати docker-compose для запуску контейнера MongoDB.
🚀 Налаштування MongoDB з Docker Compose
Спочатку створіть файл з ім'ям docker-compose.yml
у вашій робочій директорії та вставте наступний вміст:
services:
mongodb:
image: mongo:latest
container_name: mongodb
ports:
- "27017:27017"
volumes:
- mongo-data:/data/db
volumes:
mongo-data:
Цей файл docker-compose.yml визначає конфігурацію для сервісу MongoDB. Розглянемо кожну секцію конфігурації:
- services: Описує сервіси, які буде запускати Docker Compose. У нашому випадку ми визначаємо один сервіс —
mongodb
. - mongodb: Це ім’я нашого сервісу.
- image: mongo:latest: Вказує на Docker образ, який використовується для сервісу MongoDB. Образ
mongo:latest
відноситься до останньої офіційної версії MongoDB. - container_name: mongodb: Призначає контейнеру ім’я
mongodb
, що спрощує його керування та звертання до нього. - ports: Прив'язує порт MongoDB (27017) всередині контейнера до того ж порту на хост-машині. Це дозволяє отримати доступ до екземпляра MongoDB на стандартному порту.
"27017:27017"
: Забезпечує зв'язок хост-машини з MongoDB через порт 27017 — стандартний порт MongoDB.- volumes: Монтуює том
mongo-data
в контейнер MongoDB за шляхом/data/db
, де MongoDB зберігає свої файли бази даних. mongo-data:/data/db
: Це змонтує іменований томmongo-data
до каталогу/data/db
всередині контейнера.- volumes: Ця секція визначає постійний том, який використовується контейнером MongoDB.
- mongo-data: Том
mongo-data
забезпечує збереження даних MongoDB поза файловою системою контейнера.
Монтуючи том mongo-data
до каталогу /data/db
всередині контейнера MongoDB, ми забезпечуємо, щоб MongoDB зберігала свої дані в постійному місці. Це гарантує, що наш додаток чату може отримувати історії розмов навіть після перезавантаження або відтворення контейнера MongoDB.
Тепер, коли ми налаштували MongoDB за допомогою Docker Compose та зрозуміли важливість постійного зберігання, наступним кроком є інтеграція бази даних з нашим додатком Streamlit для безперешкодного зберігання та отримання повідомлень чату.
🏗 Інтеграція MongoDB для зберігання чатів
Тепер, коли Docker запустив MongoDB у фоновому режимі, ми можемо змінити код додатка, щоб зберігати повідомлення чату в базі даних.
🎬 Запуск MongoDB з Docker
Для запуску контейнера MongoDB за допомогою конфігурації docker-compose запустіть наступну команду:
docker-compose up -d
Ця команда запустить контейнер MongoDB в режимі відокремленого виконання. Тепер, коли MongoDB працює, ми готові інтегрувати її в наш додаток.
📥 Встановлення pymongo для інтеграції MongoDB
Перш ніж ми зможемо взаємодіяти з MongoDB, нам потрібно встановити клієнт pymongo, який забезпечує доступ до MongoDB з Python. Для встановлення pymongo виконайте наступну команду:
pip install pymongo
Після встановлення ми зможемо використовувати MongoClient
для підключення до бази даних MongoDB.
⚙️ Налаштування підключення до MongoDB
У вашому Python коді імпортуйте MongoClient
з pymongo для встановлення з'єднання з базою даних MongoDB:
from pymongo import MongoClient
Тепер налаштуємо з'єднання з екземпляром MongoDB, створивши клієнта та вибравши базу даних:
client = MongoClient("mongodb://localhost:27017/")
db = client.chat_db
Ось що відбувається:
MongoClient("mongodb://localhost:27017/")
: Підключає до екземпляра MongoDB, що працює наlocalhost
на стандартному порту MongoDB (27017).db = client.chat_db
: Отримує доступ до бази данихchat_db
.
Бічна панель надає зручний спосіб для користувачів вибирати та продовжувати попередні чати, а також починати нові.
🛣️ Створення бічної панелі для відображення попередніх чатів
Щоб дозволити користувачам швидко перемикатися між попередніми чатами, ми відобразимо список існуючих чат-сесій на бічній панелі. Це здійснюється за допомогою випадаючого списку, який отримує ID чат-сесій з бази даних.
st.sidebar.title("Попередні чати")
chat_ids = get_all_chat_ids()
selected_chat_id = st.sidebar.selectbox("Виберіть чат", chat_ids)
if selected_chat_id:
st.session_state.chat_id = selected_chat_id.split()[0] # Виділяємо частину UUID
Функціональність:
- Отримання ID чатів: Викликається функція
get_all_chat_ids()
, щоб отримати всі доступні ID чатів з колекціїchat_metadata
в базі даних. - Вибір з випадаючого списку: Випадаючий список відображається на бічній панелі, показуючи список доступних ID чатів. Це дозволяє користувачу вибрати конкретну чат-сесію.
- Оновлення Session State: Коли користувач вибирає чат-сесію,
chat_id
в session state оновлюється до вибраного UUID (першої частини вибраного ID чату).
👆 Кнопка для створення нового чату
Окрім відображення попередніх чатів, на бічній панелі буде також кнопка для створення нових чат-сесій. Коли кнопка натискається, створюється нова чат-сесія, і користувач буде перенаправлений до нового чату.
if st.sidebar.button("Новий чат"):
st.session_state.chat_id = create_new_chat()
st.rerun() # Перезавантаження сторінки, щоб відобразити новий чат
Ця кнопка дає користувачам можливість почати нову розмову. Натискаючи її, створюється нова чат-сесія, і додаток перезавантажується для відображення новоствореного чату.
Функціональність:
- Кнопка: Відображає кнопку "Новий чат" на бічній панелі.
- Створення нового чату: Коли кнопка натискається, викликається функція
create_new_chat()
, яка генерує нову чат-сесію. Новийchat_id
зберігається в session state. - Перезавантаження сторінки: Сторінка перезавантажується за допомогою
st.rerun()
, щоб відобразити нову чат-сесію.
👉 Перевірка наявності chat_id
в Session State
Щоб забезпечити нормальну роботу додатка та гарантувати, що кожен чат має валідний ідентифікатор, ми перевіряємо, чи існує chat_id
в session state. Якщо його немає, створюється нова чат-сесія.
if "chat_id" not in st.session_state:
st.session_state.chat_id = create_new_chat()
st.rerun() # Перезавантаження сторінки, щоб відобразити новий чат
Ця перевірка гарантує, що chat_id
завжди присутнє в session state. Якщо чат ID відсутнє, додаток автоматично створює новий чат і перезавантажує сторінку для відображення цього.
Функціональність:
- Перевірка Session State: Код перевіряє, чи існує
chat_id
в session state. - Створення нового чату: Якщо
chat_id
не знайдено, викликається функціяcreate_new_chat()
, яка створює нову чат-сесію і зберігаєchat_id
в session state. - Перезавантаження сторінки: Після встановлення
chat_id
сторінка перезавантажується для відображення нової чат-сесії.
📋 Отримання та відображення повідомлень
Тепер, коли ми налаштували бічну панель для вибору чатів та створення нових, ми можемо зосередитися на відображенні повідомлень чату. Ця частина коду отримує всі повідомлення для поточного chat_id
і відображає їх користувачу.
chat_id = st.session_state.chat_id
# Отримуємо всі повідомлення для поточного chat_id з бази даних
messages = get_all_messages(chat_id)
# Відображаємо всі повідомлення з бази даних
for message in messages:
with st.chat_message(message["role"]):
st.markdown(message["content"])
Цей код отримує всі повідомлення для поточної чат-сесії та відображає їх у вигляді інтерфейсу чату. Функція st.chat_message()
використовується для відображення кожного повідомлення, забезпечуючи, щоб UI нагадував типовий інтерфейс чату.
Функціональність:
- Отримання chat_id:
chat_id
отримується з session state, що забезпечує роботу з правильним чатом. - Отримання повідомлень: Функція запитує базу даних для всіх повідомлень. Вона проектує лише поля
role
таcontent
(не включаючи поле_id
), щоб отримати фактичні дані розмови. - Відображення повідомлень: Результат повертається у вигляді списку повідомлень, які можна використовувати для відображення історії чату в додатку.
Отримання повідомлень: Викликається функціяget_all_messages(chat_id)
, щоб отримати всі повідомлення для поточної чат-сесії з бази даних. - Відображення повідомлень: Кожне повідомлення перебирається та відображається за допомогою
st.chat_message()
. Вміст повідомлення рендериться за допомогоюst.markdown()
, щоб забезпечити багатоформатне відображення.
🎲 Досвід користувача
Інтегруючи бічну панель і функціональність відображення повідомлень, чат-додаток стає інтуїтивно зрозумілим та зручним для користувачів.
З цією конфігурацією ви можете почати створювати інтерфейс чату, при цьому всі взаємодії користувачів зберігаються в MongoDB, що забезпечує безперебійний досвід навіть після перезавантаження або перезапуску додатка.
👷♂️ Модифікації в коді Streamlit для потокової передачі
Щоб відображати потокові відповіді динамічно, ми модифікуємо код Streamlit всередині блоку повідомлень асистента. Замість того, щоб відображати всю відповідь одразу, ми додаємо кожен шматок потокової відповіді в реальному часі.
🤖 Потокова відповідь асистента
Основне оновлення коду — це інтеграція потокових відповідей від асистента, що забезпечує більш динамічний і реальний досвід спілкування. Це досягається зміною способу отримання та відображення відповіді асистента.
with st.chat_message("assistant"):
response = st.write_stream(get_streaming_response(messages))
for part in response:
response_content += part
get_streaming_response(messages)
: Оновлена функція потоком отримує відповідь асистента частинами (шматками), замість того щоб повертати всю відповідь одразу. Вона використовує генератор для динамічного видавання кожної частини відповіді.st.write_stream(response)
: Функціяst.write_stream()
обробляє потоки частин у реальному часі і поступово рендерить їх у блоці чату асистента.response_content += part
: Коли частини відповіді потоком надходять, вони додаються доresponse_content
, щоб сформувати повну відповідь.
Збереження повідомлень: Функціяget_all_messages(chat_id)
викликається для отримання всіх повідомлень для поточної чат-сесії з бази даних.- Відображення повідомлень: Кожне повідомлення перебирається та відображається за допомогою
st.chat_message()
. Вміст повідомлення рендериться за допомогоюst.markdown()
, що забезпечує багатоформатне відображення.
🎲 Досвід користувача
Інтегруючи бічну панель і функціональність відображення повідомлень, чат-додаток стає інтуїтивно зрозумілим та зручним для користувачів.
З цією конфігурацією ви можете почати створювати інтерфейс чату, при цьому всі взаємодії користувачів зберігаються в MongoDB, що забезпечує безперебійний досвід навіть після перезавантаження або перезапуску додатка.
👷♂️ Модифікації в коді Streamlit для потокової передачі
Щоб відображати потокові відповіді динамічно, ми модифікуємо код Streamlit всередині блоку повідомлень асистента. Замість того, щоб відображати всю відповідь одразу, ми додаємо кожен шматок потокової відповіді в реальному часі.
🤖 Потокова відповідь асистента
Основне оновлення коду — це інтеграція потокових відповідей від асистента, що забезпечує більш динамічний і реальний досвід спілкування. Це досягається зміною способу отримання та відображення відповіді асистента.
with st.chat_message("assistant"):
response = st.write_stream(get_streaming_response(messages))
for part in response:
response_content += part
get_streaming_response(messages)
: Оновлена функція потоком отримує відповідь асистента частинами (шматками), замість того щоб повертати всю відповідь одразу. Вона використовує генератор для динамічного видавання кожної частини відповіді.st.write_stream(response)
: Функціяst.write_stream()
обробляє потоки частин у реальному часі і поступово рендерить їх у блоці чату асистента.response_content += part
: Коли частини відповіді потоком надходять, вони додаються доresponse_content
, щоб сформувати повну відповідь.
📌 Завершення
Ось повний код, який об’єднує всі секції:
from pymongo import MongoClient
import uuid
from datetime import datetime
import streamlit as st
from ollama import chat
# Підключення до MongoDB
client = MongoClient("mongodb://localhost:27017/")
db = client.chat_db
def add_message(chat_id, role, content):
"""Додати повідомлення до колекції, специфічної для чату, в базі даних."""
collection = db[chat_id]
collection.insert_one({"role": role, "content": content})
def get_all_messages(chat_id):
"""Отримати всі повідомлення для специфічного chat_id з колекції чату."""
collection = db[chat_id]
return list(collection.find({}, {"_id": 0, "role": 1, "content": 1}))
def create_new_chat():
"""Створити нову чат-сесію, повернути її UUID та створити для неї нову колекцію."""
chat_id = str(uuid.uuid4())
db.create_collection(chat_id)
db.chat_metadata.insert_one({"chat_id": chat_id, "created_at": datetime.utcnow()})
return chat_id
def get_all_chat_ids():
"""Отримати всі унікальні chat_id (імена колекцій) з бази даних, відсортовані за часом створення."""
chat_ids = db.chat_metadata.find({}, {"_id": 0, "chat_id": 1, "created_at": 1}).sort("created_at", -1)
return [f"{doc['chat_id'][:4]} ({doc['created_at'].strftime('%Y-%m-%d %H:%M:%S')})" for doc in chat_ids]
def get_response(messages, model='llama3.2:3b'):
try:
response = chat(model, messages=messages, stream=False)
return response['message']['content']
except Exception as e:
st.error(f"Error: {e}")
response = "Sorry, I couldn't process your request."
return response
def get_streaming_response(messages, model='llama3.2:3b'):
try:
for part in chat(model, messages=messages, stream=True):
yield part['message']['content']
except Exception as e:
# Handle the exception
print(f"An error occurred: {e}")
yield "An error occurred while processing the stream."
st.title("🦙 Llama Chat")
# Бічна панель для відображення попередніх чатів
st.sidebar.title("Previous Chats")
chat_ids = get_all_chat_ids()
selected_chat_id = st.sidebar.selectbox("Select a chat", chat_ids)
if selected_chat_id:
st.session_state.chat_id = selected_chat_id.split()[0] # Extract the UUID part
# Кнопка для створення нового чату
if st.sidebar.button("New Chat"):
st.session_state.chat_id = create_new_chat()
st.rerun() # Reload the page to reflect the new chat
# Перевірка, чи встановлено chat_id в стані сесії
if "chat_id" not in st.session_state:
st.session_state.chat_id = create_new_chat()
st.rerun() # Reload the page to reflect the new chat
chat_id = st.session_state.chat_id
# Отримання всіх повідомлень для поточного chat_id з бази даних
messages = get_all_messages(chat_id)
# Відображення всіх повідомлень з бази даних
for message in messages:
with st.chat_message(message["role"]):
st.markdown(message["content"])
# Створення текстового вводу для користувача
prompt = st.chat_input("Write your message here")
if prompt:
with st.chat_message("user"):
st.markdown(prompt)
add_message(chat_id, "user", prompt)
# Включення нового повідомлення користувача в список повідомлень
messages.append({'role': 'user', 'content': prompt})
response_content = ""
with st.chat_message("assistant"):
response = st.write_stream(get_streaming_response(messages))
for part in response:
response_content += part
add_message(chat_id, "assistant", response_content)
messages.append({'role': 'assistant', 'content': response_content})
🏁 Завершення
З цими функціями у вас тепер є повноцінний чат-додаток, де користувачі можуть взаємодіяти з асистентом, зберігати розмови та переглядати попередні чати!
👨💻 Повний вихідний код для цієї статті доступний на GitHub: ollama-streamlit-mongodb-chat.
Не соромтеся досліджувати, клонувати або вносити свій внесок у репозиторій, щоб покращити додаток!
Перекладено з: Building a ChatGPT-Like App with MongoDB, Streamlit, and Local LLMs: A Step-by-Step Guide