Я использую 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()
Чтобы упростить и уточнить, если пользователь нажимает кнопку во время генерации ответа или подготовки к генерации, процесс необходимо немедленно отменить. Затем система должна переключиться на другую модель, не принимая во внимание статус прерванного процесса или используемые модели. Проблема возникает, когда модель готовится к печати, а не во время самого процесса печати. В таких случаях система должна оперативно перейти на альтернативную модель. Это касается каждой используемой модели. Нет порядка.
Нам нужно обновить функции 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.
Ключевые изменения:
Обновить код:
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.
Это автоматически исправит эту проблему. Код работает именно так, как есть. Просто нужно обновить Олламу.
Не уверен, как фрагмент кода связан с описанным вопросом. У вас есть приложение с одной кнопкой
Stop
. При запуске модель ламы начинает генерировать ответ. Но затем, если нажатьStop
, модельphi
перезапустит все поколение. А еще внутри модели ламы естьmistral
модель. Что именно следует остановить? Какая из моделей? И почему вы просто создаете новую глобальную константуstop_called=False/True
и проверяете ее в начале каждой сопрограммы?