Как немедленно отменить задачу Asyncio, которая использует библиотеку Ollama Python для генерации ответа?

Я использую Ollama для генерации ответов на основе больших языковых моделей (LLM) с помощью API Ollama Python. Я хочу отменить генерацию ответа, нажав кнопку «Стоп». Проблема в том, что отмена задачи работает только в том случае, если генерация ответа уже начала печать. Если задача все еще обрабатывается и готовится к печати, отмена не работает, и ответ все равно печатается. Точнее, эта функция prompt_mistral("Testing") по-прежнему выполняется и печатает ответ даже после нажатия кнопки.

Мой код:

import ollama
import asyncio
import threading
from typing import Optional
import tkinter as tk

# Create the main window
root = tk.Tk()
root.title("Tkinter Button Example")

worker_loop: Optional[asyncio.AbstractEventLoop] = None
task_future: Optional[asyncio.Future] = None

async def get_answer_from_phi3():

    print("Trying")

    messages = [
            {"role": "system", "content": "Hello"}
        ]

    client = ollama.AsyncClient()
    
    stream = await client.chat(
    model='phi3',
    messages=messages,
    stream=True,
    options= {
    "top_k": 1})

    try:
        async for chunk in stream:
            # Store generated answer
           print(chunk['message']['content'], end='', flush=True)
            

    except asyncio.exceptions.CancelledError as e:
        print("Cancelled")
        pass

    except Exception as e:
        print(e)
        return "Sorry,vv an error occurred while processing your request."


async def prompt_mistral(query):
    messages = []
    messages.append({"role": "assistant", "content": "Write a song that celebrates the beauty, diversity, and importance of our planet, Earth. The song should evoke vivid imagery of the natural world, from lush forests and majestic mountains to serene oceans and vast deserts. It should capture the essence of Earth as a living, breathing entity that sustains all forms of life. Incorporate themes of harmony, unity, and interconnectedness, emphasizing how all elements of nature are intertwined and how humanity is an integral part of this complex web. The lyrics should reflect a sense of wonder and appreciation for the planet's resources and ecosystems, highlighting the delicate balance that sustains life. Include references to various landscapes, climates, and wildlife, painting a picture of Earth's diverse environments. The song should also touch on the responsibility we have to protect and preserve the planet for future generations, addressing issues like climate change, deforestation, pollution, and conservation efforts. Use poetic language and metaphors to convey the grandeur and fragility of Earth, and infuse the song with a hopeful and inspiring tone that encourages listeners to take action in safeguarding our shared home. The melody should be uplifting and emotionally resonant, complementing the powerful message of the lyrics"})
    generated_answer = ''
    try:
        client = ollama.AsyncClient()
        stream = await client.chat(
            model='mistral',
            messages=messages,
            stream=True,
            options= {
                "top_k": 1}
        )

        async for chunk in stream:
            # Store generated answer
            generated_answer += chunk['message']['content']
            print(chunk['message']['content'])
            
    
    except asyncio.exceptions.CancelledError as e:
        print("Cancelled reponse")
        return

    except Exception as e:
        print(e)

        return "Sorry,vv an error occurred while processing your request."

def prompt_llama(message):

    async def prompt():

        messages = []
        messages.append({"role": "assistant", "content": message})
        try:
            client = ollama.AsyncClient()
            stream = await client.chat(
                model='llama2',
                messages=messages,
                stream=True,
                options= {
                    "top_k": 1}
            )

            generated_answer = ''
            
            async for chunk in stream:
                # Store generated answer
                generated_answer += chunk['message']['content']
                print(chunk['message']['content'])    
        
            if "help" in generated_answer:
                await prompt_mistral("Testing")
            else:
                print(generated_answer)

        except asyncio.exceptions.CancelledError as e:
            print("Cancelled")
            return

        except Exception as e:
            print(e)

            return "Sorry,vv an error occurred while processing your request."

    def mistral_worker_function():
        global worker_loop, task_future
        worker_loop = asyncio.new_event_loop()
        task_future = worker_loop.create_task(prompt())
        worker_loop.run_until_complete(task_future) 

    print("Starting thread")
    thread = threading.Thread(target=mistral_worker_function)
    thread.start()
    client = ollama.AsyncClient()

# Define the function to be called when the button is pressed
def on_button_click():
    global worker_loop, task_future
    # the loop and the future are not threadsafe
    worker_loop.call_soon_threadsafe(
        lambda: task_future.cancel()
    )
    

    def phi3_worker_function():
        global worker_loop, task_future
        worker_loop = asyncio.new_event_loop()
        task_future = worker_loop.create_task(get_answer_from_phi3())
        worker_loop.run_until_complete(task_future)

    print("Starting thread")
    thread = threading.Thread(target=phi3_worker_function())
    thread.start()

# Create the button
button = tk.Button(root, text = "Stop", command=on_button_click)

# Place the button on the window
button.pack(pady=20)

prompt_llama("Hi")

# Start the Tkinter event loop
root.mainloop()

Не уверен, как фрагмент кода связан с описанным вопросом. У вас есть приложение с одной кнопкой Stop. При запуске модель ламы начинает генерировать ответ. Но затем, если нажать Stop, модель phi перезапустит все поколение. А еще внутри модели ламы есть mistral модель. Что именно следует остановить? Какая из моделей? И почему вы просто создаете новую глобальную константу stop_called=False/True и проверяете ее в начале каждой сопрограммы?

Johnny Cheesecutter 21.07.2024 23:24

Чтобы упростить и уточнить, если пользователь нажимает кнопку во время генерации ответа или подготовки к генерации, процесс необходимо немедленно отменить. Затем система должна переключиться на другую модель, не принимая во внимание статус прерванного процесса или используемые модели. Проблема возникает, когда модель готовится к печати, а не во время самого процесса печати. В таких случаях система должна оперативно перейти на альтернативную модель. Это касается каждой используемой модели. Нет порядка.

noocoder777 22.07.2024 09:05
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
2
109
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Нам нужно обновить функции on_button_click() и prompt_llama().

def on_button_click():
    global worker_loop, task_future
    if worker_loop and task_future:
        worker_loop.call_soon_threadsafe(task_future.cancel)

    def phi3_worker_function():
        global worker_loop, task_future
        worker_loop = asyncio.new_event_loop()
        asyncio.set_event_loop(worker_loop)
        task_future = worker_loop.create_task(get_answer_from_phi3())
        worker_loop.run_until_complete(task_future)

    print("Starting thread")
    thread = threading.Thread(target=phi3_worker_function)
    thread.start()

def prompt_llama(message):
    async def prompt():
        messages = [{"role": "assistant", "content": message}]
        try:
            client = ollama.AsyncClient()
            stream = await client.chat(
                model='llama2',
                messages=messages,
                stream=True,
                options = {"top_k": 1}
            )

            generated_answer = ''
            async for chunk in stream:
                generated_answer += chunk['message']['content']
                print(chunk['message']['content'])

            if "help" in generated_answer:
                await prompt_mistral("Testing")
            else:
                print(generated_answer)

        except asyncio.exceptions.CancelledError:
            print("Cancelled")
        except Exception as e:
            print(f"Error: {e}")

    def mistral_worker_function():
        global worker_loop, task_future
        worker_loop = asyncio.new_event_loop()
        asyncio.set_event_loop(worker_loop)
        task_future = worker_loop.create_task(prompt())
        worker_loop.run_until_complete(task_future)

    print("Starting thread")
    thread = threading.Thread(target=mistral_worker_function)
    thread.start()

Чтобы немедленно отменить задачу asyncio, которая использует библиотеку Ollama Python для генерации ответа, вам необходимо убедиться, что запрос на отмену может быстро прервать выполнение сопрограммы. Это предполагает более частую проверку отмены в сопрограмме и соответствующую обработку CancelledError.

Ключевые изменения:

  1. Проверка отмены: добавлен if Task_future.cancelled(): прерывание внутри асинхронных циклов for для частой проверки отмены.
  2. Обработка исключений: asyncio.Exceptions.CancelledError изменен на asyncio.CancelledError, чтобы правильно перехватывать исключение отмены.
  3. Отмена Threadsafe: гарантировалось, что отмена безопасно вызывается в цикле событий с помощью worker_loop.call_soon_threadsafe(task_future.cancel).

Обновить код:

import ollama
import asyncio
import threading
from typing import Optional
import tkinter as tk

root = tk.Tk()
root.title("Tkinter Button Example")

worker_loop: Optional[asyncio.AbstractEventLoop] = None
task_future: Optional[asyncio.Future] = None

async def get_answer_from_phi3():
    print("Trying")

    messages = [
        {"role": "system", "content": "Hello"}
    ]

    client = ollama.AsyncClient()

    try:
        stream = await client.chat(
            model='phi3',
            messages=messages,
            stream=True,
            options = {"top_k": 1}
        )

        async for chunk in stream:
            if task_future.cancelled():
                break
            print(chunk['message']['content'], end='', flush=True)

    except asyncio.CancelledError:
        print("Cancelled")
    except Exception as e:
        print(e)
        return "Sorry, an error occurred while processing your request."

async def prompt_mistral(query):
    messages = []
    messages.append({"role": "assistant", "content": "Write a song that celebrates the beauty, diversity, and importance of our planet, Earth. The song should evoke vivid imagery of the natural world, from lush forests and majestic mountains to serene oceans and vast deserts. It should capture the essence of Earth as a living, breathing entity that sustains all forms of life. Incorporate themes of harmony, unity, and interconnectedness, emphasizing how all elements of nature are intertwined and how humanity is an integral part of this complex web. The lyrics should reflect a sense of wonder and appreciation for the planet's resources and ecosystems, highlighting the delicate balance that sustains life. Include references to various landscapes, climates, and wildlife, painting a picture of Earth's diverse environments. The song should also touch on the responsibility we have to protect and preserve the planet for future generations, addressing issues like climate change, deforestation, pollution, and conservation efforts. Use poetic language and metaphors to convey the grandeur and fragility of Earth, and infuse the song with a hopeful and inspiring tone that encourages listeners to take action in safeguarding our shared home. The melody should be uplifting and emotionally resonant, complementing the powerful message of the lyrics"})
    
    try:
        client = ollama.AsyncClient()
        stream = await client.chat(
            model='mistral',
            messages=messages,
            stream=True,
            options = {"top_k": 1}
        )

        async for chunk in stream:
            if task_future.cancelled():
                break
            print(chunk['message']['content'])

    except asyncio.CancelledError:
        print("Cancelled response")
        return
    except Exception as e:
        print(e)
        return "Sorry, an error occurred while processing your request."

def prompt_llama(message):
    async def prompt():
        messages = []
        messages.append({"role": "assistant", "content": message})
        try:
            client = ollama.AsyncClient()
            stream = await client.chat(
                model='llama2',
                messages=messages,
                stream=True,
                options = {"top_k": 1}
            )

            generated_answer = ''

            async for chunk in stream:
                if task_future.cancelled():
                    break
                generated_answer += chunk['message']['content']
                print(chunk['message']['content'])

            if "help" in generated_answer:
                await prompt_mistral("Testing")
            else:
                print(generated_answer)

        except asyncio.CancelledError:
            print("Cancelled")
            return
        except Exception as e:
            print(e)
            return "Sorry, an error occurred while processing your request."

    def mistral_worker_function():
        global worker_loop, task_future
        worker_loop = asyncio.new_event_loop()
        task_future = worker_loop.create_task(prompt())
        worker_loop.run_until_complete(task_future)

    print("Starting thread")
    thread = threading.Thread(target=mistral_worker_function)
    thread.start()
    client = ollama.AsyncClient()

def on_button_click():
    global worker_loop, task_future
    if task_future and not task_future.done():
        worker_loop.call_soon_threadsafe(task_future.cancel)
    
    def phi3_worker_function():
        global worker_loop, task_future
        worker_loop = asyncio.new_event_loop()
        task_future = worker_loop.create_task(get_answer_from_phi3())
        worker_loop.run_until_complete(task_future)

    print("Starting thread")
    thread = threading.Thread(target=phi3_worker_function)
    thread.start()

button = tk.Button(root, text = "Stop", command=on_button_click)
button.pack(pady=20)

prompt_llama("Hi")
root.mainloop()
Ответ принят как подходящий

Обновите Ollama до последней версии с помощью curl https://ollama.ai/install.sh | sh в Linux.

Это автоматически исправит эту проблему. Код работает именно так, как есть. Просто нужно обновить Олламу.

Другие вопросы по теме